MyBatisMyBatis 源码分析——生成Statement接口实例

JDBC的文化对JAVA开发人员来讲在简约不了的知了。PreparedStatement的打算进一步胸有成竹。我们太广用到有俩独方式:executeQuery方法以及executeUpdate方法。这俩单方法外还有一个execute方法。只是这艺术我们充分少用。但是mybatis框架就是也因此是方法来促成的。不管mybatis用是啊一个术来实现。有少数足以得——那就算是须得Statement接口实例。你得这样子理解mybatis把哪些获得Statement接口实例做了一个完美的包裹。而就一个包裹就是高达亦然章出现的StatementHandler接口。

mybatis里面实现StatementHandler接口有四只类似。

RoutingStatementHandler类:笔者把其了解呢底三个像样的代理类。

CallableStatementHandler类:对承诺处理JDBC里面的CallableStatement类。

PreparedStatementHandler类:对诺处理JDBC里面的PreparedStatement类。

SimpleStatementHandler类:对许处理JDBC里面的貌似Statement接口实例(笔者为无晓JDBC是用受他呀)。

正而上面所云的撰稿人把RoutingStatementHandler类理解啊老三单近乎的代理类。mybatis并从未一直去引用后面三只八九不离十。而是通过RoutingStatementHandler类来判定时到底要调用哪个类。再去履行相关的Statement接口实例。

 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

即无异于段子源码就是前一章尾部源码的继执行。源码的作用就是新建一个RoutingStatementHandler类实例。关键之触及是当RoutingStatementHandle类的构造函数里面。

 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

起此处就可以看出作者为什么说RoutingStatementHandler类可以了解呢老三只类似的代理类。事实上所有的劳作都是里面成员delegate来做的。而delegate又是在构造函数里面进行判定生成的。看样子在此地JDBC的老三栽操作方式到的反映出来。通过MappedStatement的getStatementType方法取得相应返回值,判断时SQL语句是使为此啊一样种植操作办法来进行。默认情况下是因此Prepared方式。当前作者非是瞎说的。在MappedStatement的Builder方法里便已经装了。请读者们自行查看。

 1  public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
 2       mappedStatement.configuration = configuration;
 3       mappedStatement.id = id;
 4       mappedStatement.sqlSource = sqlSource;
 5       mappedStatement.statementType = StatementType.PREPARED;
 6       mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<ParameterMapping>()).build();
 7       mappedStatement.resultMaps = new ArrayList<ResultMap>();
 8       mappedStatement.sqlCommandType = sqlCommandType;
 9       mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
10       String logId = id;
11       if (configuration.getLogPrefix() != null) {
12         logId = configuration.getLogPrefix() + id;
13       }
14       mappedStatement.statementLog = LogFactory.getLog(logId);
15       mappedStatement.lang = configuration.getDefaultScriptingLanuageInstance();
16     }

倘实际不思量用默认的办法进行处理吧,可以以连锁每一个XML节点的statementType属性进行安装。如下

<select id="SelectProducts" resultMap="result" statementType="STATEMENT" >
        select * from Products where #{0} > ProductID and ProductName like #{1}
    </select>

生成Statement接口实例要就此到StatementHandler接口的二元单艺术:prepare方法与parameterize方法。prepare方法用于完成构建Statement接口实例。parameterize方法用于拍卖Statement接口实例对应之参数。理解当下同过程需要调头查看SimpleExecutor类的doQuery方法。

 1 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
 2     Statement stmt = null;
 3     try {
 4       Configuration configuration = ms.getConfiguration();
 5       StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
 6       stmt = prepareStatement(handler, ms.getStatementLog());
 7       return handler.<E>query(stmt, resultHandler) ; 
 8     } finally {
 9       closeStatement(stmt);
10     }
11   }

源码的prepareStatement方法中可以反映prepare方法与parameterize方法的打算。通过prepareStatement方法就可得一个整体Statement接口实例。最后以经StatementHandler接口实例的query方法来获得相应的结果。笔者暂还过了这一个过程(query方法处理结果)。让咱来看望关于prepare方法与parameterize方法。

 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

面说交prepare方法就是用于构建Statement接口实例。默认情况是PreparedStatementHandler类。那么笔者就将PreparedStatementHandler类来切入吧。当笔者点开PreparedStatementHandler类的源码,试着去查转prepare方法。发现搜索不交。原来他以PreparedStatementHandler类的父类(BaseStatementHandler类)里面。

 1  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
 2     ErrorContext.instance().sql(boundSql.getSql());
 3     Statement statement = null;
 4     try {
 5       statement = instantiateStatement(connection);
 6       setStatementTimeout(statement, transactionTimeout);
 7       setFetchSize(statement);
 8       return statement;
 9     } catch (SQLException e) {
10       closeStatement(statement);
11       throw e;
12     } catch (Exception e) {
13       closeStatement(statement);
14       throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
15     }
16   }

