带你学习Mybatis之StatementHandler数据库交互

StatementHandler数据库交互

StatementHandler接口是mybatis的核心接口之一,StatementHandler接口中功能很多,进行预编译并且调用ParameterHandler的setParameters()方法设置参数,创建Statement对象,通过ParameterHandler为SQL语句绑定实参,执行select、insert、update、delete等多种类型的SQL语句,批量执行SQL语句,通过ResultSetHandler将结果集映射成结果对象。

StatementHandler流程

  • 创建: 由Configuration方法中newStatementHandler()方法生成的,生成的是RoutingStatementHandler
  • 选择: RoutingStatementHandler的通过适配器模式找到对应(根据上下文)的StatementHandler执行的,并且有SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler
  • Executor会调用StatementHandler的prepare方法创建Statement,调用parameterize方法通过ParameterHandler设置参数,完成预编译(只有Statement为PreparedStatement时才会进行预编译)
  • 最后调用ResultSetHandler封装结果集
public interface StatementHandler {
 // 从连接中获取一个Statement
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException
;
 // 绑定statement执行时所需的实参
  void parameterize(Statement statement)
      throws SQLException
;
 // 批量执行SQL语句
  void batch(Statement statement)
      throws SQLException
;
 // 执行update/insert/delete语句
  int update(Statement statement)
      throws SQLException
;
 // 执行select语句
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException
;

  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException
;

  BoundSql getBoundSql();
 // 获取其中封装的ParameterHandler
  ParameterHandler getParameterHandler();
}

StatementHandler有两个实现类RoutingStatementHandler和BaseStatementHandler

BaseStatementHandler

BaseStatementHandler是实现了一个StatementHandler接口的抽象类,只提供了参数绑定相关的方法

public abstract class BaseStatementHandler implements StatementHandler {
    protected final Configuration configuration;
    protected final ObjectFactory objectFactory;
    protected final TypeHandlerRegistry typeHandlerRegistry;
   // 将结果集映射为对象
    protected final ResultSetHandler resultSetHandler;
   // 主要是为SQL语句绑定实参,使用传入的实参替换SQL语句的中?占位符
    protected final ParameterHandler parameterHandler;
   // 记录执行SQL语句的Executor对象
    protected final Executor executor;
   // 记录SQL语句对应的MappedStatement
    protected final MappedStatement mappedStatement;
   // 记录用户设置的offset和limit,用于用在结果集中定位映射的起始位置和结束位置
    protected final RowBounds rowBounds;
   // 记录SQK语句对应的BoundSql
    protected BoundSql boundSql;

    protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
        this.objectFactory = this.configuration.getObjectFactory();
        if (boundSql == null) {
           // 调用keyGenerator的processBefore方法获取主键
            this.generateKeys(parameterObject);
            boundSql = mappedStatement.getBoundSql(parameterObject);
        }

        this.boundSql = boundSql;
        this.parameterHandler = this.configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        this.resultSetHandler = this.configuration.newResultSetHandler(executor, mappedStatement, rowBounds, this.parameterHandler, resultHandler, boundSql);
    }

    public BoundSql getBoundSql() {
        return this.boundSql;
    }

    public ParameterHandler getParameterHandler() {
        return this.parameterHandler;
    }
  // 实现StatementHandler的prepare方法
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(this.boundSql.getSql());
        Statement statement = null;

        try {
           // 初始化Statement对象,由子类实现(模板方法模式)
            statement = this.instantiateStatement(connection);
            this.setStatementTimeout(statement, transactionTimeout);
            this.setFetchSize(statement);
            return statement;
        } catch (SQLException var5) {
            this.closeStatement(statement);
            throw var5;
        } catch (Exception var6) {
            this.closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + var6, var6);
        }
    }

    protected abstract Statement instantiateStatement(Connection var1) throws SQLException;

    protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
        Integer queryTimeout = null;
        if (this.mappedStatement.getTimeout() != null) {
            queryTimeout = this.mappedStatement.getTimeout();
        } else if (this.configuration.getDefaultStatementTimeout() != null) {
            queryTimeout = this.configuration.getDefaultStatementTimeout();
        }

        if (queryTimeout != null) {
            stmt.setQueryTimeout(queryTimeout);
        }

        StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);
    }

    protected void setFetchSize(Statement stmt) throws SQLException {
        Integer fetchSize = this.mappedStatement.getFetchSize();
        if (fetchSize != null) {
            stmt.setFetchSize(fetchSize);
        } else {
            Integer defaultFetchSize = this.configuration.getDefaultFetchSize();
            if (defaultFetchSize != null) {
                stmt.setFetchSize(defaultFetchSize);
            }

        }
    }

    protected void closeStatement(Statement statement) {
        try {
            if (statement != null) {
                statement.close();
            }
        } catch (SQLException var3) {
        }

    }

    protected void generateKeys(Object parameter) {
        KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator();
        ErrorContext.instance().store();
        keyGenerator.processBefore(this.executor, this.mappedStatement, (Statement)null, parameter);
        ErrorContext.instance().recall();
    }
}

