手写Mybatis:第7章-SQL执行器的定义和实现

一、目标SQL执行的定义和实现

上一章节中关于池化数据源的调用、执行和结果封装都是在 DefaultSqlSession 中进行发起。这样写死不利于扩展也不利于 SqlSession 中每一个新增定义的方法对池化数据源的调用。怎么利于扩展呢

在这里插入图片描述

  • 解耦 DefaultSqlSession#selectOne 方法中关于对数据源的调用、执行和结果封装提供新的功能模块替代这部分编码的逻辑处理。
  • 只有提供单独的执行方法入口才能更好的扩展和应对这部分内容的需求变化包括各类入参、结果封装、执行器类型、批处理等。

二、设计SQL执行的定义和实现

解耦数据源的操作硬捆绑到 DefaultSqlSession 的执行方法上。

  • 单独提出一块执行器的服务功能之后将执行器的功能随着 DefaultSqlSession 创建时传入执行器功能。
  • 之后具体的方法调用就可以调用执行器来处理从而解耦这部分功能模块。

在这里插入图片描述

  • 首先提取出执行器的接口 Executor定义出执行方法、事务获取和相应提交、回滚、关闭的定义。
    • 同时由于执行器是一种标准的执行过程所以可以由抽象类进行实现对过程内容进行 模板模式 的过程包装。
    • 在包装过程中定义抽象类 BaseExecutor由具体的子类 SimpleExecutor 来实现。
  • 之后就是对 SQL 的处理JDBC 在处理 SQL 时分为简单处理和预处理。
    • 预处理包括准备语句、参数化传递、执行查询以及最后的结果封装和返回。
    • 所以这里把 JDBC 的这部分步骤分为结构化的类过程实现便于功能的扩展。
    • 结构化类语句处理器 StatementHandler

三、实现SQL执行的定义和实现

3.1 工程结构

mybatis-step-06
|-src
	|-main
	|	|-java
	|		|-com.lino.mybatis
    |			|-binding
    |			|	|-MapperMethod.java
	|			|	|-MapperProxy.java
	|			|	|-MapperProxyFactory.java
    |			|	|-MapperRegistry.java
    |			|-builder
    |			|	|-xml
    |			|	|	|-XMLConfigBuilder.java
    |			|	|-BaseBuilder.java
	|			|-datasource
	|			|	|-druid
	|			|	|	|-DruidDataSourceFacroty.java
	|			|	|-pooled
	|			|	|	|-PooledConnection.java
	|			|	|	|-PooledDataSource.java
	|			|	|	|-PooledDataSourceFacroty.java
	|			|	|	|-PoolState.java
	|			|	|-unpooled
	|			|	|	|-UnpooledDataSource.java
	|			|	|	|-UnpooledDataSourceFacroty.java
	|			|	|-DataSourceFactory.java
	|			|-executor
	|			|	|-resultset
	|			|	|	|-DefaultResultSetHandler.java
	|			|	|	|-ResultSetHandler.java
	|			|	|-statement
	|			|	|	|-BaseStatementHandler.java
	|			|	|	|-PreparedStatementHandler.java
	|			|	|	|-SimpleStatementHandler.java
	|			|	|	|-StatementHandler.java
	|			|	|-BaseExecutor.java
	|			|	|-Executor.java
	|			|	|-SimpleExecutor.java
    |			|-io
    |			|	|-Resources.java
    |			|-mapping
    |			|	|-BoundSql.java
    |			|	|-Environment.java
    |			|	|-MappedStatement.java
    |			|	|-ParameterMapping.java
    |			|	|-SqlCommandType.java
    |			|-session
    |			|	|-defaults
    |			|	|	|-DefaultSqlSession.java
    |			|	|	|-DefaultSqlSessionFactory.java
    |			|	|-Configuration.java
    |			|	|-ResultHandler.java
    |			|	|-SqlSession.java
    |			|	|-SqlSessionFactory.java
    |			|	|-SqlSessionFactoryBuilder.java
    |			|	|-TransactionIsolationLevel.java
    |			|-transaction
    |			|	|-jdbc
    |			|	|	|-JdbcTransaction.java
    |			|	|	|-JdbcTransactionFactory.java
    |			|	|-Transaction.java
    |			|	|-TransactionFactory.java
    |			|-type
    |			|	|-JdbcType.java
    |			|	|-TypeAliasRegistry.java
	|-test
		|-java
		|	|-com.lino.mybatis.test
		|	|-dao
		|	|	|-IUserDao.java
		|	|-po
		|	|	|-User.java
		|	|-ApiTest.java
        |-resources
        	|-mapper
        	|	|-User_Mapper.xml
        	|-mybatis-config-datasource.xml

