手写一个极简ORM工具(2)提高反射效率

335 阅读3分钟

我们通过注解和反射 制作了一个通用的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框架。