各国一个框架还来一个同台之性状——方法调来调去的。prepare方法中通过instantiateStatement方法来回到相关的Statement实例。而这个方法可是一个泛方法。

 protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

他的实例就是于分别的子类里面。完美的行使了持续的便宜。

 1  protected Statement instantiateStatement(Connection connection) throws SQLException {
 2     String sql = boundSql.getSql();
 3     if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
 4       String[] keyColumnNames = mappedStatement.getKeyColumns();
 5       if (keyColumnNames == null) {
 6         return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
 7       } else {
 8         return connection.prepareStatement(sql, keyColumnNames);
 9       }
10     } else if (mappedStatement.getResultSetType() != null) {
11       return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
12     } else {
13       return connection.prepareStatement(sql);
14     }
15   }

面的源码是PreparedStatementHandler类的。所以无用笔者多提——就是转PreparedStatement实例。

起矣PreparedStatement实例,当然就是假设对客进行设置相应的参数。这为是parameterize方法的来意。但是怎么是简简单单的装置那肯定并未呀但说的。主要还是坐mybatis对于设置参数方面做细致之设计。好话不多说。还是看一下源码最实在。

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

ParameterHandler接口的图不言而喻不用笔者多谈。DefaultParameterHandler类便是他的实例类。DefaultParameterHandler类的代码不多,可是他饱含的始末也游人如织。进去看一下尽管亮了。

 1  public void setParameters(PreparedStatement ps) {
 2     ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
 3     List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
 4     if (parameterMappings != null) {
 5       for (int i = 0; i < parameterMappings.size(); i++) {
 6         ParameterMapping parameterMapping = parameterMappings.get(i);
 7         if (parameterMapping.getMode() != ParameterMode.OUT) {
 8           Object value;
 9           String propertyName = parameterMapping.getProperty();
10           if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
11             value = boundSql.getAdditionalParameter(propertyName);
12           } else if (parameterObject == null) {
13             value = null;
14           } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
15             value = parameterObject;
16           } else {
17             MetaObject metaObject = configuration.newMetaObject(parameterObject);
18             value = metaObject.getValue(propertyName);
19           }
20           TypeHandler typeHandler = parameterMapping.getTypeHandler();
21           JdbcType jdbcType = parameterMapping.getJdbcType();
22           if (value == null && jdbcType == null) {
23             jdbcType = configuration.getJdbcTypeForNull();
24           }
25           try {
26             typeHandler.setParameter(ps, i + 1, value, jdbcType);
27           } catch (TypeException e) {
28             throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
29           } catch (SQLException e) {
30             throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
31           }
32         }
33       }
34     }

BoundSql类以平等坏面世在我们的先头,前面笔者也从没领到过关于BoundSql类的图。因为只要无一个上下文的打算是蛮不便推断出BoundSql类。笔者也只是从源码来拘禁的,也非必然是本着的。前面有的源码里面来起了用MappedStatement类。他得以说凡是一个富含节点(select节点,update节点等)信息的接近。但是针对之求实的SQL语句以的信可生少。那么BoundSql类就是存于的组建SQL句语信息。从源码里面我们可见到BoundSql类处理回来结果的信息可从没。有的只是SQL语句之参数之类的音信。如下他的内成员。

 private String sql;
  private List<ParameterMapping> parameterMappings;
  private Object parameterObject;
  private Map<String, Object> additionalParameters;
  private MetaObject metaParameters;

发矣针对性BoundSql类的定义认识,我们随后谈谈上面源码(setParameters方法有)里面有的政工吧。如果想要转手就掌握他是开呀的怎么样子做。那笔者不得不说自己功力不行。笔者不得不大体的见到他以做什么。通过BoundSql类获得相应的ParameterMapping类。找到相应之属性名(如:#{id})。接着通过传播的参数信息获得对应之MetaObject类。在经过MetaObject类和性名博对应属性名的价值。最后一步就是是透过TypeHandler接口实例设置值了。

顶了立中间StatementHandler接口的干活算结束了。对于MetaObject类是什么得到的,他还要是呀。笔者这里虽不多加言论。笔者留意的接触还是TypeHandler接口。这片之知识点官网也摆到了——typeHanlders。了解TypeHandler接口的源码也就算是变成了产一个靶了。

 

网站地图xml地图