在BoundSql中记录的SQL语句中可能包含"?"占位符,而每个"?"占位符都对应了BoundSql.parameterMappings集合中的一个元素,ParameterMapping对象中记录了对应参数的名称以及相关属性
在ParameterHandler中的setParameters方法负责为SQL语句绑定实参,该接口只有一个实现类DefaultParameterHandler,遍历BoundSql.parameterMappings集合,根据ParameterMapping对象中记录的参数名称查找相应的实参,通过TypeHandler转换为JdbcType

SimpleStatementHandler

SimpleStatementHandler继承了BaseStatementHandler抽象类,底层直接使用java.sql.Statement对象来操作数据库,SQL中不能存在占位符,parameterize方法为空实现

public class SimpleStatementHandler extends BaseStatementHandler {
    public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
    }
  
   //增删改都会走update方法
    public int update(Statement statement) throws SQLException {
        String sql = this.boundSql.getSql();
        Object parameterObject = this.boundSql.getParameterObject();
        KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator();
        int rows;
        if (keyGenerator instanceof Jdbc3KeyGenerator) {
            statement.execute(sql, 1);
           // 受影响的行数
            rows = statement.getUpdateCount();
           // 数据库生成的主键添加到parameterObject中
            keyGenerator.processAfter(this.executor, this.mappedStatement, statement, parameterObject);
        } else if (keyGenerator instanceof SelectKeyGenerator) {
            statement.execute(sql);
            rows = statement.getUpdateCount();
            keyGenerator.processAfter(this.executor, this.mappedStatement, statement, parameterObject);
        } else {
            statement.execute(sql);
            rows = statement.getUpdateCount();
        }

        return rows;
    }

    public void batch(Statement statement) throws SQLException {
        String sql = this.boundSql.getSql();
        statement.addBatch(sql);
    }

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        String sql = this.boundSql.getSql();
        statement.execute(sql);
        return this.resultSetHandler.handleResultSets(statement);
    }

    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        String sql = this.boundSql.getSql();
        statement.execute(sql);
        return this.resultSetHandler.handleCursorResultSets(statement);
    }
  
   // 初始化Statement
    protected Statement instantiateStatement(Connection connection) throws SQLException {
       // 直接使用Connection对象创建Statement对象
        return this.mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.createStatement() : connection.createStatement(this.mappedStatement.getResultSetType().getValue(), 1007);
    }

    public void parameterize(Statement statement) {
    }
}
PreparedStatementHandler

PreparedStatementHandler继承了BaseStatementHandler抽象类,底层使用java.sql.PreparedStatement对象来操作数据库,PreparedStatement是预编译的,可以在sql语句中存在"?"占位符,parameterize方法通过调用ParameterHandler.setParameters()方法完成SQL语句的参数绑定

public class PreparedStatementHandler extends BaseStatementHandler {
    public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
    }

    public int update(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        ps.execute();
        int rows = ps.getUpdateCount();
        Object parameterObject = this.boundSql.getParameterObject();
        KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(this.executor, this.mappedStatement, ps, parameterObject);
        return rows;
    }

    public void batch(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        ps.addBatch();
    }

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        ps.execute();
        return this.resultSetHandler.handleResultSets(ps);
    }

    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        ps.execute();
        return this.resultSetHandler.handleCursorResultSets(ps);
    }
  
   //初始化Statement,进行预编译
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = this.boundSql.getSql();
       // 根据mappedStatement.getKeyGenerator()字段的值,创建PrepareStatement
        if (this.mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
            String[] keyColumnNames = this.mappedStatement.getKeyColumns();
            return keyColumnNames == null ? connection.prepareStatement(sql, 1) : connection.prepareStatement(sql, keyColumnNames);
        } else {
            return this.mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.prepareStatement(sql) : connection.prepareStatement(sql, this.mappedStatement.getResultSetType().getValue(), 1007);
        }
    }

    public void parameterize(Statement statement) throws SQLException {
        this.parameterHandler.setParameters((PreparedStatement)statement);
    }
}
CallableStatementHandler

