框架实现

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);
        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 语句和其映射信息
  • Executor:负责执行 SQL 并返回结果
  • Dynamic Proxy:动态生成 Mapper 接口实现,简化用户调用

异常处理:

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

Executor 相关

Executor 接口

public interface Executor {

    <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception;

    void close() throws Exception;
}

SimpleExecutor 实现类

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 = configuration.getDataSource().getConnection();

获取并解析 SQL:

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

创建 PreparedStatement 并绑定参数:

  • 根据参数类型信息,通过反射获取参数值
  • 使用 JDBC 的 PreparedStatement 完成 SQL 的参数设置

执行查询并处理结果集:

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

适用场景

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

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