模拟Mybatis实现原理

模拟Mybatis实现原理

MyBatis框架的实现,说白了就是利用SQL语句查询到数据,然后再根据类型生成程序需要的Java对象

理论

这里通过自己的思路,模拟了一套Mybatis的实现,技术用到了javassite

源码地址

https://github.com/hiparker/orm-test-javassist

为什么不直接用反射?

总所周知,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;
    }
}

本文由 在码圈 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
您可以自由的转载和修改,但请务必注明文章来源并且不可用于商业目的。
本站部分内容收集于互联网,如果有侵权内容、不妥之处,请联系我们删除。敬请谅解!
原文链接:https://www.arcinbj.com/archives/mybatisjavassite
最后更新于:2020-08-24 21:48:19

请博主喝咖啡 ☕.