3.2 SQL执行实现的关系图

在这里插入图片描述

  • Executor 接口定义为执行器入口确定出事务和操作和 SQL 执行的统一标准接口。
    • 并以执行器接口定义实现抽象类 BaseExecutor也就是用抽象类处理统一共用的事务和执行 SQL 的标准流程。
    • 也就是这里定义的执行 SQL 的抽象接口由子类 SimpleExecutor 实现。
  • 在具体的简单 SQL 执行器实现类中处理 doQuery 方法的具体操作过程。
    • 这个过程中则会引入进来 SQL 语句处理器的创建创建过程仍由 Configuration 配置项提供。
  • 当执行器开发完成之后接下来就交给 DefaultSqlSessionFactory 开启 openSession 的时候随着构造函数参数传递给 DefaultSqlSession 中这样在执行 DefaultSqlSession#selectOne 的时候就可以调用执行器进行处理。这也就完成 解耦 操作。

3.3 执行器的定义和实现

  • 执行器分为接口、抽象类、简单执行器实现类三部分。这就是 模板模式
  • 模板模式通常在框架的源码中对于一些标准流程的处理都会有抽象类的存在。它负责提供共性功能逻辑以及对接口方法的执行过程进行定义和处理并提取抽象接口交由子类实现。

3.3.1 Executor 接口

Executor.java

package com.lino.mybatis.executor;

import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.transaction.Transaction;
import java.sql.SQLException;
import java.util.List;

/**
 * @description: 执行器
 */
public interface Executor {

    /**
     * 结果处理器
     */
    ResultHandler NO_RESULT_HANDLER = null;

    /**
     * 查询
     *
     * @param ms            映射器语句
     * @param parameter     参数
     * @param resultHandler 结果处理器
     * @param boundSql      SQL对象
     * @param <E>           返回的类型
     * @return List<E>
     */
    <E> List<E> query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);

    /**
     * 获取事务
     *
     * @return 事务对象
     */
    Transaction getTransaction();

    /**
     * 提交
     *
     * @param required 是否请求执行
     * @throws SQLException SQL异常
     */
    void commit(boolean required) throws SQLException;

    /**
     * 回滚
     *
     * @param required 是否请求执行
     * @throws SQLException SQL异常
     */
    void rollback(boolean required) throws SQLException;

    /**
     * 关闭
     *
     * @param forceRollback 是否强制回滚
     */
    void close(boolean forceRollback);
}
  • 在执行器中的定义的接口包括事务相关的处理方法、执行 SQL 查询的操作。

3.3.2 BaseExecutor 抽象基类

BaseExecutor.java

package com.lino.mybatis.executor;

import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.transaction.Transaction;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.util.List;

/**
 * @description: 执行器抽象基类
 */
public abstract class BaseExecutor implements Executor {

    private org.slf4j.Logger logger = LoggerFactory.getLogger(BaseExecutor.class);

    protected Configuration configuration;
    protected Transaction transaction;
    protected Executor wrapper;

    private boolean closed;

    public BaseExecutor(Configuration configuration, Transaction transaction) {
        this.configuration = configuration;
        this.transaction = transaction;
        this.wrapper = this;
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
        if (closed) {
            throw new RuntimeException("Executor was closed.");
        }
        return doQuery(ms, parameter, resultHandler, boundSql);
    }

    protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);

    @Override
    public Transaction getTransaction() {
        if (closed) {
            throw new RuntimeException("Executor was closed.");
        }
        return transaction;
    }

    @Override
    public void commit(boolean required) throws SQLException {
        if (closed) {
            throw new RuntimeException("Cannot commit, transaction is already closed.");
        }
        if (required) {
            transaction.commit();
        }
    }

    @Override
    public void rollback(boolean required) throws SQLException {
        if (!closed) {
            if (required) {
                transaction.rollback();
            }
        }
    }

    @Override
    public void close(boolean forceRollback) {
        try {
            try {
                rollback(forceRollback);
            } finally {
                transaction.close();
            }
        } catch (SQLException e) {
            logger.warn("Unexpected exception on closing transaction. Cause: " + e);
        } finally {
            transaction = null;
            closed = true;
        }
    }
}
  • 在抽象基类中封装了执行器的全部接口这样具体的子类继承抽象类后就不用处理这些共性的方法。
  • 与此同时在 query 查询方法中封装了一些必要的流程处理如检测关闭等。

3.3.3 SimpleExecutor 简单执行器实现

SimpleExecutor.java

package com.lino.mybatis.executor;

import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.transaction.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @description: 简单执行器
 */
public class SimpleExecutor extends BaseExecutor {

    public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
    }

    @Override
    protected <E> List<E> doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, resultHandler, boundSql);
            Connection connection = transaction.getConnection();
            Statement stmt = handler.prepare(connection);
            handler.parameterize(stmt);
            return handler.query(stmt, resultHandler);
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
}
  • 简单执行器 SimpleExecutor 继承抽象基类实现抽象方法 doQuery
    • 这个方法包装数据源的获取、语句处理器的创建以及对 Statement 的实例化和相关参数设置。最后执行 SQL 的处理和结果的返回操作。

3.4 语句处理器

  • 语句处理器是 SQL 执行器中依赖的部分SQL 执行器封装事务、连接和检测环境等。
  • 而语句处理器则是准备语句、参数化传递、执行 SQL、封装结果的处理。

3.4.1 StatementHandler 语句处理器接口

StatementHandler.java

package com.lino.mybatis.executor.statement;

import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @description: 语句处理器
 */
public interface StatementHandler {

    /**
     * 准备语句
     *
     * @param connection 链接
     * @return Statement语句
     */
    Statement prepare(Connection connection);

    /**
     * 参数化
     *
     * @param statement 语句
     */
    void parameterize(Statement statement) throws SQLException;

    /**
     * 执行查询
     *
     * @param statement     语句
     * @param resultHandler 结果处理器
     * @param <E>           泛型类型
     * @return 泛型集合
     */
    <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
}
  • 语句处理器的核心包括准备语句、参数化传递参数、执行查询的操作。
  • Mybatis 源码中还包括 update、批处理、获取参数处理器等。

3.4.2 BaseStatementHandler 抽象基类

BaseStatementHandler.java

package com.lino.mybatis.executor.statement;

import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @description: 语句处理器抽象基类
 */
public abstract class BaseStatementHandler implements StatementHandler {

    protected final Configuration configuration;
    protected final Executor executor;
    protected final MappedStatement mappedStatement;

    protected final Object parameterObject;
    protected final ResultSetHandler resultSetHandler;

    protected BoundSql boundSql;

    public BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.parameterObject = parameterObject;
        this.boundSql = boundSql;
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, boundSql);
    }

    @Override
    public Statement prepare(Connection connection) {
        Statement statement = null;
        try {
            // 实例化 Statement
            statement = instantiateStatement(connection);
            // 参数设置可以被抽取提供配置
            statement.setQueryTimeout(350);
            statement.setFetchSize(10000);
            return statement;
        } catch (Exception e) {
            throw new RuntimeException("Error prepare statement. Cause: " + e, e);
        }
    }

    protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

}
  • 在语句处理器基类中将参数信息、结果信息进行封装处理。
  • 之后是对 BaseStatementHandler#prepare 方法的处理包括定义实例化抽象方法这个方法交由各个具体的子类进行处理。
    • SimpleStatementHandler 简单语句处理器只是对 SQL 的最基本执行没有参数的设置。
    • PreparedStatementHandler 预处理语句处理器是我们在 JDBC 中使用的最多的操作方式PreparedStatement 设置 SQL传递参数的设置过程。

