• MyBatis 原码解析(version:3.2.7)


    mybatis-plus 实践及架构原理.pdf

    mybatis-plus思维导图

    首先,我们看使用原生的JDBC来操作数据库的方式:

    // 1. 获取JDBC Connection
    Connection connection = DbManager.getConnectoin();
    // 2. 组装sql语句
    String sql = "insert into temp(id, name) values(?,?)";
    // 3. 构造 PreparedStatement
    PreparedStatement ps = connection.prepareStatement(sql);
    // 4. 为 PreparedStatement 设置参数
    ps.setLong(1, 1L);
    ps.setString(2, "aaa");
    // 5. 执行 PreparedStatement
    int count = ps.executeUpdate();

    那么,MyBatis是如何对上面的过程进行封装的呢?

    我们以update为例,看MyBatis是如何封装这几个步骤的:

    sql执行的入口:

    DefaultSqlSession.update(String statement, Object parameter) --> Executor.update(MappedStatement ms, Object parameter) -->
     SimpleExecutor.doUpdate(MappedStatement ms, Object parameter)

    DefaultSqlSession.update(String, Object)

    public int update(String statement, Object parameter) {
        try {
          dirty = true;
          MappedStatement ms = configuration.getMappedStatement(statement); // 将mapper中的sql解析成原生的JDBC能够识别的sql,其中包括MyBatis标签的解析,"#{key,JdbcType=XXX}"占位符替换成"?"
          return executor.update(ms, wrapCollection(parameter)); // 构造java.sql.PreparedStatement --> 往java.sql.PreparedStatement设置sql的参数 --> 执行sql语句
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
    }

    可以看出,MyBatis将这个过程封装成了两步:

    1. sql语句解析 (解析sql,替换占位符为"?")

    2. sql语句执行 (设置sql中的参数,执行sql)

    ### 1. sql语句解析

    configuration.getMappedStatement(statement)会去解析MyBatis的标签(比如:<foreach>、<if>等),并且将"#{key, JdbcType=XXX}"替换成原生的JDBC看的懂的占位符"?"。
    流程如下:

    Configuration.getMappedStatement(String, boolean)  --> Configuration.buildAllStatements() --> MapperAnnotationBuilder.parseStatement(Method) -->
     MapperAnnotationBuilder.buildSqlSourceFromStrings(String[], Class<?>, LanguageDriver) --> RawLanguageDriver.createSqlSource(Configuration, String, Class<?>)
     --> XMLLanguageDriver.createSqlSource(Configuration, String, Class<?>)

     20170721更新:(xml应该是如下)

    Configuration.getMappedStatement(String, boolean)  --> Configuration.buildAllStatements() --> XMLStatementBuilder.parseStatementNode() -->
     LanguageDriver.createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) --> RawLanguageDriver.createSqlSource(Configuration, String, Class<?>)
     --> XMLLanguageDriver.createSqlSource(Configuration, String, Class<?>)

    可以看出,MyBatis中的动态SQL解析是通过别名为 xml 语言驱动器 org.apache.ibatis.scripting.xmltags.XmlLanguageDriver 驱动解析的。

    public class XMLLanguageDriver implements LanguageDriver {
    
      public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
      }
    
      public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
        XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
        return builder.parseScriptNode();
      }
    
      public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
        if (script.startsWith("<script>")) { // issue #3
          XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
          return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
        } else {
          script = PropertyParser.parse(script, configuration.getVariables()); // issue #127
          TextSqlNode textSqlNode = new TextSqlNode(script);
          if (textSqlNode.isDynamic()) { // 动态sql解析入口
            return new DynamicSqlSource(configuration, textSqlNode);
          } else {
            return new RawSqlSource(configuration, script, parameterType);
          }
        }
      }
    
    }

    DynamicSqlSource.java

    public class DynamicSqlSource implements SqlSource {
    
      private Configuration configuration;
      private SqlNode rootSqlNode;
    
      public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
        this.configuration = configuration;
        this.rootSqlNode = rootSqlNode;
      }
    
      public BoundSql getBoundSql(Object parameterObject) {
        DynamicContext context = new DynamicContext(configuration, parameterObject); // 构造DynamicContext,并将DynamicContext中的属性ContextMap bindings赋值
        rootSqlNode.apply(context); // 处理sql中的标签,比如:<foreach>、<if>等
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
        Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
        SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); // 将占位符"#{key, jdbcType=XXX}"替换成占位符"?"。也就是原生的java.sql.PreparedStatement支持的占位符。(具体是通过GenericTokenParser#parse(String text)来实现的)
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject); // 构造BoundSql
        for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
          boundSql.setAdditionalParameter(entry.getKey(), entry.getValue()); // 将参数的key和value存放至BoundSql的MetaObject metaParameters;中
        }
        return boundSql;
      }
    
    }

    ####2. sql语句执行

    通过SimpleExecutor.java 去执行sql语句。其中,最重要的两步是 1. prepareStatement(handler, ms.getStatementLog()); 2. handler.update(stmt);

    public class SimpleExecutor extends BaseExecutor {
    
      public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
      }
    
      // 所有的数据库更新操作,都会调用doUpdate(),包括insert、update、delete
      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); // 构造执行Statement的StatementHandler
          stmt = prepareStatement(handler, ms.getStatementLog()); // 通过java.sql.Connection拿到java.sql.PreparedStatement,并且为 java.sql.PreparedStatement 设置sql中的参数
          return handler.update(stmt); // 代理执行 java.sql.PreparedStatement (PreparedStatementHandler.update(Statement))
        } finally {
          closeStatement(stmt);
        }
      }
    
      // 所有的数据库查询操作,都用调用doQuery()
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
    
      public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
        return Collections.emptyList();
      }
    
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection); // 通过 java.sql.Connection 拿到 java.sql.PreparedStatement (PreparedStatementHandler.instantiateStatement(Connection))
        handler.parameterize(stmt); // 为 java.sql.PreparedStatement 设置sql中的参数。(DefaultParameterHandler.setParameters(PreparedStatement))
        return stmt;
      }
    
    }

    至此,MyBatis的封装就一目了然了。^_^

    附:
    MyBatis开发文档: http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html

  • 相关阅读:
    TransformAroundPointPlugin
    探讨VMP 2.12.3 导入表修复
    spring+quartz 实现定时任务二
    一个奇怪的sql异常
    php程序里面使用sudo来执行应用程序
    spring+quartz 实现定时任务一
    让Git忽略SSL证书错误技巧
    个人开发网站集合
    完美解决KMplayer无法播放RMVB、RM电影问题
    如何知道自己的Windows是否已激活? 如何实现免激活升级? 重装Windows不需再激活?
  • 原文地址:https://www.cnblogs.com/kevin-yuan/p/7206988.html
Copyright © 2020-2023  润新知