在目前各个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();
}
}
还有一些类没有在这里列举,此处仅仅提供一种思路而已