3.4.3 SimpleStatementHandler 简单语句处理器

SimpleStatementHandler.java

package com.lino.mybatis.executor.statement;

import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @description: 简单语句处理器STATEMENT
 */
public class SimpleStatementHandler extends BaseStatementHandler {

    public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameterObject, resultHandler, boundSql);
    }

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        return connection.createStatement();
    }

    @Override
    public void parameterize(Statement statement) {

    }

    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        String sql = boundSql.getSql();
        statement.execute(sql);
        return resultSetHandler.handleResultSets(statement);
    }
}
  • 简单语句处理器只是对 SQL 的最基本执行没有参数的设置。

3.4.4 PreparedStatementHandler 预处理语句处理器

PreparedStatementHandler.java

package com.lino.mybatis.executor.statement;

import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @description: 预处理语句处理器PREPARED
 * @author: lingjian
 * @createDate: 2022/11/8 14:13
 */
public class PreparedStatementHandler extends BaseStatementHandler {

    public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultSetHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameterObject, resultSetHandler, boundSql);
    }

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        return connection.prepareStatement(sql);
    }

    @Override
    public void parameterize(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.setLong(1, Long.parseLong(((Object[]) parameterObject)[0].toString()));
    }

    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.handleResultSets(ps);
    }
}
  • 在预处理语句处理器中包括 instantiateStatement 预处理 SQLparameterize 设置参数以及 query 查询的执行操作。
  • parameterize 暂时是写死的处理后续再完善。
  • query 是执行查询和对结果的封装结果的封装后续再完善。

3.5 结果处理器

定义结果处理器接口结果集处理器接口默认Map结果集处理器实现类

3.5.1 结果处理器接口

ResultHandler.java

package com.lino.mybatis.session;

/**
 * @description: 结果处理器
 */
public interface ResultHandler {
    /**
     * 处理结果
     */
    void handleResult();
}

3.5.2 结果集处理器接口

ResultSetHandler.java

package com.lino.mybatis.executor.resultset;

import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @description: 结果集处理器
 */
public interface ResultSetHandler {
    /**
     * 处理结果集
     *
     * @param stmt 语句
     * @param <E>  泛型
     * @return 泛型集合
     * @throws SQLException SQL异常
     */
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;
}

3.5.3 默认Map结果集处理器实现类

DefaultResultSetHandler.java

package com.lino.mybatis.executor.resultset;

import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import java.lang.reflect.Method;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 默认Map结果处理器
 */
public class DefaultResultSetHandler implements ResultSetHandler {

    private final BoundSql boundSql;

    public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {
        this.boundSql = boundSql;
    }

