jdk1.5中annotation的应用(1)--DAO实现

在目前各个O/RM框架大行其道的时候为什么要在设计其他框架呢,最主要考虑到O/RM的性能以及团队的学习曲线.因为各个O/RM都不能最大的使用数据库所专门提供的特殊功能,
如要在系统中实现统计业务收入:包括在业务维度和地区维度汇总,使用pl/sql就非常好实现,但是使用O/RM就没有那么方便了
select DECODE(GROUPING_ID(yw), 1, '所有业务', yw) 业务,
DECODE(GROUPING_ID(address), 1, '所有地区', address) 地区
,sum(paycount) from tjincome where dayno=20070329
group by rollup(yw,address) order by yw,address
,即使象Hibernate做了方言仍然是相差很远,使用O/RM浪费了大量的数据库功能。所以我一直都是推荐在项目中还是直接使用JDBC操作.
但是象传统的JDBC写法有非常烦琐,不得不做很多重复的工作:资源的获取,资源的关闭,结果集向对象的转化,Statement参数的设置等等.因此需要找一个既可以使用sql编程又可以避免烦琐的方法。

考虑如下的实现方式:
@DAOMapping(properties = { @DAOProperty(field = "age", name = "age"),
  @DAOProperty(field = "name", name = "name") })
 public class Student {
 }
 上面配置的是对象属性和数据库字段之间的映射关系。
 
 @DAOLoadOneRecord(sql = "select name,age from student where name=?")
 @DAOStatementParameters(parameters = { @DAOParameter(parameterInMethod = "1", type = String.class) })
 @DAOMapping(properties = { @DAOProperty(field = "age", name = "age"),
               @DAOProperty(field = "name", name = "name") }) 
 public Student getStudent(String name);
 解释:获得某个学生。通过DAOLoadOneRecord传入sql语句,DAOStatementParameters传入参数,其中parameterInMethod=1指的是方法第一个参数,查询返回的结果集向对象的转化读取方法上的@DAOMapping注释,如果没有则读取方法返回类型(如Student)上的DAOMapping描述,如果仍然没有配置,则取默认的属性名称和字段名称相等进行属性赋值。
 
 @DAOFindRecord(sql = "select name,age from student", beanType = Student.class)
 public List<Student> getAllStudent();
 使用@DAOFindRecord查询一个集合操作。参数设置以及结果集与对象之间的转换规范如上。
 
 @DAOFindRecord(sql = "select name,age from student where name like ?", beanType = Student.class, startParameter = 2, fetchParameter = 3)
 @DAOStatementParameters(parameters = { @DAOParameter(parameterInMethod = "1", type = String.class) })
 public List<Student> getStudents(String name, int start, int fetchCount); 
 支持分页的配置
 
 @DAOSql(sql = "delete from student where name=?")
 @DAOStatementParameters(parameters = { @DAOParameter(parameterInMethod = "1", type = String.class) })
 public int delStudent(String name);
 使用@DAOSql传递insert,update,del语句,如果方法有返回值(只能是int),则返回本次操作影响的条数
 
 @DAOSql(sql = "insert student(name,age) values(?,?)")
 @DAOStatementParameters(parameters = {
   @DAOParameter(parameterInMethod = "1.name", type = String.class),
   @DAOParameter(parameterInMethod = "1.age", type = int.class) })
 public void addStudent(Student student);
 这个地方有点特殊的地方是parameterInMethod参数"1.name"表示的是方法第一个参数对象的name属性值,当然也可以继续嵌套如1.address.city  
 
 还有一些业务非常复杂的不能简单的使用一条sql表达的可以采取如下策略:
 @DAOInvokeImpl(_class = StudentInvokeImpl.class)
 public Map<String, Student> getAllStudentForMap(int start, int fetchCount); 
 指定一个@DAOInvokeImpl描述该方法的实现方法,其中@DAOInvokeImpl如下。
         @Target(value = ElementType.METHOD)
         @Retention(value = RetentionPolicy.RUNTIME)
         public @interface DAOInvokeImpl {
          Class _class();
         
          String method() default "";
         
          String factoryMethod() default "";
         }
 如果没有配置factoryMethod则new一个对象,如果没有配置method则方法名字和本方法的名字相同,方法定义类似于
 public Map<String,Student> getAllStudentForMap(Connection con,int start,int fetchCount){}
 第一个参数必须是Connection con已传入数据库连接,只所以要在第一个传,是为了支持jdk1.5的方法参数数量可变这个类型(不知道怎么说),如concat(String... msg)
 
 当要添加一个实体的时候,只需要为该实体添加一个interface,在interface中定义所需要的方法,并且使用上面这些规则注释,将interface配置进配置文件。
 如
 <services>
     <daos>
         <dao className="demo.dao.StudentTestDao"/>
     </daos>
 </services> 
 当系统启动的时候读取该配置文件,使用cglib生成这个interface的代理对象存放在ServiceContext中,当需要某个DAO的时候,传如interface的class对象StudentTestDao.class
 从ServiceContext中得到cglib对这个interface的代理对象,当调用interface的方法时,代理对象读取方法上的annotation描述执行相应操作。
 
 最主要的工具类代码如下:
 public class DAOUtil {
 public static Object run(Class interfaceClass, Method method,
   Object[] args, Connection con) {
  PreparedStatement pstmt = null;
  ResultSet rs = null;
  try {
   // 首先是是否自定义调用
   DAOInvokeImpl invokeImplAnnotation = method
     .getAnnotation(DAOInvokeImpl.class);
   DAOFindRecord findRecordAnnotation = method
     .getAnnotation(DAOFindRecord.class);
   DAOLoadOneRecord loadOneRecordAnnotation = method
     .getAnnotation(DAOLoadOneRecord.class);
   DAOSql sqlAnnotation = method.getAnnotation(DAOSql.class);
   int configNum = 0;
   if (invokeImplAnnotation != null) {
    configNum++;
   }
   if (findRecordAnnotation != null) {
    configNum++;
   }
   if (loadOneRecordAnnotation != null) {
    configNum++;
   }
   if (sqlAnnotation != null) {
    configNum++;
   }
   if (configNum != 1) {
    throw new DAOAnnotationConfigException(
      "DAOInvokeImpl,DAOFindRecord,DAOLoadOneRecord,DAODelRecord只能其中配置一个");
   }
   if (invokeImplAnnotation != null) {
    // 得到类的实现实例
    Object invokeImpl = invokeImplAnnotation._class().newInstance();
    // 得到调用方法
    Class<?>[] parameterTypes = method.getParameterTypes();
    Class<?>[] invokeImplParameters = new Class<?>[parameterTypes.length + 1];
    invokeImplParameters[0] = Connection.class;
    for (int i = 1; i < invokeImplParameters.length; i++) {
     invokeImplParameters[i] = parameterTypes[i - 1];
    }
    method = invokeImplAnnotation._class().getMethod(
      method.getName(), invokeImplParameters);
    Object[] parameterValues = new Object[args.length + 1];
    parameterValues[0] = con;
    for (int i = 1; i < parameterValues.length; i++) {
     parameterValues[i] = args[i - 1];
    }
    return method.invoke(invokeImpl, parameterValues);
   }

   // 读取一条记录
   Class<?> returnType = method.getReturnType();
   List statementParameters = new ArrayList();
   if (loadOneRecordAnnotation != null) {
    // 根据sql语句生成PreparedStatement
    AssertUtil.assertNotEmpty(loadOneRecordAnnotation.sql());

    pstmt = con.prepareStatement(loadOneRecordAnnotation.sql());
    // 设置参数
    setPrepareStatementParameter(method, args, pstmt,
      statementParameters);
    if (SystemConfig.getBoolean(SystemConfigConstants.ShowSQL)) {
     System.out.println(loadOneRecordAnnotation.sql() + ",参数:"
       + statementParameters);
    }
    // 查询结果得到结果集
    rs = pstmt.executeQuery();
    if (rs.next()) {
     return exportDateFromResultSet(method, rs, returnType);
    } else {
     return null;
    }
   }

   // 查询多条记录
   if (findRecordAnnotation != null) {
    // 根据sql语句生成PreparedStatement
    AssertUtil.assertNotEmpty(findRecordAnnotation.sql());

    boolean notAllDate = false;
    if (findRecordAnnotation.startParameter() != -1
      && findRecordAnnotation.fetchParameter() != -1) {
     notAllDate = true;
    }
    if (notAllDate) {
     pstmt = con.prepareStatement(findRecordAnnotation.sql(),
       ResultSet.TYPE_FORWARD_ONLY,
       ResultSet.CONCUR_READ_ONLY);
    } else {
     pstmt = con.prepareStatement(findRecordAnnotation.sql());
    }
    // 设置参数
    setPrepareStatementParameter(method, args, pstmt,
      statementParameters);
    if (SystemConfig.getBoolean(SystemConfigConstants.ShowSQL)) {
     System.out.println(findRecordAnnotation.sql() + ",参数:"
       + statementParameters);
    }
    // 查询结果得到结果集
    rs = pstmt.executeQuery();
    int startParameter = findRecordAnnotation.startParameter();
    int fetchParameter = findRecordAnnotation.fetchParameter();
    int fetchCount = 0;
    int start = 0;
    if (notAllDate) {
     fetchCount = Integer.valueOf(args[fetchParameter - 1]
       .toString());
     start = Integer
       .valueOf(args[startParameter - 1].toString());
     // 游标滚动
     if (start != 0) {
      rs.absolute(start);
     }
    }
    // 实例化
    List result = new ArrayList();
    int i = 0;
    while (rs.next()) {
     if (notAllDate && i >= fetchCount) {
      break;
     }
     result.add(exportDateFromResultSet(method, rs,
       findRecordAnnotation.beanType()));
     i++;
    }
    return result;
   }

   //
   if (sqlAnnotation != null) {
    // 根据sql语句生成PreparedStatement
    AssertUtil.assertNotEmpty(sqlAnnotation.sql());

    pstmt = con.prepareStatement(sqlAnnotation.sql());
    // 设置参数
    setPrepareStatementParameter(method, args, pstmt,
      statementParameters);
    if (SystemConfig.getBoolean(SystemConfigConstants.ShowSQL)) {
     System.out.println(sqlAnnotation.sql() + ",参数:"
       + statementParameters);
    }
    // 查询结果得到结果集
    int result = pstmt.executeUpdate();
    if (int.class.equals(returnType)) {
     return result;
    } else if (void.class.equals(returnType)) {

    } else {
     throw new DAOBeanException("更新或删除语句只能返回int或void");
    }
   }
  } catch (SQLException e) {
   throw new DAOSQLException(e);
  } catch (IllegalAccessException e) {
   throw new DAOBeanException(e);
  } catch (InvocationTargetException e) {
   throw new DAOBeanException(e);
  } catch (NoSuchMethodException e) {
   throw new DAOBeanException(e);
  } catch (InstantiationException e) {
   throw new DAOBeanException(e);
  } finally {
   if (rs != null) {
    try {
     rs.close();
    } catch (SQLException e) {

    }
   }
   if (pstmt != null) {
    try {
     pstmt.close();
    } catch (SQLException e) {

    }
   }
  }
  return null;
 }

//从结果集中提取数据封装成java对象 
private static Object exportDateFromResultSet(Method method, ResultSet rs,
   Class beanType) throws InstantiationException,
   IllegalAccessException, SQLException, InvocationTargetException {
  // 得到方法的返回类型
  DAOMapping mapping = getMapping(method, beanType);
  if (mapping != null) {
   Object result = beanType.newInstance();
   DAOProperty[] properties = mapping.properties();
   for (int i = 0; i < properties.length; i++) {
    String propertyName = properties[i].name();
    Class propertyType = null;
    try {
     PropertyDescriptor[] propertyDescriptors = Introspector
       .getBeanInfo(beanType).getPropertyDescriptors();
     for (PropertyDescriptor p : propertyDescriptors) {
      if (p.getName().equals(propertyName)) {
       propertyType = p.getPropertyType();
      }
     }
    } catch (SecurityException e) {
     throw new DAOBeanException(e);
    } catch (IntrospectionException e) {
     throw new DAOBeanException(e);
    }
    if (propertyType == null) {
     throw new DAOBeanException(beanType + "没有属性:"
       + propertyName);
    }
    Object propertyValue = null;
   //根据属性的类型调用相应的jdbc方法从结果集中得到该类型的数据
    if (propertyType.equals(String.class)) {
     propertyValue = rs.getString(properties[i].field());
    } else if (propertyType.equals(Integer.class)
      || propertyType.equals(int.class)) {
     propertyValue = rs.getInt(properties[i].field());
    } else if (propertyType.equals(Long.class)
      || propertyType.equals(long.class)) {
     propertyValue = rs.getLong(properties[i].field());
    } else if (propertyType.equals(Boolean.class)
      || propertyType.equals(boolean.class)) {
     propertyValue = rs.getBoolean(properties[i].field());
    } else if (propertyType.equals(Byte.class)
      || propertyType.equals(byte.class)) {
     propertyValue = rs.getByte(properties[i].field());
    } else if (propertyType.equals(Byte[].class)
      || propertyType.equals(byte[].class)) {
     propertyValue = rs.getBytes(properties[i].field());
    } else if (propertyType.equals(Date.class)) {
     propertyValue = rs.getTimestamp(properties[i].field());
    }

    else if (propertyType.equals(Double.class)
      || propertyType.equals(double.class)) {
     propertyValue = rs.getDouble(properties[i].field());
    } else if (propertyType.equals(Float.class)
      || propertyType.equals(float.class)) {
     propertyValue = rs.getFloat(properties[i].field());
    }

    else if (propertyType.equals(Short.class)
      || propertyType.equals(short.class)) {
     propertyValue = rs.getShort(properties[i].field());
    } else if (propertyType.equals(URL.class)) {
     propertyValue = rs.getURL(properties[i].field());
    } else if (propertyType.equals(DayNo.class)) {
     int dayNo = rs.getInt(properties[i].field());
     if (dayNo != 0) {
      propertyValue = new DayNo();
     } else {
      propertyValue = null;
     }
    } else if (propertyType.equals(Timestamp.class)) {
     propertyValue = rs.getTimestamp(properties[i].field());
    }
//对java1.5的枚举的支持
else if (propertyType.isEnum()) {
     String enumString = rs.getString(properties[i].field());
     try {
      propertyValue = getEnumValue(propertyType, enumString);
     } catch (NoSuchMethodException e) {

     } catch (IllegalAccessException e) {
      throw new DAOBeanException(e);
     } catch (InvocationTargetException e) {
      throw new DAOBeanException(e);
     }
    } else {
     throw new DAOSQLException("属性类型不支持"
       + propertyType.getName());
    }

    BeanUtils.copyProperty(result, propertyName, propertyValue);
   }
   return result;
  } else {
   if (rs.getMetaData().getColumnCount() != 1) {
    throw new DAOBeanException("结果集中返回多列,但是程序却指定只取一列");
   }
   return rs.getObject(1);
  }
 }

 @SuppressWarnings("unchecked")
 private static Object getEnumValue(Class propertyType, String enumString)
   throws NoSuchMethodException, IllegalAccessException,
   InvocationTargetException {
  Object propertyValue;
  Method methodForValueOf = propertyType.getDeclaredMethod("valueOf",
    new Class[] { String.class });
  propertyValue = methodForValueOf.invoke(propertyType,
    new Object[] { enumString });
  return propertyValue;
 }

//根据配置从java bean中取出属性值设置pstmt参数 
private static void setPrepareStatementParameter(Method method,
   Object[] args, PreparedStatement pstmt, List statementParameters)
   throws IllegalAccessException, InvocationTargetException,
   NoSuchMethodException, SQLException {
  DAOStatementParameters statementparametersAnnotation = method
    .getAnnotation(DAOStatementParameters.class);
  // 需要设置参数
  if (statementparametersAnnotation != null) {
   DAOParameter[] parameterAnnotations = statementparametersAnnotation
     .parameters();
   // 设置参数
   for (int i = 0; i < parameterAnnotations.length; i++) {
    DAOParameter parameterTemp = parameterAnnotations[i];
    // 得到参数的值
    String parameterValueAnnotation = parameterTemp
      .parameterInMethod();
    // 得到参数下标
    int pointIndex = parameterValueAnnotation.indexOf(".");
    String parameterIndexString = parameterValueAnnotation;
    String propertySegment = null;
    if (pointIndex != -1) {
     parameterIndexString = parameterIndexString.substring(0,
       pointIndex);
     propertySegment = parameterValueAnnotation
       .substring(pointIndex + 1);
    }
    // 得到参数值
    Object parameter = args[Integer.valueOf(parameterIndexString) - 1];
    if (propertySegment != null) {
     parameter = PropertyUtils.getProperty(parameter,
       propertySegment);
    }
    // 根据sql参数类型设置参数值
    statementParameters.add(parameter);
    if (parameter == null) {
     pstmt.setNull(i + 1, Types.VARCHAR);
    } else {
     Class type = parameterTemp.type();
     if (type.equals(String.class)) {
      pstmt.setString(i + 1, parameter.toString());
     } else if (type.equals(Integer.class)
       || type.equals(int.class)) {
      pstmt.setInt(i + 1, Integer.valueOf(parameter
        .toString()));
     } else if (type.equals(Long.class)
       || type.equals(long.class)) {
      pstmt
        .setLong(i + 1, Long.valueOf(parameter
          .toString()));
     } else if (type.equals(Boolean.class)
       || type.equals(boolean.class)) {
      pstmt.setBoolean(i + 1, Boolean.valueOf(parameter
        .toString()));
     } else if (type.equals(Byte.class)
       || type.equals(byte.class)) {
      pstmt
        .setByte(i + 1, Byte.valueOf(parameter
          .toString()));
     } else if (type.equals(Byte[].class)
       || type.equals(byte[].class)) {
      pstmt.setBytes(i + 1, (byte[]) parameter);
     } else if (type.equals(Date.class)) {
      pstmt.setTimestamp(i + 1, new Timestamp(
        ((Date) parameter).getTime()));
     }

     else if (type.equals(Double.class)
       || type.equals(double.class)) {
      pstmt.setDouble(i + 1, Double.valueOf(parameter
        .toString()));
     } else if (type.equals(Float.class)
       || type.equals(float.class)) {
      pstmt.setFloat(i + 1, Float.valueOf(parameter
        .toString()));
     }

     else if (type.equals(Short.class)
       || type.equals(short.class)) {
      pstmt.setShort(i + 1, Short.valueOf(parameter
        .toString()));
     } else if (type.equals(URL.class)) {
      try {
       pstmt.setURL(i + 1, new URL(parameter.toString()));
      } catch (MalformedURLException e) {
       e.printStackTrace();
       throw new DAOSQLException(e);
      }
     } else if (type.equals(DayNo.class)) {
      DayNo dayNo = (DayNo) parameter;
      pstmt.setInt(i + 1, dayNo.getDateForInt());
     } else if (type.equals(Timestamp.class)) {
      pstmt.setTimestamp(i + 1, new Timestamp(
        ((Date) parameter).getTime()));
     } else if (type.isEnum()) {
      pstmt.setString(i + 1, parameter.toString());
     } else {
      throw new DAOSQLException("属性类型不支持" + type.getName());
     }
    }
   }

  }
 }

 public int getSqlType(Class _class) {
  return 0;
  // return Types.VARCHAR
 }

//如果方法上注释了DAOMapping则取方法,否则取方法返回类型的DAOMapping 
@SuppressWarnings("unchecked")
 private static DAOMapping getMapping(Method method, Class returnResult) {
  DAOMapping result = method.getAnnotation(DAOMapping.class);
  if (result == null) {
   result = (DAOMapping) returnResult.getAnnotation(DAOMapping.class);
  }
  return result;
 }
}
  
//使用cglib生成代理类,当调用接口上的方法时讲转调该类
public class DAOInterceptor implements MethodInterceptor {

 public Object intercept(Object object, Method method, Object[] args,
   MethodProxy proxy) throws Throwable {
  // 是否是数据库方法
  if (!method.getName().equals("toString")
    && !method.getName().equals("hashCode")
    && !method.getName().equals("equals")) {
   TransactionContext transaction = TransactionContext.getInstance(
     false, null);
   if (transaction == null) {
    throw new TransactionException("DAO的操作必须在数据库事务内进行");
   }
   return DAOUtil.run(method.getDeclaringClass(), method, args,
     transaction.getConnection());
  } else {
   return proxy.invokeSuper(object, args);
  }
 }
}  


使用cglib生成代理类,接口代理的生成工厂
public class DAOFactory {
 public static Object createDAO(Class _class) {
  Enhancer enhancer = new Enhancer();
  enhancer.setSuperclass(_class);
  enhancer.setCallback(new DAOInterceptor());
  return enhancer.create();
 }

还有一些类没有在这里列举,此处仅仅提供一种思路而已

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值