Java-22 深入浅出 MyBatis – 手写ORM框架3 手写SqlSession、Executor 工作原理

点一下关注吧!!!非常感谢!!持续更新!!!

大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html

目前已经更新到了:

  • MyBatis(正在更新)
  • 框架实现

    上节已经实现了部分内容 下面我们继续

    SqlSession 相关

    SqlSessionFactoryBuilder

    public class SqlSessionFactoryBuilder {
    
        private Configuration configuration;
    
        public SqlSessionFactoryBuilder() {
            configuration = new Configuration();
        }
    
        public SqlSessionFactory build(InputStream inputStream) throws DocumentException, PropertyVetoException, ClassNotFoundException {
            XMLConfigerBuilder xmlConfigerBuilder = new XMLConfigerBuilder(configuration);
            Configuration conf = xmlConfigerBuilder.parseConfiguration(inputStream);
            return new DefaultSqlSessionFactory(conf);
        }
    
    }
    
    

    SqlSessionFactory

    public interface SqlSessionFactory {
    
        SqlSession openSession();
    
    }
    

    SqlSession

    public interface SqlSession {
    
        <E> List<E> selectList(String statementId, Object ...params) throws Exception;
    
        <T> T selectOne(String statementId, Object ...params) throws Exception;
    
        void close() throws Exception;
    }
    
    

    DefaultSqlSession

    @AllArgsConstructor
    public class DefaultSqlSession implements SqlSession {
    
        private Configuration configuration;
    
        private Executor simpleExecutor = new SimpleExecutor();
    
        public DefaultSqlSession(Configuration configuration) {
            this.configuration = configuration;
        }
    
        @Override
        public <E> List<E> selectList(String statementId, Object... params) throws Exception {
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            // System.out.println("DefaultSqlSession => selectList => + " + "statementId: " + statementId + ", mappedStatement: " + mappedStatement);
            return simpleExecutor.query(configuration, mappedStatement, params);
        }
    
        @Override
        public <T> T selectOne(String statementId, Object... params) throws Exception {
            List<Object> objects = selectList(statementId, params);
            if (objects.size() == 1) {
                return (T) objects.get(0);
            }
            throw new RuntimeException("DefaultSqlSession selectOne 返回结果不唯一: " + statementId);
        }
    
        @Override
        public void close() throws Exception {
            simpleExecutor.close();
        }
    
        @Override
        public <T> T getMapper(Class<?> mapperClass) {
            Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String methodName = method.getName();
                    if (method.getDeclaringClass() == Object.class) {
                        return method.invoke(this, args);
                    }
                    String className = method.getDeclaringClass().getName();
                    String statementId = className + "." + methodName;
                    Type genericReturnType = method.getGenericReturnType();
                    if(genericReturnType instanceof ParameterizedType){
                        List<Object> objects = selectList(statementId, args);
                        return objects;
                    }
                    return selectOne(statementId,args);
    
                }
            });
            return (T) proxyInstance;
        }
    }
    
    

    请添加图片描述

    类定义与注解

    AllArgsConstructor:
    用于生成包含所有字段的构造方法,简化代码。
    表示可以用所有字段直接构造一个 DefaultSqlSession 对象。

    DefaultSqlSession:
    实现了 SqlSession 接口,作为 MyBatis 的核心会话管理类。
    包含配置 (Configuration) 和执行器 (Executor) 的实例。

    属性

  • private Configuration configuration:保存配置信息,例如 MappedStatement 映射等,负责管理 SQL 的元数据。
  • private Executor simpleExecutor = new SimpleExecutor();默认执行器,用于执行 SQL 语句并返回结果。初始化为 SimpleExecutor 实例。
  • 工作原理总结

    查询流程:

  • 用户通过 Mapper 接口调用方法。
  • 动态代理拦截调用,根据方法签名生成 statementId。
  • 调用 selectList 或 selectOne 执行查询。
  • 返回查询结果。
  • 核心组件:

  • Configuration:管理配置信息。
  • MappedStatement:描述 SQL 语句和其映射信息。
  • Ezecutor:负责执行 SQL 并返回结果。
  • Dynamic Proxy:动态生成 Mapper 接口实现,简化用户调用。
  • 异常处理:

  • 当查询结果不唯一时,selectOne 方法会抛出异常。
  • 这种约束确保单条查询返回的结果始终是明确的。
  • 构造方法

    DefaultSqlSession(Configuration configuration):

  • 接受一个 Configuration 对象并赋值给类中的 configuration 属性。
  • 确保该对象的实例在初始化时具备必要的配置信息。
  • Executor 相关

    Executor

    public interface Executor {
    
        <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception;
    
        void close() throws Exception;
    }
    
    

    请添加图片描述

    DefaultExecutor

    public class SimpleExecutor implements Executor {
    
        private Connection connection;
    
        @Override
        public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception {
            connection = configuration.getDataSource().getConnection();
            String sql = mappedStatement.getSql();
            BoundSql boundSql = getBoundSql(sql);
            PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
            String parameterType = mappedStatement.getParameterType();
            Class<?> parameterTypeClass = getClassType(parameterType);
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
            int n = 0;
            for (ParameterMapping pm : parameterMappingList) {
                String content = pm.getName();
                Field declaredField = parameterTypeClass.getDeclaredField(content);
                declaredField.setAccessible(true);
                Object object = declaredField.get(params[0]);
                preparedStatement.setObject(n + 1, object);
            }
    
            // 执行sql
            ResultSet resultSet = preparedStatement.executeQuery();
            String resultType = mappedStatement.getResultType();
            Class<?> resultTypeClass = getClassType(resultType);
            List<Object> objects = new ArrayList<>();
    
            // 封装返回结果集
            while (resultSet.next()) {
                Object o = resultTypeClass.newInstance();
                // 元数据
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 1; i <= metaData.getColumnCount(); i ++) {
                    // 字段名
                    String columnName = metaData.getColumnName(i);
                    // 字段的值
                    Object value = resultSet.getObject(columnName);
    
                    // 反射 根据数据库和实体 完成
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(o, resultTypeClass.getDeclaredField(columnName).getType().cast(value));
                }
                objects.add(o);
            }
    
            return (List<E>) objects;
        }
    
        private BoundSql getBoundSql(String sql) {
            // 标记处理类 配置标记解析器来完成对占位符的解析处理工作
            ParameterMappingTokenHandler parameterMappingHandler = new ParameterMappingTokenHandler();
            GenericTokenParser genericTokenParser = new GenericTokenParser(
                    "#{", "}",
                    parameterMappingHandler
            );
            // 解析出来的sql
            String parseSql = genericTokenParser.parse(sql);
            // #{} 里边的参数
            List<ParameterMapping> parameterMapping = parameterMappingHandler.getParameterMappings();
            BoundSql boundSql = new BoundSql(parseSql, parameterMapping);
            System.out.println("SimpleExecutor getBoundSql: " + boundSql.getSqlText());
            return boundSql;
        }
    
        private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
            if(parameterType != null){
                return Class.forName(parameterType);
            }
            return null;
        }
    
        @Override
        public void close() throws Exception {
            connection.close();
        }
    }
    
    

    请添加图片描述

    类的作用

    SimpleExecutor 类的主要作用是:

  • 根据传入的 Configuration 和 MappedStatement 来执行 SQL 查询。
  • 通过 JDBC 操作数据库,并使用反射将查询结果映射为指定类型的 Java 对象。
  • 支持参数绑定和动态 SQL 解析(如 #{} 占位符的处理)。
  • 代码逻辑解析

  • 类成员:Connection connection: 用于管理数据库连接。
  • 核心方法:query

    实现 Executor 接口的查询功能,逻辑分为以下几步:

    建立数据库连接:

    connection = configuration.getDataSource().getConnection();
    

    获取并解析 SQL:

  • 通过 MappedStatement 获取 SQL 模板(含占位符 #{})。
  • 调用 getBoundSql 方法,将 #{} 替换为 ? 并获取参数信息。
  • 创建 PreparedStatement 并绑定参数:

  • 根据参数类型信息,通过反射获取参数值。
  • 使用 JDBC 的 PreparedStatement 完成 SQL 的参数设置。
  • 执行查询并处理结果集:

  • 通过 ResultSet 获取查询结果。
  • 使用反射动态构建结果对象,将结果集中的数据填充到指定的 Java 类型中。
  • 适用场景

    该类是 MyBatis 的核心实现之一,适合用来:

  • 实现数据库操作的封装。
  • 提供动态代理支持的 Mapper 接口。
  • 管理 SQL 查询的执行过程。
  • 作者:武子康

    物联沃分享整理
    物联沃-IOTWORD物联网 » Java-22 深入浅出 MyBatis – 手写ORM框架3 手写SqlSession、Executor 工作原理

    发表回复