    @Override
    public <E> List<E> handleResultSets(Statement stmt) throws SQLException {
        ResultSet resultSet = stmt.getResultSet();
        try {
            return resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
        List<T> list = new ArrayList<>();
        try {
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            // 每次遍历值
            while (resultSet.next()) {
                T obj = (T) clazz.newInstance();
                for (int i = 1; i <= columnCount; i++) {
                    Object value = resultSet.getObject(i);
                    String columnName = metaData.getColumnName(i);
                    String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
                    Method method;
                    if (value instanceof Timestamp) {
                        method = clazz.getMethod(setMethod, LocalDateTime.class);
                    } else {
                        method = clazz.getMethod(setMethod, value.getClass());
                    }
                    method.invoke(obj, value);
                }
                list.add(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }
}

3.6 在配置项中添加执行器、语句处理器、结果处理器

Configuration.java

package com.lino.mybatis.session;

import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.SimpleExecutor;
import com.lino.mybatis.executor.resultset.DefaultResultSetHandler;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.executor.statement.PreparedStatementHandler;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 配置项
 * @author: lingjian
 * @createDate: 2022/11/7 21:32
 */
public class Configuration {

    //省略...

    /**
     * 生产执行器
     *
     * @param transaction 事务
     * @return 执行器
     */
    public Executor newExecutor(Transaction transaction) {
        return new SimpleExecutor(this, transaction);
    }

    /**
     * 创建语句处理器
     *
     * @param executor        执行器
     * @param mappedStatement 映射器语句类
     * @param parameter       参数
     * @param resultHandler   结果处理器
     * @param boundSql        SQL语句
     * @return StatementHandler 语句处理器
     */
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
        return new PreparedStatementHandler(executor, mappedStatement, parameter, resultHandler, boundSql);
    }

    /**
     * 创建结果集处理器
     *
     * @param executor        执行器
     * @param mappedStatement 映射器语句类
     * @param boundSql        SQL语句
     * @return ResultSetHandler 结果集处理器
     */
    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {
        return new DefaultResultSetHandler(executor, mappedStatement, boundSql);
    }
}

3.7 执行器创建和使用

  • 执行器开发完成以后则需要串联到 DefaultSqlSession 中进行使用。
  • 串联过程在创建 DefaultSqlSession 的时候构建出执行器并作为参数传递进去。这里涉及到 DefaultSqlSessionFactory#openSession 的处理。

3.7.1 在 DefaultSqlSession 中开启执行器

DefaultSqlSession.java

package com.lino.mybatis.session.defaults;

import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import com.lino.mybatis.session.SqlSessionFactory;
import com.lino.mybatis.session.TransactionIsolationLevel;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.TransactionFactory;
import java.sql.SQLException;

/**
 * @description: 默认的SqlSessionFactory实现类
 */
public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        Transaction tx = null;
        try {
            final Environment environment = configuration.getEnvironment();
            TransactionFactory transactionFactory = environment.getTransactionFactory();
            tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);
            // 创建执行器
            final Executor executor = configuration.newExecutor(tx);
            // 创建 DefaultSqlSession
            return new DefaultSqlSession(configuration, executor);
        } catch (Exception e) {
            try {
                assert tx != null;
                tx.close();
            } catch (SQLException ignore) {

            }
            throw new RuntimeException("Error opening session. Cause: " + e);
        }
    }
}
  • openSession 中开启事务传递给执行器的创建。并在执行器创建完毕后作为参数传递给 DefaultSqlSession

3.7.2 在 DefaultSqlSession 使用执行器

DefaultSqlSession.java

package com.lino.mybatis.session.defaults;

import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import java.lang.reflect.Method;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 默认sqlSession实现类
 */
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;
    private Executor executor;

    public DefaultSqlSession(Configuration configuration, Executor executor) {
        this.configuration = configuration;
        this.executor = executor;
    }

    @Override
    public <T> T selectOne(String statement) {
        return (T) ("你被代理了!" + statement);
    }

    @Override
    public <T> T selectOne(String statement, Object parameter) {
        MappedStatement ms = configuration.getMappedStatement(statement);
        List<T> list = executor.query(ms, parameter, Executor.NO_RESULT_HANDLER, ms.getBoundSql());
        return list.get(0);
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type, this);
    }

    @Override
    public Configuration getConfiguration() {
        return configuration;
    }
}
  • DefaultSqlSession#selectOne 中获取 MappedStatement 映射语句后则传递给执行器进行处理。
  • 那么现在这些类经过设计思想的解耦后就变得更加干净整洁了也易于维护和扩展了。

四、测试SQL执行的定义和实现

ApiTest.java

@Test
public void test_SqlSessionFactoryExecutor() throws IOException {
    // 1.从SqlSessionFactory中获取SqlSession
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 2.获取映射器对象
    IUserDao userDao = sqlSession.getMapper(IUserDao.class);

    // 3.测试验证
    User user = userDao.queryUserInfoById(1L);
    logger.info("测试结果{}", JSON.toJSONString(user));
}

测试结果

16:40:58.484 [main] INFO  c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
16:40:59.195 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 1436664465.
16:40:59.262 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
  • 从测试结果看我们已经把 DefaultSqlSession#selectOne 中的调用换成执行器完成整个过程的处理解耦了部分的逻辑操作方便后续的扩展。

五、总结SQL执行的定义和实现

  • 整个实现都是处理解耦这件事情从 DefaultSqlSession#selectOne 对数据源的处理解耦到执行器中进行操作。而执行器中又包括了对 JDBC 处理的拆解链接、准备语句、封装参数、处理结果所有的这些过程经过解耦后的类和方法在后续方便进行扩展。
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6