好玩的网站源码,温州网站关键词推广,新建网站的步骤,广东学校网站建设公司文章目录 一、目标#xff1a;Insert自增索引值二、设计#xff1a;Insert自增索引值三、实现#xff1a;Insert自增索引值3.1 工程结构3.2 Insert自增索引值类图3.3 修改执行器3.3.1 修改执行器接口3.3.2 抽象执行器基类 3.4 键值生成器3.4.1 键值生成器接口3.4.2 不用键值… 文章目录 一、目标Insert自增索引值二、设计Insert自增索引值三、实现Insert自增索引值3.1 工程结构3.2 Insert自增索引值类图3.3 修改执行器3.3.1 修改执行器接口3.3.2 抽象执行器基类 3.4 键值生成器3.4.1 键值生成器接口3.4.2 不用键值生成器3.4.3 使用JDBC3键值生成器3.4.4 不自增键值生成器 3.5 配置和映射中添加键值生成器3.5.1 映射器语句类3.5.2 配置项3.5.3 默认Map结果处理器 3.6 解析selectkey3.6.1 修改语句处理器抽象基类3.6.2 预处理语句处理器3.6.3 注解配置构建器3.6.4 映射构建器助手3.6.5 XML语句构建器 3.7 JDBC类型和类型处理器注册机修改3.7.1 JDBC类型3.7.2 类型处理器注册机 3.8 JDBC链接获取 四、测试Insert自增索引值4.1 测试环境配置4.1.1 修改DAO持久层4.1.2 修改mapper配置文件 4.2 单元测试4.2.1 单元测试4.2.2 插入查询测试  五、总结Insert自增索引值 一、目标Insert自增索引值 在执行插入 SQL 后要返回此条插入语句后的自增索引? 当一次数据库操作有2条执行 SQL 语句的时候重点在于必须在同一个 DB 连接下否则将会失去事务的特性。 也就表示着如果不是同一 DB 连接下返回的自增 ID 将会是一个 0 值。 目标在解析 Mapper 配置文件以后调用预处理语句处理器执行 SQL 时需要在同一个链接下进行处理。 
二、设计Insert自增索引值 在执行插入 SQL 后返回插入的索引值。 需要在 insert 标签中新增 selectKey 标签并在 selectKey 标签中执行查询数据库索引的操作。所以基于这样的新增标签则会在 XML 语句构建器中添加对 selectKey 标签的解析并同样也需要把新增的解析映射器语句存放到配置项中。最终到执行 SQL 时在一个 DB 连接下完成两个 SQL 的操作。 以解析 Mapper XML 为入口处理 insert/delete/update/select 类型的 SQL 为入口获取 selectKey 标签并对此标签内的 SQL 进行解析封装。 把它当成一个查询操作封装成映射语句。注意这里只会对 insert 标签起作用其他标签并不会配置 selectKey 的操作。 当把 selectKey 解析完成以后也是像解析其他类型的标签一样按照 MappedStatement 映射器语句存放到 Configuration 配置项中。 这样后面执行 DefaultSqlSession 获取 SQL 的时候就可以从配置项获取并在执行器完成 SQL 的操作。注意对于键值的处理是单独包装的 KeyGenerator 键值生成器完成 SQL 的调用和结果封装的。 另外由于这里执行了2条 SQL 语句一条插入一条查询。而2条 SQL 必须在同一个 DB 连接下才能把正确的索引值返回回来。 因为这样是保证了一个事务的特性否则是查询不到插入的索引值的。注意获取链接是在 JdbcTransaction#getConnection 方法获取的只有获取的链接是已经存在的同一个才能正确执行返回结果。  
三、实现Insert自增索引值 
3.1 工程结构 
mybatis-step-14
|-src|-main|	|-java|		|-com.lino.mybatis|			|-annotations|			|	|-Delete.java|			|	|-Insert.java|			|	|-Select.java|			|	|-Update.java|			|-binding|			|	|-MapperMethod.java|			|	|-MapperProxy.java|			|	|-MapperProxyFactory.java|			|	|-MapperRegistry.java|			|-builder|			|	|-annotations|			|	|	|-MapperAnnotationBuilder.java|			|	|-xml|			|	|	|-XMLConfigBuilder.java|			|	|	|-XMLMapperBuilder.java|			|	|	|-XMLStatementBuilder.java|			|	|-BaseBuilder.java|			|	|-MapperBuilderAssistant.java|			|	|-ParameterExpression.java|			|	|-ResultMapResolver.java|			|	|-SqlSourceBuilder.java|			|	|-StaticSqlSource.java|			|-datasource|			|	|-druid|			|	|	|-DruidDataSourceFacroty.java|			|	|-pooled|			|	|	|-PooledConnection.java|			|	|	|-PooledDataSource.java|			|	|	|-PooledDataSourceFacroty.java|			|	|	|-PoolState.java|			|	|-unpooled|			|	|	|-UnpooledDataSource.java|			|	|	|-UnpooledDataSourceFacroty.java|			|	|-DataSourceFactory.java|			|-executor|			|	|-keygen|			|	|	|-Jdbc3KeyGenerator.java|			|	|	|-KeyGenerator.java|			|	|	|-NoKeyGenerator.java|			|	|	|-SelectKeyGenerator.java|			|	|-parameter|			|	|	|-ParameterHandler.java|			|	|-result|			|	|	|-DefaultResultContext.java|			|	|	|-DefaultResultHandler.java|			|	|-resultset|			|	|	|-DefaultResultSetHandler.java|			|	|	|-ResultSetHandler.java|			|	|	|-ResultSetWrapper.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|			|	|-ResultFlag.java|			|	|-ResultMap.java|			|	|-ResultMapping.java|			|	|-SqlCommandType.java|			|	|-SqlSource.java|			|-parsing|			|	|-GenericTokenParser.java|			|	|-TokenHandler.java|			|-reflection|			|	|-factory|			|	|	|-DefaultObjectFactory.java|			|	|	|-ObjectFactory.java|			|	|-invoker|			|	|	|-GetFieldInvoker.java|			|	|	|-Invoker.java|			|	|	|-MethodInvoker.java|			|	|	|-SetFieldInvoker.java|			|	|-property|			|	|	|-PropertyNamer.java|			|	|	|-PropertyTokenizer.java|			|	|-wrapper|			|	|	|-BaseWrapper.java|			|	|	|-BeanWrapper.java|			|	|	|-CollectionWrapper.java|			|	|	|-DefaultObjectWrapperFactory.java|			|	|	|-MapWrapper.java|			|	|	|-ObjectWrapper.java|			|	|	|-ObjectWrapperFactory.java|			|	|-MetaClass.java|			|	|-MetaObject.java|			|	|-Reflector.java|			|	|-SystemMetaObject.java|			|-scripting|			|	|-defaults|			|	|	|-DefaultParameterHandler.java|			|	|	|-RawSqlSource.java|			|	|-xmltags|			|	|	|-DynamicContext.java|			|	|	|-MixedSqlNode.java|			|	|	|-SqlNode.java|			|	|	|-StaticTextSqlNode.java|			|	|	|-XMLLanguageDriver.java|			|	|	|-XMLScriptBuilder.java|			|	|-LanguageDriver.java|			|	|-LanguageDriverRegistry.java|			|-session|			|	|-defaults|			|	|	|-DefaultSqlSession.java|			|	|	|-DefaultSqlSessionFactory.java|			|	|-Configuration.java|			|	|-ResultContext.java|			|	|-ResultHandler.java|			|	|-RowBounds.java|			|	|-SqlSession.java|			|	|-SqlSessionFactory.java|			|	|-SqlSessionFactoryBuilder.java|			|	|-TransactionIsolationLevel.java|			|-transaction|			|	|-jdbc|			|	|	|-JdbcTransaction.java|			|	|	|-JdbcTransactionFactory.java|			|	|-Transaction.java|			|	|-TransactionFactory.java|			|-type|			|	|-BaseTypeHandler.java|			|	|-DateTypeHandler.java|			|	|-IntegerTypeHandler.java|			|	|-JdbcType.java|			|	|-LongTypeHandler.java|			|	|-StringTypeHandler.java|			|	|-TypeAliasRegistry.java|			|	|-TypeHandler.java|			|	|-TypeHandlerRegistry.java|-test|-java|	|-com.lino.mybatis.test|	|-dao|	|	|-IActivityDao.java|	|-po|	|	|-Activity.java|	|-ApiTest.java|-resources|-mapper|	|-Activity_Mapper.xml|-mybatis-config-datasource.xml3.2 Insert自增索引值类图 在整个核心实现类中以 XMLStatmentBuilder 新增 processSelectKeyNodes 节点解析构建 MappedStatement 映射类语句。此处的映射类语句会通过 KeyGenerator 键值生成器实现类包装。 它的包装主要用于负责对 selectkey 标签中 SQL 语句的查询和结果封装。类似于 DefaultSqlSession 调用 Executor 执行器的过程。 另外整个 insert 的执行需要在 PreparedStatementHandler 预处理语句处理器的 update 方法进行扩充执行完插入操作再执行查询处理。 方法的返回值返回的是 SQL 影响的条数入参中的对象属性与 selectKey 配置的一样的名称字段会被填充索引值返回。注意在 Mybatis 框架中所有的 SQL 操作在执行器中只有 select 和 update也就是 insert/delete/update 都被 update 封装处理了。  
3.3 修改执行器 
3.3.1 修改执行器接口 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.session.RowBounds;
import com.lino.mybatis.transaction.Transaction;
import java.sql.SQLException;
import java.util.List;/*** description: 执行器*/
public interface Executor {.../*** 查询** param ms            映射器语句* param parameter     参数* param rowBounds     分页记录限制* param resultHandler 结果处理器* param boundSql      SQL对象* param E           返回的类型* return ListE* throws SQLException SQL异常*/E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;/*** 查询** param ms            映射器语句* param parameter     参数* param rowBounds     分页记录限制* param resultHandler 结果处理器* param E           返回的类型* return ListE* throws SQLException SQL异常*/E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;...
}3.3.2 抽象执行器基类 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.session.RowBounds;
import com.lino.mybatis.transaction.Transaction;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;/*** description: 执行器抽象基类*/
public abstract class BaseExecutor implements Executor {...Overridepublic E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {if (closed) {throw new RuntimeException(Executor was closed.);}return doQuery(ms, parameter, rowBounds, resultHandler, boundSql);}Overridepublic E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql  ms.getBoundSql(parameter);return query(ms, parameter, rowBounds, resultHandler, boundSql);}/*** 更新方法** param ms        映射器语句* param parameter 参数* return 返回的是受影响的行数* throws SQLException SQL异常*/protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;/*** 查询方法** param ms            映射器语句* param parameter     参数* param rowBounds     分页记录限制* param resultHandler 结果处理器* param boundSql      SQL对象* param E           返回的类型* return ListE* throws SQLException SQL异常*/protected abstract E ListE doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;...
}3.4 键值生成器 
键值生成器 KeyGenerator 接口和对应的实现类用于包装对 Mapper XML insert 标签中 selectKey 下语句的处理。这个接口有3个实现类。 NoKeyGenerator默认空实现不对主键单独处理。Jdbc3keyGenerator主要用于数据库的自增主键比如 MySQL、PostgreSQL。SelectKeyGenerator主要用于数据库不支持自增主键的情况比如 Oracle、DB2  
3.4.1 键值生成器接口 KeyGenerator.java package com.lino.mybatis.executor.keygen;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.MappedStatement;
import java.sql.Statement;/*** description: 键值生成器接口*/
public interface KeyGenerator {/*** 针对Sequence主键而言在执行insert sql前必须指定一个主键值给要插入的记录* 如Oracle、DB2KeyGenerator提供了processBefore()方法。** param executor  执行器* param ms        映射器语句类* param stmt      语句类* param parameter 参数*/void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);/*** 针对自增主键主键的表在插入时不需要主键而是在插入过程自动获取一个自增的主键* 比如MySQL、PostgreSQLKeyGenerator提供了processAfter()方法** param executor  执行器* param ms        映射器语句类* param stmt      语句类* param parameter 参数*/void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
}在键值生成器接口中定义了2个方法processBefore、processAfter processBefore针对 Sequence 主键而言在执行 insert sql 前必须指定一个主键值给要插入的记录。 比如Oracle、DB2KeyGenerator 提供了 processBefore() 方法。 processAfter针对自增主键的表在插入时不需要主键而是在插入过程自动获取一个自增的主键。 比如MySQL、PostgreSQLKeyGenerator 提供了 processAfter() 方法。   
3.4.2 不用键值生成器 NoKeyGenerator.java package com.lino.mybatis.executor.keygen;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.MappedStatement;
import java.sql.Statement;/*** description: 不用键值生成器*/
public class NoKeyGenerator implements KeyGenerator {Overridepublic void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {// Do Nothing}Overridepublic void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {// Do Nothing}
}3.4.3 使用JDBC3键值生成器 Jdbc3KeyGeneration.java package com.lino.mybatis.executor.keygen;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.TypeHandler;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;/*** description: 使用 JDBC3 Statement.getGeneratedKeys*/
public class Jdbc3KeyGenerator implements KeyGenerator {Overridepublic void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {// Do Nothing}Overridepublic void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {}public void processBatch(MappedStatement ms, Statement stmt, ListObject parameters) {try (ResultSet rs  stmt.getGeneratedKeys()) {final Configuration configuration  ms.getConfiguration();final TypeHandlerRegistry typeHandlerRegistry  configuration.getTypeHandlerRegistry();final String[] keyProperties  ms.getKeyProperties();final ResultSetMetaData rsmd  rs.getMetaData();TypeHandler?[] typeHandlers  null;if (keyProperties ! null  rsmd.getColumnCount()  keyProperties.length) {for (Object parameter : parameters) {// there should be one row for each statement (also one for each parameter)if (!rs.next()) {break;}final MetaObject metaParam  configuration.newMetaObject(parameter);if (typeHandlers  null) {// 先取得类型处理器typeHandlers  getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties);}// 填充键值populateKeys(rs, metaParam, keyProperties, typeHandlers);}}} catch (Exception e) {throw new RuntimeException(Error getting generated key or setting result to parameter object. Cause:   e, e);}}private TypeHandler?[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam, String[] keyProperties) {TypeHandler?[] typeHandlers  new TypeHandler?[keyProperties.length];for (int i  0; i  keyProperties.length; i) {if (metaParam.hasSetter(keyProperties[i])) {Class? keyPropertyType  metaParam.getSetterType(keyProperties[i]);TypeHandler? th  typeHandlerRegistry.getTypeHandler(keyPropertyType, null);typeHandlers[i]  th;}}return typeHandlers;}private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler?[] typeHandlers) throws SQLException {for (int i  0; i  keyProperties.length; i) {TypeHandler? th  typeHandlers[i];if (th ! null) {Object value  th.getResult(rs, i  1);metaParam.setValue(keyProperties[i], value);}}}
}3.4.4 不自增键值生成器 SelectKeyGeneration.java package com.lino.mybatis.executor.keygen;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.RowBounds;
import java.sql.Statement;
import java.util.List;/*** description: 键值生成器*/
public class SelectKeyGenerator implements KeyGenerator {public static final String SELECT_KEY_SUFFIX  !selectKey;private boolean executeBefore;private MappedStatement keyStatement;public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {this.executeBefore  executeBefore;this.keyStatement  keyStatement;}Overridepublic void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {if (executeBefore) {processGeneratedKeys(executor, ms, parameter);}}Overridepublic void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {if (!executeBefore) {processGeneratedKeys(executor, ms, parameter);}}private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {try {if (parameter ! null  keyStatement ! null  keyStatement.getKeyProperties() ! null) {String[] keyProperties  keyStatement.getKeyProperties();final Configuration configuration  ms.getConfiguration();final MetaObject metaParam  configuration.newMetaObject(parameter);if (keyProperties ! null) {Executor keyExecutor  configuration.newExecutor(executor.getTransaction());ListObject values  keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);if (values.size()  0) {throw new RuntimeException(SelectKey returned no data.);} else if (values.size()  1) {throw new RuntimeException(SelectKey returned more than one value.);} else {MetaObject metaResult  configuration.newMetaObject(values.get(0));if (keyProperties.length  1) {if (metaResult.hasGetter(keyProperties[0])) {setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));} else {setValue(metaParam, keyProperties[0], values.get(0));}} else {handleMultipleProperties(keyProperties, metaParam, metaResult);}}}}} catch (Exception e) {throw new RuntimeException(Error selecting key or setting result to parameter object. Cause:   e);}}private void handleMultipleProperties(String[] keyProperties, MetaObject metaParam, MetaObject metaResult) {String[] keyColumns  keyStatement.getKeyColumns();if (keyColumns  null || keyColumns.length  0) {for (String keyProperty : keyProperties) {setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));}} else {if (keyColumns.length ! keyProperties.length) {throw new RuntimeException(If SelectKey has key columns, the number must match the number of key properties.);}for (int i  0; i  keyProperties.length; i) {setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));}}}private void setValue(MetaObject metaParam, String property, Object value) {if (metaParam.hasSetter(property)) {metaParam.setValue(property, value);} else {throw new RuntimeException(No setter found for the keyProperty   property   in   metaParam.getOriginalObject().getClass().getName()  .);}}
}SelectKeyGenerator 核心实现主要体现在 processAfter 方法对 processGeneratorKeys 的调用处理。 这个方法的调用过程中通过从配置项中获取 JDBC 链接和 Executor 执行器。之后使用执行器对传入进来的 MappedStatement 执行处理也就是对应的 keyStatement 参数。 和执行 select 语句一样在通过执行器 keyExecutor.query 获取到结果以后使用 MetaObject 反射工具类向对象属性设置查询结果。 这个封装的结果就是封装到了入参对象中对应的字段上比如用户对象的 id 字段。  
3.5 配置和映射中添加键值生成器 
3.5.1 映射器语句类 MappedStatement.java package com.lino.mybatis.mapping;import com.lino.mybatis.executor.keygen.Jdbc3KeyGenerator;
import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.executor.keygen.NoKeyGenerator;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import java.util.Collections;
import java.util.List;/*** description: 映射器语句类*/
public class MappedStatement {private String resource;private Configuration configuration;private String id;private SqlCommandType sqlCommandType;private SqlSource sqlSource;Class? resultType;private LanguageDriver lang;private ListResultMap resultMaps;private KeyGenerator keyGenerator;private String[] keyProperties;private String[] keyColumns;public MappedStatement() {}/*** 获取SQL对象** param parameterObject 参数* return SQL对象*/public BoundSql getBoundSql(Object parameterObject) {// 调用 SqlSource#getBoundSqlreturn sqlSource.getBoundSql(parameterObject);}public static class Builder {private MappedStatement mappedStatement  new MappedStatement();public Builder(Configuration configuration, String id, SqlCommandType sqlCommandType, SqlSource sqlSource, Class? resultType) {mappedStatement.configuration  configuration;mappedStatement.id  id;mappedStatement.sqlCommandType  sqlCommandType;mappedStatement.sqlSource  sqlSource;mappedStatement.resultType  resultType;mappedStatement.keyGenerator  configuration.isUseGeneratedKeys() SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();mappedStatement.lang  configuration.getDefaultScriptingLanguageInstance();}public MappedStatement build() {assert mappedStatement.configuration ! null;assert mappedStatement.id ! null;mappedStatement.resultMaps  Collections.unmodifiableList(mappedStatement.resultMaps);return mappedStatement;}public Builder resource(String resource) {mappedStatement.resource  resource;return this;}public String id() {return mappedStatement.id;}public Builder resultMaps(ListResultMap resultMaps) {mappedStatement.resultMaps  resultMaps;return this;}public Builder keyGenerator(KeyGenerator keyGenerator) {mappedStatement.keyGenerator  keyGenerator;return this;}public Builder keyProperty(String keyProperty) {mappedStatement.keyProperties  delimitedStringToArray(keyProperty);return this;}}private static String[] delimitedStringToArray(String in) {if (in  null || in.trim().length()  0) {return null;} else {return in.split(,);}}public Configuration getConfiguration() {return configuration;}public String getId() {return id;}public SqlCommandType getSqlCommandType() {return sqlCommandType;}public SqlSource getSqlSource() {return sqlSource;}public Class? getResultType() {return resultType;}public LanguageDriver getLang() {return lang;}public ListResultMap getResultMaps() {return resultMaps;}public String[] getKeyProperties() {return keyProperties;}public KeyGenerator getKeyGenerator() {return keyGenerator;}public String getResource() {return resource;}public String[] getKeyColumns() {return keyColumns;}
}添加 keyGenerator、keyProperties、keyColumns 键值生成器 
3.5.2 配置项 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.keygen.KeyGenerator;
import com.lino.mybatis.executor.parameter.ParameterHandler;
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.mapping.ResultMap;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.DefaultObjectFactory;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.lino.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.scripting.LanguageDriverRegistry;
import com.lino.mybatis.scripting.xmltags.XMLLanguageDriver;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;/*** description: 配置项*/
public class Configuration {/*** 环境*/protected Environment environment;/*** 是否使用自动生成键值对*/protected boolean useGeneratedKeys  false;/*** 映射注册机*/protected MapperRegistry mapperRegistry  new MapperRegistry(this);/*** 映射的语句存在Map里*/protected final MapString, MappedStatement mappedStatements  new HashMap(16);/*** 结果映射存在Map里*/protected final MapString, ResultMap resultMaps  new HashMap(16);/*** 键值生成器存在Map里*/protected final MapString, KeyGenerator keyGenerators  new HashMap(16);...public boolean isUseGeneratedKeys() {return useGeneratedKeys;}public void setUseGeneratedKeys(boolean useGeneratedKeys) {this.useGeneratedKeys  useGeneratedKeys;}...public void addResultMap(ResultMap resultMap) {resultMaps.put(resultMap.getId(), resultMap);}public void addKeyGenerator(String id, KeyGenerator keyGenerator) {keyGenerators.put(id, keyGenerator);}public KeyGenerator getKeyGenerator(String id) {return keyGenerators.get(id);}public boolean hasKeyGenerators(String id) {return keyGenerators.containsKey(id);}
}添加 keyGenerators 键值生成器列表 
3.5.3 默认Map结果处理器 DefaultResultSetHandler.java package com.lino.mybatis.executor.resultset;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.result.DefaultResultContext;
import com.lino.mybatis.executor.result.DefaultResultHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.mapping.ResultMapping;
import com.lino.mybatis.reflection.MetaClass;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import com.lino.mybatis.type.TypeHandler;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;/*** description: 默认Map结果处理器*/
public class DefaultResultSetHandler implements ResultSetHandler {...private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ListClass? constructorArgTypes, ListObject constructorArgs, String columnPrefix) throws SQLException {final Class? resultType  resultMap.getType();final MetaClass metaType  MetaClass.forClass(resultType);if (typeHandlerRegistry.hasTypeHandler(resultType)) {// 基本类型return createPrimitiveResultObject(rsw, resultMap, columnPrefix);} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {// 普通的Bean对象类型return objectFactory.create(resultType);}throw new RuntimeException(Do not know how to create an instance of   resultType);}private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {final Class? resultType  resultMap.getType();final String columnName;if (!resultMap.getResultMappings().isEmpty()) {final ListResultMapping resultMappingList  resultMap.getResultMappings();final ResultMapping mapping  resultMappingList.get(0);columnName  prependPrefix(mapping.getColumn(), columnPrefix);} else {columnName  rsw.getColumnNames().get(0);}final TypeHandler? typeHandler  rsw.getTypeHandler(resultType, columnName);return typeHandler.getResult(rsw.getResultSet(), columnName);}private String prependPrefix(String columnName, String prefix) {if (columnName  null || columnName.length()  0 || prefix  null || prefix.length()  0) {return columnName;}return prefix  columnName;}...
}3.6 解析selectkey 
selectKey 标签主要用在 Mapper XML 中的 insert 语句里所以我们主要是对 XMLStatementBuilder XML 语句构建器的解析过程进行扩展。 
3.6.1 修改语句处理器抽象基类 BaseStatementHandler.java package com.lino.mybatis.executor.statement;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.executor.parameter.ParameterHandler;
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 com.lino.mybatis.session.RowBounds;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;/*** description: 语句处理器抽象基类* author: lingjian* createDate: 2022/11/8 13:53*/
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 final ParameterHandler parameterHandler;protected final RowBounds rowBounds;protected BoundSql boundSql;public 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;// 新增判断因为update不会传入boundSql参数这里做初始化处理if (boundSql  null) {generateKeys(parameterObject);boundSql  mappedStatement.getBoundSql(parameterObject);}this.boundSql  boundSql;this.parameterObject  parameterObject;this.parameterHandler  configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);this.resultSetHandler  configuration.newResultSetHandler(executor, mappedStatement, rowBounds, resultHandler, boundSql);}Overridepublic Statement prepare(Connection connection) {Statement statement  null;try {// 实例化 Statementstatement  instantiateStatement(connection);// 参数设置可以被抽取提供配置statement.setQueryTimeout(350);statement.setFetchSize(10000);return statement;} catch (Exception e) {throw new RuntimeException(Error prepare statement. Cause:   e, e);}}/*** 初始化语句** param connection 连接* return 语句* throws SQLException SQL异常*/protected abstract Statement instantiateStatement(Connection connection) throws SQLException;/*** 生成键值对** param parameter 参数*/protected void generateKeys(Object parameter) {KeyGenerator keyGenerator  mappedStatement.getKeyGenerator();keyGenerator.processBefore(executor, mappedStatement, null, parameter);}
}3.6.2 预处理语句处理器 
StatementHandler 语句处理器接口所定义的方法在 SQL 执行上只有 update 和 query。所以我们要扩展的 insert 操作也是对 update 方法的扩展操作处理。 PreparedStatementHandler.java package com.lino.mybatis.executor.statement;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;/*** description: 预处理语句处理器PREPARED*/
public class PreparedStatementHandler extends BaseStatementHandler {public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject,RowBounds rowBounds, ResultHandler resultSetHandler, BoundSql boundSql) {super(executor, mappedStatement, parameterObject, rowBounds, resultSetHandler, boundSql);}Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {String sql  boundSql.getSql();return connection.prepareStatement(sql);}Overridepublic void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);}Overridepublic int update(Statement statement) throws SQLException {PreparedStatement ps  (PreparedStatement) statement;ps.execute();int rows  ps.getUpdateCount();Object parameterObject  boundSql.getParameterObject();KeyGenerator keyGenerator  mappedStatement.getKeyGenerator();keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);return rows;}Overridepublic E ListE query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps  (PreparedStatement) statement;ps.execute();return resultSetHandler.handleResultSets(ps);}
}在 update 方法中扩展对 selectKey 标签中语句的处理。其实这部分内容就是获取 MapperStatement 映射语句类中 KeyGenerator之后调用 KeyGenerator#processAfter 方法。这部分的调用就是前面对 SelectKeyGenerator#processGeneratorKeys 键值生成器方法的调用讲解。 
3.6.3 注解配置构建器 MapperAnnotationBuilder.java package com.lino.mybatis.builder.annotation;import com.lino.mybatis.annotations.Delete;
import com.lino.mybatis.annotations.Insert;
import com.lino.mybatis.annotations.Select;
import com.lino.mybatis.annotations.Update;
import com.lino.mybatis.binding.MapperMethod;
import com.lino.mybatis.builder.MapperBuilderAssistant;
import com.lino.mybatis.executor.keygen.Jdbc3KeyGenerator;
import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.executor.keygen.NoKeyGenerator;
import com.lino.mybatis.mapping.SqlCommandType;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;/*** description: 注解配置构建器 Mapper*/
public class MapperAnnotationBuilder {private final SetClass? extends Annotation sqlAnnotationTypes  new HashSet();private Configuration configuration;private MapperBuilderAssistant assistant;private Class? type;public MapperAnnotationBuilder(Configuration configuration, Class? type) {String resource  type.getName().replace(., /)  .java (best guess);this.assistant  new MapperBuilderAssistant(configuration, resource);this.configuration  configuration;this.type  type;sqlAnnotationTypes.add(Select.class);sqlAnnotationTypes.add(Insert.class);sqlAnnotationTypes.add(Update.class);sqlAnnotationTypes.add(Delete.class);}public void parse() {String resource  type.toString();if (!configuration.isResourceLoaded(resource)) {assistant.setCurrentNamespace(type.getName());Method[] methods  type.getMethods();for (Method method : methods) {if (!method.isBridge()) {// 解析语句parseStatement(method);}}}}/*** 解析语句** param method 方法*/private void parseStatement(Method method) {Class? parameterTypeClass  getParameterType(method);LanguageDriver languageDriver  getLanguageDriver(method);SqlSource sqlSource  getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);if (sqlSource ! null) {final String mappedStatementId  type.getName()  .  method.getName();SqlCommandType sqlCommandType  getSqlCommandType(method);KeyGenerator keyGenerator;String keyProperty  id;if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {keyGenerator  configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();} else {keyGenerator  new NoKeyGenerator();}boolean isSelect  sqlCommandType  SqlCommandType.SELECT;String resultMapId  null;if (isSelect) {resultMapId  parseResultMap(method);}// 调用助手类assistant.addMappedStatement(mappedStatementId,sqlSource,sqlCommandType,parameterTypeClass,resultMapId,getReturnType(method),keyGenerator,keyProperty,languageDriver);}}...}3.6.4 映射构建器助手 MapperBuilderAssistant.java package com.lino.mybatis.builder;import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.mapping.*;
import com.lino.mybatis.reflection.MetaClass;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.TypeHandler;
import java.util.ArrayList;
import java.util.List;/*** description: 映射构建器助手建造者*/
public class MapperBuilderAssistant extends BaseBuilder {private String currentNamespace;private String resource;public MapperBuilderAssistant(Configuration configuration, String resource) {super(configuration);this.resource  resource;}public ResultMapping buildResultMapping(Class? resultType, String property, String column, ListResultFlag flags) {Class? javaTypeClass  resolveResultJavaType(resultType, property, null);TypeHandler? typeHandlerInstance  resolveTypeHandler(javaTypeClass, null);ResultMapping.Builder builder  new ResultMapping.Builder(configuration, property, column, javaTypeClass);builder.typeHandler(typeHandlerInstance);builder.flags(flags);return builder.build();}private Class? resolveResultJavaType(Class? resultType, String property, Class? javaType) {if (javaType  null  property ! null) {try {MetaClass metaResultType  MetaClass.forClass(resultType);javaType  metaResultType.getSetterType(property);} catch (Exception ignore) {}}if (javaType  null) {javaType  Object.class;}return javaType;}public String getCurrentNamespace() {return currentNamespace;}public void setCurrentNamespace(String currentNamespace) {this.currentNamespace  currentNamespace;}public String applyCurrentNamespace(String base, boolean isReference) {if (base  null) {return null;}if (isReference) {if (base.contains(.)) {return base;}} else {if (base.startsWith(currentNamespace  .)) {return base;}if (base.contains(.)) {throw new RuntimeException(Dots are not allowed in element names, please remove it from   base);}}return currentNamespace  .  base;}/*** 添加映射器语句*/public MappedStatement addMappedStatement(String id, SqlSource sqlSource, SqlCommandType sqlCommandType,Class? parameterType, String resultMap, Class? resultType,KeyGenerator keyGenerator, String keyProperty, LanguageDriver lang) {// 给id加上namespace前缀com.lino.mybatis.test.dao.IUserDao.queryUserInfoByIdid  applyCurrentNamespace(id, false);MappedStatement.Builder statementBuilder  new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);statementBuilder.resource(resource);statementBuilder.keyGenerator(keyGenerator);statementBuilder.keyProperty(keyProperty);// 结果映射 给 MappedStatement#resultMapssetStatementResultMap(resultMap, resultType, statementBuilder);MappedStatement statement  statementBuilder.build();// 映射语句信息建造完存放到配置项中configuration.addMappedStatement(statement);return statement;}private void setStatementResultMap(String resultMap, Class? resultType, MappedStatement.Builder statementBuilder) {// 因为暂时还没有在 Mapper XML 中配置 Map 返回结果所以这里返回的是 nullresultMap  applyCurrentNamespace(resultMap, true);ListResultMap resultMaps  new ArrayList();if (resultMap ! null) {String[] resultMapNames  resultMap.split(,);for (String resultMapName : resultMapNames) {resultMaps.add(configuration.getResultMap(resultMapName.trim()));}}/** 通常使用 resultType 即可满足大部分场景* select idqueryUserInfoById resultTypecom.lino.mybatis.test.po.User* 使用 resultType 的情况下Mybatis 会自动创建一个 ResultMap基于属性名称映射列到 JavaBean 的属性上。*/else if (resultType ! null) {ResultMap.Builder inlineResultMapBuilder  new ResultMap.Builder(configuration, statementBuilder.id()  -Inline, resultType, new ArrayList());resultMaps.add(inlineResultMapBuilder.build());}statementBuilder.resultMaps(resultMaps);}public ResultMap addResultMap(String id, Class? type, ListResultMapping resultMappings) {// 补全ID全路径如com.lino.mybatis.test.dao.IActivityDao  activityMapid  applyCurrentNamespace(id, false);ResultMap.Builder inlineResultMapBuilder  new ResultMap.Builder(configuration, id, type, resultMappings);ResultMap resultMap  inlineResultMapBuilder.build();configuration.addResultMap(resultMap);return resultMap;}
}3.6.5 XML语句构建器 XMLStatementBuilder.java package com.lino.mybatis.builder.xml;import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.builder.MapperBuilderAssistant;
import com.lino.mybatis.executor.keygen.Jdbc3KeyGenerator;
import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.executor.keygen.NoKeyGenerator;
import com.lino.mybatis.executor.keygen.SelectKeyGenerator;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.SqlCommandType;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Element;
import java.util.List;
import java.util.Locale;/*** description: XML语言构建器*/
public class XMLStatementBuilder extends BaseBuilder {private MapperBuilderAssistant builderAssistant;private Element element;public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, Element element) {super(configuration);this.builderAssistant  builderAssistant;this.element  element;}/*** 解析语句(select|insert|update|delete)* select* idselectPerson* parameterTypeint* parameterMapdeprecated* resultTypehashmap* resultMappersonResultMap* flushCachefalse* useCachetrue* timeout10000* fetchSize256* statementTypePREPARED* resultSetTypeFORWARD_ONLY* SELECT * FROM PERSON WHERE ID  #{id}* /select*/public void parseStatementNode() {String id  element.attributeValue(id);// 参数类型String parameterType  element.attributeValue(parameterType);Class? parameterTypeClass  resolveAlias(parameterType);// 外部应用 resultMapString resultMap  element.attributeValue(resultMap);// 结果类型String resultType  element.attributeValue(resultType);Class? resultTypeClass  resolveAlias(resultType);// 获取命令类型(select|insert|update|delete)String nodeName  element.getName();SqlCommandType sqlCommandType  SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));// 获取默认语言驱动器Class? langClass  configuration.getLanguageRegistry().getDefaultDriverClass();LanguageDriver langDriver  configuration.getLanguageRegistry().getDriver(langClass);// 解析selectKeyprocessSelectKeyNodes(id, parameterTypeClass, langDriver);// 解析成SqlSourceDynamicSqlSource/RawSqlSourceSqlSource sqlSource  langDriver.createSqlSource(configuration, element, parameterTypeClass);// 属性标记【仅对insert有用】MyBatis 会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值String keyProperty  element.attributeValue(keyProperty);KeyGenerator keyGenerator  null;String keyStatementId  id  SelectKeyGenerator.SELECT_KEY_SUFFIX;keyStatementId  builderAssistant.applyCurrentNamespace(keyStatementId, true);if (configuration.hasKeyGenerators(keyStatementId)) {keyGenerator  configuration.getKeyGenerator(keyStatementId);} else {keyGenerator  configuration.isUseGeneratedKeys()  SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() :new NoKeyGenerator();}// 调用助手类builderAssistant.addMappedStatement(id,sqlSource,sqlCommandType,parameterTypeClass,resultMap,resultTypeClass,keyGenerator,keyProperty,langDriver);}private void processSelectKeyNodes(String id, Class? parameterTypeClass, LanguageDriver langDriver) {ListElement selectKeyNodes  element.elements(selectKey);parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver);}private void parseSelectKeyNodes(String parentId, ListElement list, Class? parameterTypeClass, LanguageDriver languageDriver) {for (Element nodeToHandle : list) {String id  parentId  SelectKeyGenerator.SELECT_KEY_SUFFIX;parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, languageDriver);}}/*** selectKey keyPropertyid orderAFTER resultTypelong* SELECT LAST_INSERT_ID()* /selectKey*/private void parseSelectKeyNode(String id, Element nodeToHandle, Class? parameterTypeClass, LanguageDriver langDriver) {String resultType  nodeToHandle.attributeValue(resultType);Class? resultTypeClass  resolveClass(resultType);boolean executeBefore  BEFORE.equals(nodeToHandle.attributeValue(order, AFTER));String keyProperty  nodeToHandle.attributeValue(keyProperty);// 默认String resultMap  null;KeyGenerator keyGenerator  new NoKeyGenerator();// 解析成SqlSourceDynamicSqlSource/RawSqlSourceSqlSource sqlSource  langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);SqlCommandType sqlCommandType  SqlCommandType.SELECT;// 调用助手类builderAssistant.addMappedStatement(id,sqlSource,sqlCommandType,parameterTypeClass,resultMap,resultTypeClass,keyGenerator,keyProperty,langDriver);// 给id加上namespace前缀id  builderAssistant.applyCurrentNamespace(id, false);// 存放键值生成器配置MappedStatement keyStatement  configuration.getMappedStatement(id);configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));}
}通过 parseStatementNode 解析 insert/delete/update/select 标签方法扩展对 selectkey 标签的处理。processSelectKeyNodes 方法是专门用于处理 selectKey 标签下的语句。另外是对 keyProperty 的初识操作。 因为很多时候对 SQL 的解析里面并没有 selectKey 以及获取自增主键结果的返回处理。那么这个时候会采用默认的 keyGenerator 获取处理通常都会是实例化 NoKeyGenerator 赋值。  
selectKey keyPropertyid orderAFTER resultTypelongSELECT LAST_INSERT_ID()
/selectKey在 processSelectKeyNode 中先进行对 selectKey 标签上resultType、keyProperty 等属性的解析之后解析 SQL 封装成 SqlSession最后阶段是对解析信息的保存处理。 分别存放成 MappedStatement 映射器语句、SelectKeyGenerator 键值生成器。  
3.7 JDBC类型和类型处理器注册机修改 
3.7.1 JDBC类型 JdbcType.java package com.lino.mybatis.type;import java.sql.Types;
import java.util.HashMap;
import java.util.Map;/*** description: JDBC枚举类型*/
public enum JdbcType {// JDBC枚举类型INTEGER(Types.INTEGER),BIGINT(Types.BIGINT),FLOAT(Types.FLOAT),DOUBLE(Types.DOUBLE),DECIMAL(Types.DECIMAL),VARCHAR(Types.VARCHAR),CHAR(Types.CHAR),TIMESTAMP(Types.TIMESTAMP);public final int TYPE_CODE;private static MapInteger, JdbcType codeLookup  new HashMap();static {for (JdbcType type : JdbcType.values()) {codeLookup.put(type.TYPE_CODE, type);}}JdbcType(int code) {this.TYPE_CODE  code;}public static JdbcType forCode(int code) {return codeLookup.get(code);}
}添加 BIGINT 类型 
3.7.2 类型处理器注册机 TypeHandlerRegistry.java package com.lino.mybatis.type;import java.lang.reflect.Type;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;/*** description: 类型处理器注册机*/
public final class TypeHandlerRegistry {private final MapJdbcType, TypeHandler? JDBC_TYPE_HANDLER_MAP  new EnumMap(JdbcType.class);private final MapType, MapJdbcType, TypeHandler? TYPE_HANDLER_MAP  new HashMap(16);private final MapClass?, TypeHandler? ALL_TYPE_HANDLER_MAP  new HashMap(16);public TypeHandlerRegistry() {register(Long.class, new LongTypeHandler());register(long.class, new LongTypeHandler());register(Integer.class, new IntegerTypeHandler());register(int.class, new IntegerTypeHandler());register(String.class, new StringTypeHandler());register(String.class, JdbcType.CHAR, new StringTypeHandler());register(String.class, JdbcType.VARCHAR, new StringTypeHandler());register(Date.class, new DateTypeHandler());}private T void register(Type javaType, TypeHandler? extends T typeHandler) {register(javaType, null, typeHandler);}public void register(JdbcType jdbcType, TypeHandler? typeHandler) {JDBC_TYPE_HANDLER_MAP.put(jdbcType, typeHandler);}private void register(Type javaType, JdbcType jdbcType, TypeHandler? handler) {if (null ! javaType) {MapJdbcType, TypeHandler? map  TYPE_HANDLER_MAP.computeIfAbsent(javaType, k - new HashMap(16));map.put(jdbcType, handler);}ALL_TYPE_HANDLER_MAP.put(handler.getClass(), handler);}SuppressWarnings(unchecked)public TypeHandler? getTypeHandler(Class? type, JdbcType jdbcType) {return getTypeHandler((Type) type, jdbcType);}public boolean hasTypeHandler(Class? javaType) {return hasTypeHandler(javaType, null);}public boolean hasTypeHandler(Class? javaType, JdbcType jdbcType) {return javaType ! null  getTypeHandler((Type) javaType, jdbcType) ! null;}private T TypeHandlerT getTypeHandler(Type type, JdbcType jdbcType) {MapJdbcType, TypeHandler? jdbcHandlerMap  TYPE_HANDLER_MAP.get(type);TypeHandler? handler  null;if (jdbcHandlerMap ! null) {handler  jdbcHandlerMap.get(jdbcType);if (handler  null) {handler  jdbcHandlerMap.get(null);}}// type driver generics herereturn (TypeHandlerT) handler;}public TypeHandler? getMappingTypeHandler(Class? extends TypeHandler? handlerType) {return ALL_TYPE_HANDLER_MAP.get(handlerType);}
}添加 registry 注册方法。 
3.8 JDBC链接获取 
由于是在同一个操作下处理两条 SQL分别是插入和返回索引。那么这两条 SQL 其实要在同一个链接下才能正确的获取到结果也就是保证了一个事务的特性。 JdbcTransaction.java package com.lino.mybatis.transaction.jdbc;import com.lino.mybatis.session.TransactionIsolationLevel;
import com.lino.mybatis.transaction.Transaction;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;/*** description: JDBC 事务直接利用 JDBC 的commit、rollback。依赖于数据源获得的连接管理事务范围*/
public class JdbcTransaction implements Transaction {protected Connection connection;protected DataSource dataSource;protected TransactionIsolationLevel level  TransactionIsolationLevel.NONE;protected boolean autoCommit;public JdbcTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {this.dataSource  dataSource;this.level  level;this.autoCommit  autoCommit;}public JdbcTransaction(Connection connection) {this.connection  connection;}Overridepublic Connection getConnection() throws SQLException {if (null ! connection) {return connection;}connection  dataSource.getConnection();connection.setTransactionIsolation(level.getLevel());connection.setAutoCommit(autoCommit);return connection;}Overridepublic void commit() throws SQLException {if (connection ! null  !connection.getAutoCommit()) {connection.commit();}}Overridepublic void rollback() throws SQLException {if (connection ! null  !connection.getAutoCommit()) {connection.rollback();}}Overridepublic void close() throws SQLException {if (connection ! null  !connection.getAutoCommit()) {connection.close();}}
}也就是 JdbcTransaction#getConnection 方法。 在前面我们实现时只是一个 dataSource.getConnection 获取链接。这样就相当于每次获得的链接都是一个新的连接。那么两条 SQL 的执行分别在各自的 JDBC 连接下则不会正确的返回插入后的索引值。 所以我们这里进行判断如果连接不为空则不在创建新的 JDBC 连接使用当前连接即可。 
四、测试Insert自增索引值 
4.1 测试环境配置 
4.1.1 修改DAO持久层 IActivityDao.java package com.lino.mybatis.test.dao;import com.lino.mybatis.test.po.Activity;/*** description: 活动持久层*/
public interface IActivityDao {/*** 根据活动ID查询活动** param activityId 活动ID* return 活动对象*/Activity queryActivityById(Long activityId);/*** 新增** param activity 活动类* return Integer*/Integer insert(Activity activity);
}4.1.2 修改mapper配置文件 Activity_Mapper.xml ?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.lino.mybatis.test.dao.IActivityDaoresultMap idactivityMap typecom.lino.mybatis.test.po.Activityid columnid propertyid/result columnactivity_id propertyactivityId/result columnactivity_name propertyactivityName/result columnactivity_desc propertyactivityDesc/result columncreate_time propertycreateTime/result columnupdate_time propertyupdateTime//resultMapselect idqueryActivityById parameterTypejava.lang.Long resultMapactivityMapSELECT activity_id, activity_name, activity_desc, create_time, update_timeFROM activityWHERE activity_id  #{activityId}/selectinsert idinsert parameterTypecom.lino.mybatis.test.po.ActivityINSERT INTO activity(activity_id, activity_name, activity_desc, create_time, update_time)VALUES (#{activityId}, #{activityName}, #{activityDesc}, now(), now())selectKey keyPropertyid orderAFTER resultTypelongSELECT LAST_INSERT_ID()/selectKey/insert
/mapper在 insert 标签下添加 selectKey 标签并使用 SELECT LAST_INSERT_ID() 查询方法返回自增索引值。 
4.2 单元测试 
4.2.1 单元测试 ApiTest.java Test
public void test_insert() {// 1.获取映射器对象IActivityDao dao  sqlSession.getMapper(IActivityDao.class);Activity activity  new Activity();activity.setActivityId(10004L);activity.setActivityName(测试活动);activity.setActivityDesc(测试数据插入);activity.setCreator(xiaolingge);// 2.测试验证Integer result  dao.insert(activity);logger.info(测试结果count: {} index: {}, result, JSON.toJSONString(activity.getId()));sqlSession.commit();
}测试结果 14:53:54.803 [main] INFO  c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value10004
14:53:54.803 [main] INFO  c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value测试活动
14:53:54.803 [main] INFO  c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value测试数据插入
14:53:54.809 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果count: 1 index: 129通过测试结果 index: 129 可以看到我们已经可以在插入数据后返回数据库自增字段的结果。 
4.2.2 插入查询测试 ApiTest.java Test
public void test_insert_select() throws IOException {// 解析 XMLReader reader  Resources.getResourceAsReader(mybatis-config-datasource.xml);XMLConfigBuilder xmlConfigBuilder  new XMLConfigBuilder(reader);Configuration configuration  xmlConfigBuilder.parse();// 获取 DefaultSqlSessionfinal Environment environment  configuration.getEnvironment();TransactionFactory transactionFactory  environment.getTransactionFactory();Transaction tx  transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);// 创建执行器final Executor executor  configuration.newExecutor(tx);SqlSession sqlSession  new DefaultSqlSession(configuration, executor);// 执行查询默认是一个集合参数Activity activity  new Activity();activity.setActivityId(10005L);activity.setActivityName(测试活动);activity.setActivityDesc(测试数据插入);activity.setCreator(xiaolingge);int result  sqlSession.insert(com.lino.mybatis.test.dao.IActivityDao.insert, activity);Object obj  sqlSession.selectOne(com.lino.mybatis.test.dao.IActivityDao.insert!selectKey);logger.info(测试结果count: {} index: {}, result, JSON.toJSONString(obj));sqlSession.commit();
}测试结果 14:57:10.348 [main] INFO  c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value10005
14:57:10.349 [main] INFO  c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value测试活动
14:57:10.349 [main] INFO  c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value测试数据插入
14:57:10.364 [main] INFO  c.l.m.s.defaults.DefaultSqlSession - 执行查询 statementcom.lino.mybatis.test.dao.IActivityDao.insert!selectKey parameternull
14:57:10.365 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果count: 1 index: 7从测试结果看记录正常插入到数据库并返回 index7测试结果通过 
五、总结Insert自增索引值 
在原有的 Mapper XML 对各类标签语句的解析中对 insert 操作进行扩展添加新的标签 selectKey 并通过这样一个标签的解析、执行、封装处理把最终的插入索引结果返回到入参对象的对应属性字段上。 那么同时我们所处理的是类似 mysql 这样带有自增索引的数据库用这样的方式来串联整个流程。 注意本章节是首次在一个操作中执行2条 SQL 语句为了能让最后可以查询到自增索引那么这两天 SQL 必须是在同一个链接下。