MyBatis框架的实现,说白了就是利用SQL语句查询到数据,然后再根据类型生成程序需要的Java对象
理论
这里通过自己的思路,模拟了一套Mybatis的实现,技术用到了javassite
源码地址
为什么不直接用反射?
总所周知,Java本身的反射是非常吃性能的。
如果将Mybatis查询回来的数据,全部反射到Java的List对象中,效率是极低的。
再说了,如果当前查询回来的数据有10w+
的话,全部都反射来反射去一直newInstance()
,Java本身也别干别的活了,光反射就能把它玩死。
尤其是在newInstance
之后,还要把数据库查询出来的字段,放在对应对象的字段上
// 反射对象
clazz
// 获得当前类下的所有字段
Field[] fields = clazz.getDeclaredFields();
// 然后再循环,比较当前字段名与数据库抽取的rs字段名一致则
field.setAccessible(true); // 设置私有可操作
field.set(对象,rs.getXXX);
这可费了老鼻子劲了!
那这10w+数据以什么方式可以高效抽取呢?
最高效的东西,往往都是最简单的东西!
举个例子:
思考一下,打开一个纯 .html 文本
<html>
<head>
</head>
<body>
hello wolrd!
</body>
</html>
再打开一个 .jsp 相同的文本
想一想,哪儿个会更快一些?
那肯定是纯 html 文本快,因为就是原生的,不需要任何翻译,不要任何处理,直接拿给浏览器展示!
硬编码
user.id = id;
user.setName(name)
同样, 如果对于一个对象,不需要反射,采用硬编码直接new一个这个类型的对象出来set属性值
,也是最快的一种方案,恰巧javassite
可以帮我们做到这一点!
核心原理就是,让Java本身先反射一层该对象Class的调用它get set 的代理类,最后直接调用该代理类就直接完成硬编码赋值!
核心处理程序
import javassist.*;
import lombok.extern.slf4j.Slf4j;
import org.ormtest.step005.annotation.Column;
import org.ormtest.step005.entity.User;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
/**
* Created Date by 2020/1/11 0011.
*
* 助手类工厂
*
* @author Parker
*/
@Slf4j
public final class EntityFactoryHelper {
/**
* entityhelper 字典
*/
static private final Map<Class<?>,AbstarctEntityHelper> _entityHelperMap = new HashMap<>();
/**
* 私有化构造函数
*/
private EntityFactoryHelper(){}
/**
*
* @param entityClazz
* @return
*/
static public AbstarctEntityHelper getEntityHelper(Class<?> entityClazz) throws Exception{
// 这里需要全新设计,
// 接下来就该请出 javassist 了!
if(null == entityClazz){
return null;
}
AbstarctEntityHelper helper = _entityHelperMap.get(entityClazz);
// 如果字典map中 存在 则直接返回对象 不需要二次创建 避免性能过度损耗
if(null != helper){
return helper;
}
// 使用 Javassist 动态生成 Java 字节码
///////////////////////////////////////////////////////////////////////
ClassPool clazzPool = ClassPool.getDefault();
// 添加class 系统路径
clazzPool.appendSystemPath();
// 导包
// import org.ormtest.step005.ResultSet
// import org.ormtest.step005.entity.User
clazzPool.importPackage(ResultSet.class.getName());
clazzPool.importPackage(entityClazz.getName());
// 抽象助手类
CtClass abstractEntityHelper = clazzPool.getCtClass(AbstarctEntityHelper.class.getName());
// 助手类名称
final String entityHelperName = entityClazz.getName()+"_Helper";
// 构建助手类
CtClass helperClazz = clazzPool.makeClass(entityHelperName, abstractEntityHelper);
// 添加构造函数
CtConstructor constructor = new CtConstructor(new CtClass[0],helperClazz);
// 空的方法体
constructor.setBody("{}");
helperClazz.addConstructor(constructor);
// 创建方法体 ---------------
final StringBuilder sb = new StringBuilder();
sb.append("public Object create( ").append("java.sql.ResultSet rs").append(")").append(" throws Exception ").append("{\n");
sb.append("if( null == rs) { return null; }");
// 创建对象
sb.append(entityClazz.getName())
.append(" obj = ")
.append("new ")
.append(entityClazz.getName())
.append("();\n");
Field[] fields = entityClazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
Column annotation = field.getAnnotation(Column.class);
// 非法判断
if(null == annotation){
continue;
}
// 数据库字段名称
String columnName = annotation.name();
PropertyDescriptor pd = new PropertyDescriptor(field.getName(),entityClazz);
// 获得写入方法
Method writeMethod = pd.getWriteMethod();
if(field.getType().getName().equals(Double.class.getName())){
sb.append("Double param").append(i).append(" = new Double(rs.getDouble(\"").append(columnName).append("\"));");
}else if(field.getType().getName().equals(Float.class.getName())){
sb.append("Float param").append(i).append(" = new Float(rs.getFloat(\"").append(columnName).append("\"));");
}else if(field.getType().getName().equals(Byte.class.getName())){
sb.append("Byte param").append(i).append(" = new Byte(rs.getByte(\"").append(columnName).append("\"));");
}else if(field.getType().getName().equals(Short.class.getName())){
sb.append("Short param").append(i).append(" = new Short(rs.getShort(\"").append(columnName).append("\"));");
}if(field.getType().getName().equals(Integer.class.getName())){
sb.append("Integer param").append(i).append(" = new Integer(rs.getInt(\"").append(columnName).append("\"));");
}else if(field.getType().getName().equals(Long.class.getName())){
sb.append("Long param").append(i).append(" = new Long(rs.getLong(\"").append(columnName).append("\"));");
}else if(field.getType().getName().equals(BigDecimal.class.getName())){
sb.append("BigDecimal param").append(i).append(" = new BigDecimal(rs.getBigDecimal(\"").append(columnName).append("\"));");
}else if(field.getType().getName().equals(String.class.getName())){
sb.append("String param").append(i).append(" = new String(rs.getString(\"").append(columnName).append("\"));");
}else{
//log.error("无法识别的类型,type={}",field.getType());
continue;
}
sb.append("obj.").append(writeMethod.getName()).append("(").append("param").append(i).append(");");
}
sb.append("return obj;\n");
sb.append("}\n");
// 解析方法
CtMethod makeMethod = CtNewMethod.make(sb.toString(), helperClazz);
// 添加方法到clazz中
helperClazz.addMethod(makeMethod);
// 生成文件 测试查看使用
// helperClazz.writeFile("E:/personal/practive/workspace/orm-test-javassist/src/main/java/org/ormtest/temp");
// 获取 JAVA 类
Class<?> javaClazz = helperClazz.toClass();
// 创建帮助对象实例
///////////////////////////////////////////////////////////////////////
helper = (AbstarctEntityHelper) javaClazz.newInstance();
// 将实例对象 添加至 map 字典中
_entityHelperMap.put(javaClazz,helper);
return helper;
}
}