美文网首页
mybatis-plus偶见小bug

mybatis-plus偶见小bug

作者: 丑人林宗己 | 来源:发表于2020-11-19 22:39 被阅读0次

    最近使用mybatis-plus出现了一个小bug,版本是mybatis-plus:3.0.1

    使用IService接口内置的updateBatchById时报错了,原因是没有找到对应的xxxx.Mapper.updateById

    所以翻了一下BaseMapper的接口也确实没有该同名方法的声明,觉得挺不可思议的,一个知名度相当高的Mybatis加强版组件,居然有这样低级的错误?但是翻阅时,发现内置写了SqlMethod.UPDATE_BY_IDSQL脚本,出于好奇,了解一下Mybatis-plus的实现方案。

    于是到Gitee下载了Mybatis-plus的源码稍微翻了一下(版本3.4.1),装配Mapper思路大概如下:

    MybatisMapperRegistry.addMapper()
     ->MybatisMapperAnnotationBuilder.parse()
      ->AbstractSqlInjector.inspectInject()
      // 很重要的一段代码:List<AbstractMethod> methodList = this.getMethodList(mapperClass)
       ->AbstractMethod.addMappedStatement()
    

    DefaultSqlInjector返回了BaseMapper方法下所有的声明了的方法对应的MappedStatement,通过injectMappedStatement方法实现注入Mybatis最核心Configuration类的mappedStatements缓存中。

    由于用的是批量更新,顺道看看批量更新有没有做一些优化。

    截了一部分3.0.1的代码(3.4.1的代码实现稍微复杂一点,但是本质是相同的):

    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
            if (CollectionUtils.isEmpty(entityList)) {
                throw new IllegalArgumentException("Error: entityList must not be empty");
            } else {
                try {
                    SqlSession batchSqlSession = this.sqlSessionBatch();
                    Throwable var4 = null;
    
                    try {
                        int i = 0;
                        String sqlStatement = this.sqlStatement(SqlMethod.UPDATE_BY_ID);
    
                        for(Iterator var7 = entityList.iterator(); var7.hasNext(); ++i) {
                            T anEntityList = var7.next();
                            ParamMap<T> param = new ParamMap();
                            param.put("et", anEntityList);
                            batchSqlSession.update(sqlStatement, param);
                            if (i >= 1 && i % batchSize == 0) {
                                batchSqlSession.flushStatements();
                            }
                        }
    
                        batchSqlSession.flushStatements();
    

    注意看sqlSessionBatch()这个方法,最终决定了Executor的类型为BatchExecutor,而看该类实现的doUpdate方法

    public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
        final Configuration configuration = ms.getConfiguration();
        final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
        final BoundSql boundSql = handler.getBoundSql();
        final String sql = boundSql.getSql();
        final Statement stmt;
        if (sql.equals(currentSql) && ms.equals(currentStatement)) {
     // 如果是同一个sql,使用的是同一个MappedStatment,则使用同一个Statement
          int last = statementList.size() - 1;
          stmt = statementList.get(last);
          applyTransactionTimeout(stmt);
         handler.parameterize(stmt);//fix Issues 322
          BatchResult batchResult = batchResultList.get(last);
          batchResult.addParameterObject(parameterObject);
        } else {
          Connection connection = getConnection(ms.getStatementLog());
          stmt = handler.prepare(connection, transaction.getTimeout());
          handler.parameterize(stmt);    //fix Issues 322
          currentSql = sql;
          currentStatement = ms;
          statementList.add(stmt);
          batchResultList.add(new BatchResult(ms, sql, parameterObject));
        }
      // handler.parameterize(stmt);
        handler.batch(stmt);
        return BATCH_UPDATE_RETURN_VALUE;
      }
    

    查阅SimpleExecutor可以看到每次执行完SQL都会将Statement关闭(源码如下),主要目的是清理该Statement所占据的数据库资源,比如游标。所以如果在批量操作中使用同一个Statement来执行SQL,然后定期执行多次后再通过flushStatements操作达到刷新的目的,可以避免频繁的创建,销毁Statement所带来的高额开销。

    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.update(stmt);
        } finally {
          closeStatement(stmt);
        }
      }
    

    相关文章

      网友评论

          本文标题:mybatis-plus偶见小bug

          本文链接:https://www.haomeiwen.com/subject/vrqriktx.html