我们通过注解和反射 制作了一个通用的ORM工具。但是这个工具有一个问题每一次都要通过反射获取属性信息,再设置对应的属性。这样子和硬编码的性能上差了很多。
有什么办法既有硬编码的效率又有使用反射的灵活性呢???
在前面文章中我们使用Javassist 动态生成Class文件,我们可以动态生成Class文件这样子执行性能得到了保证,而且又有使用反射的灵活性。
使用Javassist动态生成EntityHelper
首先创建一个抽象类 AbstractEntityHelper 作为各个具体类的实现类
import java.sql.ResultSet;
public abstract class AbstractEntityHelper {
public abstract Object create(ResultSet rs);
}
创建EntityHelperFactory,创建工厂
package com.yad.orm.v3;
import javassist.*;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
public class EntityHelperFactory {
private EntityHelperFactory(){}
//用来保存 具体类的实现类
private static final Map<Class<?>,AbstractEntityHelper> entityHelperMap = new HashMap<>();
public static AbstractEntityHelper getEntityHelper(Class<?> clazz) throws Exception {
if (clazz==null) return null;
AbstractEntityHelper helper = entityHelperMap.get(clazz);
if (helper!=null) return helper;
//获取系统路径以及JVM的同路径下的class
ClassPool pool = ClassPool.getDefault();
//导包
//import java.sql.ResultSet;
pool.importPackage(ResultSet.class.getName());
pool.importPackage(clazz.getName());
//找到抽象类的助手类
CtClass abstractEntityHelper = pool.getCtClass(AbstractEntityHelper.class.getName());
//抽象实现类名称
final String entityImplHelperName = clazz.getName()+"Helper";
//创建类
//public UserEntityHelper extends AbstractEntityHelper{}
CtClass helperClazz = pool.makeClass(entityImplHelperName, abstractEntityHelper);
//创建构造函数
CtConstructor constructor = new CtConstructor(new CtClass[0],helperClazz);
constructor.setBody("{}");
//给类添加构造器
helperClazz.addConstructor(constructor);
StringBuffer sb= new StringBuffer();
// 添加一个函数, 也就是实现抽象类中的 create 函数
sb.append("public Object create(java.sql.ResultSet rs) throws Exception {\n");
// 生成以下代码:
// UserEntity obj = new UserEntity();
sb.append(clazz.getName())
.append(" obj = new ")
.append(clazz.getName())
.append("();\n");
// 通过反射方式获取类的字段数组,
// 并生成代码
// 获取类的字段数组并生成代码
Field[] fArr = clazz.getFields();
for (Field f : fArr) {
// 获取字段上注解
Column annoColumn = f.getAnnotation(Column.class);
if (annoColumn == null) {
// 如果注解为空,
// 则直接跳过...
continue;
}
// 获取列名称
String colName = annoColumn.name();
if (f.getType() == Integer.TYPE) {
// 生成如下代码:
// obj._userId = rs.getInt("user_id");
sb.append("obj.")
.append(f.getName())
.append(" = rs.getInt(\"")
.append(colName)
.append("\");\n");
} else if (f.getType().equals(String.class)) {
// 生成如下代码:
// obj._userName = rs.getString("user_name");
sb.append("obj.")
.append(f.getName())
.append(" = rs.getString(\"")
.append(colName)
.append("\");\n");
} else {
// 不支持的类型...
// 如果需要支持 float、long、boolean 等类型,
// 接着往下写就可以了
}
}
sb.append("return obj;\n");
sb.append("}");
//给helperClazz 添加上这个方法
CtMethod method = CtMethod.make(sb.toString(),helperClazz);
helperClazz.addMethod(method);
helperClazz.writeFile("C:/");
//生产Class文件
Class<?> javaClazz = helperClazz.toClass();
helper = (AbstractEntityHelper) javaClazz.newInstance();//实例化
entityHelperMap.put(clazz,helper);//添加到map中
return helper;
}
}
再回头来修改查询代码
package com.yad.orm.v3;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class App003 {
static public void main(String[] argvArray) throws Exception {
(new App003()).start();
}
private void start() throws Exception {
// 加载 Mysql 驱动
Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
// 数据库连接地址
String dbConnStr = "jdbc:mysql://localhost:3306/ormtest?user=root&password=Yad@121413";
// 创建数据库连接
Connection conn = DriverManager.getConnection(dbConnStr);
// 简历陈述对象
Statement stmt = conn.createStatement();
// 创建 SQL 查询
String sql = "select * from dog limit 20000";
// 执行查询
ResultSet rs = stmt.executeQuery(sql);
//去EntityHelperFactory工厂获取 对应具体类型的 EntityHelper
AbstractEntityHelper helper = EntityHelperFactory.getEntityHelper(DogEntity.class);
while (rs.next()) {
// 通过 对应具体类型的 EntityHelper 创建新的实体对象
DogEntity entity = (DogEntity) helper.create(rs);
}
// 关闭数据库连接
stmt.close();
conn.close();
}
}
这样子我们还是可以方便通过EntityHelper自动实现查询结果赋值和对象实例化。
再来看看通过Javassist 生成的Class 文件
package com.yad.orm.v3;
import java.sql.ResultSet;
public class DogEntityHelper extends AbstractEntityHelper {
public DogEntityHelper() {
}
public Object create(ResultSet var1) throws Exception {
DogEntity var2 = new DogEntity();
var2.id = var1.getInt("id");
var2.name = var1.getString("name");
var2.size = var1.getInt("size");
return var2;
}
}
可以看出其实我们通过Javasisst 动态生成具体类的对应Class所实现的具体内容和硬编码的效果是一样的,也就是说这样子我们既有了硬编码的性能,又有反射的灵活性。后续就可以以此进行拓展实现功能更为完善的ORM框架。