CallableStatementHandler继承了BaseStatementHandler抽象类,底层使用java.sql.CallableStatement调用指定的存储过程,其parameterize方法通过调用ParameterHandler.setParameters()方法完成SQL语句的参数绑定,并指定输出参数的索引位置和JDBC类型。

public class CallableStatementHandler extends BaseStatementHandler {
    public CallableStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
    }

    public int update(Statement statement) throws SQLException {
        CallableStatement cs = (CallableStatement)statement;
        cs.execute();
        int rows = cs.getUpdateCount();
        Object parameterObject = this.boundSql.getParameterObject();
        KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(this.executor, this.mappedStatement, cs, parameterObject);
        this.resultSetHandler.handleOutputParameters(cs);
        return rows;
    }

    public void batch(Statement statement) throws SQLException {
        CallableStatement cs = (CallableStatement)statement;
        cs.addBatch();
    }

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        CallableStatement cs = (CallableStatement)statement;
        cs.execute();
        List<E> resultList = this.resultSetHandler.handleResultSets(cs);
        this.resultSetHandler.handleOutputParameters(cs);
        return resultList;
    }

    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        CallableStatement cs = (CallableStatement)statement;
        cs.execute();
        Cursor<E> resultList = this.resultSetHandler.handleCursorResultSets(cs);
        this.resultSetHandler.handleOutputParameters(cs);
        return resultList;
    }

    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = this.boundSql.getSql();
        return this.mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.prepareCall(sql) : connection.prepareCall(sql, this.mappedStatement.getResultSetType().getValue(), 1007);
    }

    public void parameterize(Statement statement) throws SQLException {
        this.registerOutputParameters((CallableStatement)statement);
        this.parameterHandler.setParameters((CallableStatement)statement);
    }

    private void registerOutputParameters(CallableStatement cs) throws SQLException {
        List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
        int i = 0;

        for(int n = parameterMappings.size(); i < n; ++i) {
            ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
            if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
                if (null == parameterMapping.getJdbcType()) {
                    throw new ExecutorException("The JDBC Type must be specified for output parameter.  Parameter: " + parameterMapping.getProperty());
                }

                if (parameterMapping.getNumericScale() != null && (parameterMapping.getJdbcType() == JdbcType.NUMERIC || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {
                    cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());
                } else if (parameterMapping.getJdbcTypeName() == null) {
                    cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);
                } else {
                    cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getJdbcTypeName());
                }
            }
        }

    }
}

RoutingStatementHandler

RoutingStatementHandler是StatementHandler接口的另一个实现类,会根据MappedStatement中指定的statementType来创建对应的StatementHandler接口实现。

public class RoutingStatementHandler implements StatementHandler {
    private final StatementHandler delegate;

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
       // 根据statementType来设置相应的接口实现
        switch(ms.getStatementType()) {
        case STATEMENT:
            this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
            this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
            this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }

    }

    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        return this.delegate.prepare(connection, transactionTimeout);
    }

    public void parameterize(Statement statement) throws SQLException {
        this.delegate.parameterize(statement);
    }

    public void batch(Statement statement) throws SQLException {
        this.delegate.batch(statement);
    }

    public int update(Statement statement) throws SQLException {
        return this.delegate.update(statement);
    }

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        return this.delegate.query(statement, resultHandler);
    }

    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        return this.delegate.queryCursor(statement);
    }

    public BoundSql getBoundSql() {
        return this.delegate.getBoundSql();
    }

    public ParameterHandler getParameterHandler() {
        return this.delegate.getParameterHandler();
    }
}

https://zhhll.icu/2020/框架/mybatis/组件分析/5.mybatis之StatementHandler/

本文由 mdnice 多平台发布

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: MyBatis 是一个开源的持久层框架,可以方便地将 SQL 语句和 Java 对象进行映射。如果您想要学习 MyBatis 源码,可以按照以下步骤进行: 1. 了解 MyBatis 的架构和设计原理。可以阅读官方文档和相关书籍,例如《MyBatis 技术内幕》。 2. 下载 MyBatis 的源代码,并导入到 IDE 中。MyBatis 使用 Maven 进行构建,您可以使用 IDE 的 Maven 插件来下载依赖项。 3. 查看 MyBatis 的源代码结构。MyBatis 的主要代码在 `mybatis-3` 模块中,包括 `src/main/java` 和 `src/main/resources` 目录。其中,`src/main/java` 目录包含了 MyBatis 的核心代码,例如 `org.apache.ibatis.session.SqlSession` 类;`src/main/resources` 目录包含了 MyBatis 的配置文件和映射文件。 4. 阅读 MyBatis 的源代码。可以从 MyBatis 的入口处 `org.apache.ibatis.session.SqlSessionFactoryBuilder` 开始,深入了解 MyBatis 的初始化流程、SQL 语句的执行流程、映射文件的解析和缓存等。 5. 调试 MyBatis 的源代码。可以使用 IDE 的调试功能,对 MyBatis 进行单步调试,观察代码的执行流程,加深对 MyBatis 的理解。 6. 学习 MyBatis 的单元测试。MyBatis 的单元测试位于 `src/test/java` 目录中,可以通过单元测试来了解 MyBatis 的各个功能点的使用方法和测试用例。 7. 参与 MyBatis 的开发。如果您对 MyBatis 源码有深入的了解,并希望为 MyBatis 做出贡献,可以参与 MyBatis 的开发,贡献代码和文档,提交 issue 和 PR。MyBatis 的开发社区非常活跃,可以在官方网站和 GitHub 上找到相关信息。 希望这些步骤对您学习 MyBatis 源码有所帮助。 ### 回答2: MyBatis是一个开源的Java持久层框架,通过操作对象与数据库关系映射来提供数据持久化的功能。了解MyBatis源码是学习和使用该框架的重要一步。 首先,MyBatis的源码结构比较清晰,主要分为核心模块和附属模块。核心模块包括XML配置解析、SQL语句解析、参数处理、数据库连接管理等功能的实现,是实现MyBatis基本功能的核心部分。附属模块包括缓存、事务、插件等额外功能的实现,可以根据需要进行扩展和配置。 学习MyBatis源码可以从以下几个方面入手: 1. 配置文件解析:MyBatis通过XML配置文件来进行相关的配置,了解配置文件的解析过程可以帮助理解MyBatis的初始化过程和各项配置的作用。 2. SQL语句解析与执行:MyBatis将SQL语句封装成MappedStatement对象进行管理,了解MappedStatement的生成过程,以及SQL语句的解析、参数处理和执行过程,可以深入了解MyBatis的SQL执行原理。 3. 会话管理和事务处理:MyBatis采用SqlSessionFactory和SqlSession来管理数据库连接和事务,在MyBatis源码中可以学习到如何管理数据库连接池、事务的提交和回滚等核心功能的实现。 4. 缓存机制:MyBatis提供了一级缓存和二级缓存的功能,了解缓存的生成和更新过程,以及缓存的命中和失效原理,可以提高数据库查询性能。 总之,通过学习MyBatis源码,可以加深对该框架的理解,掌握其内部实现原理,有助于在使用时更加灵活和高效地进行开发。同时,也为以后解决一些特殊问题提供了更多的思路和方法。 ### 回答3: MyBatis是一个优秀的持久层框架,学习其源码有助于理解其底层原理和设计思想。 首先,可以从MyBatis的入口开始学习,即SqlSessionFactoryBuilder类。该类负责解析配置文件、创建Configuration对象,并通过Configuration对象创建SqlSessionFactory实例。 接下来,可以学习Configuration类,该类负责管理整个MyBatis的配置信息。其中包括了数据库连接信息、映射文件信息、缓存信息等。在该类内部,会调用XMLMapperBuilder类解析映射文件,在解析映射文件过程中,会创建MappedStatement对象,该对象表示一条SQL语句的映射信息。 学习MappedStatement对象可以了解MyBatis的SQL语句解析过程。该对象包含了SQL语句的相关信息,包括参数映射关系、返回结果映射关系等。在执行SQL语句时,会使用ParameterHandler类处理参数,通过ResultSetHandler类处理查询结果。 同时,学习到Executor接口及其实现类,可以了解MyBatis的执行过程。Executor负责执行SQL语句,其中包括了写操作的update方法和读操作的query方法。在执行过程中,会通过StatementHandler类创建PreparedStatement对象,并通过ResultSetHandler类处理执行结果。 最后,还可以学习MyBatis的事务处理和缓存机制。Transaction接口及其实现类负责事务管理,通过JDBC的事务机制实现了事务的提交和回滚。而Cache接口及其实现类负责缓存查询结果,在查询时会先从缓存中查找结果。 总结来说,通过学习MyBatis的源码可以深入理解其底层原理和设计思想。从SqlSessionFactory的创建开始,到Configuration的配置解析、MappedStatement的创建,再到Executor的执行过程和Transaction的事务管理,以及Cache的缓存机制,逐步掌握MyBatis的各个组件和它们之间的交互关系。这对于我们使用MyBatis开发项目,解决问题和优化性能都具有积极的意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾光师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值