Albrus learn how MyBatis works…φ(๑˃∀˂๑)♪
@Before
public void getConnection() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
}
@Test
public void getGames() {
GameMapper mapper = sqlSession.getMapper(GameMapper.class);
Game entity = mapper.get(1);
System.out.println("ID: " + entity);
List<Game> games1 = mapper.list();
System.out.println("Count: " + games1.size());
List<Game> games2 = sqlSession.selectList("com.deemo.hard.mapper.GameMapper.list");
System.out.println("Count: " + games2.size());
Game game = new Game();
game.setName("Deemo");
game.setPrice(32.0);
game.setDescription("");
Integer insert = mapper.insert(game);
System.out.println("The insert id: " + insert);
}
-
org.apache.ibatis.session.SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
通过各种解析器解析配置文件(
XMLConfigBuilder、XPathParser、XMLStatementBuilder、XMLMapperBuilder...
),返回DefaultSqlSessionFactory()
对象// org.apache.ibatis.builder.xml.XMLMapperBuilder#parse public void parse() { if (!configuration.isResourceLoaded(resource)) { // 1, 2 生成 MappedStatement configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); // 3 生成 MapperProxyFactory<T> bindMapperForNamespace(); } }
-
XMLStatementBuilder
解析 接口 中的SQL语句,将 #{param} 参数替换为 ? 并生成该参数的ParameterMapping
// org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode public void parseStatementNode() { // ... // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); // ... // 下一小点的入口 builderAssistant.addMappedStatement(..., sqlSource, ...); } // org.apache.ibatis.builder.SqlSourceBuilder.ParameterMappingTokenHandler#handleToken @Override public String handleToken(String content) { parameterMappings.add(buildParameterMapping(content)); return "?"; }
-
然后为
Mapper
中的每一个 接口 创建MappedStatement
并缓存在Configuration.mappedStatements(StrictMap<String, MappedStatement>)
,key
便是 接口 的全路径,这里的StrictMap
是 MyBatis 中自己继承HashMap<String, V>
设计的一个Configuration
内部类,其会在put
元素时,截取全路径的最后一个.
后的字符串做二次缓存(即接口的名称) -
为每个
Mapper
创建一个MapperProxyFactory<T>
代理工厂对象缓存在Configuration.mapperRegistry.knownMappers(Map<Class<?>, MapperProxyFactory<?>>)
中
-
-
org.apache.ibatis.session.SqlSession sqlSession = sqlSessionFactory.openSession();
返回
DefaultSqlSession()
对象,==包含Executor
对象,四大对象之一==,如果开启了二级缓存,Executor
对象会使用CachingExecutor
包裹,默认ExecutorType = SIMPLE
,便是SimpleExecutor
,还会使用拦截器链增强// org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { // ... final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); // ... } // org.apache.ibatis.session.Configuration#newExecutor public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { // 默认 executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } // ==注意==,四大对象之一,并调用拦截器链增强 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
-
GameMapper mapper = sqlSession.getMapper(GameMapper.class);
获取
Mapper
对象时,在knownMappers
中获取MapperProxyFactory<T>
代理工厂对象并创建代理对象MapperProxy<T>
,是代理对象的InvocationHandler
,持有sqlSession、mapperInterface、methodCache
信息public class MapperProxy<T> implements InvocationHandler, Serializable { // ... } public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>(); @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { // mapperProxy 便是代理对象的 InvocationHandler return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { // mapperProxy 便是代理对象的 InvocationHandler,持有 sqlSession、mapperInterface、methodCache 信息 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
-
Game entity = mapper.get(1);
通过
mapper
代理对象增删查改数据库,进入MapperProxy<T>.invoke()
方法,该方法创建并缓存MapperMethod
对象,MapperMethod
对象会解析接口方法是增删改查中的哪一种类型并通过调用持有sqlSession
对象,通过sqlSession
对象增删查改,sqlSession
底层便是通过持有的executor
对象增删查改数据库// org.apache.ibatis.binding.MapperProxy#invoke @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } // org.apache.ibatis.binding.MapperMethod#execute public Object execute(SqlSession sqlSession, Object[] args) { // ... // 参数解析器: // 没有参数返回 null // 只有一个参数直接返回 // 否则,使用 Map 包装参数(除了正常的参数名称外,还会放置 param1, param2, ...) Object param = method.convertArgsToSqlCommandParam(args); // selectOne 最终会到 selectList result = sqlSession.selectOne(command.getName(), param); // ... }
-
org.apache.ibatis.session.defaults.DefaultSqlSession#selectList
// org.apache.ibatis.session.defaults.DefaultSqlSession // #selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds) @Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { // 解析配置文件时,会为每一个 接口 创建 MappedStatement MappedStatement ms = configuration.getMappedStatement(statement); // 开启了二级缓存,便是被 CachingExecutor 包裹的 SimpleExecutor(默认 ExecutorType SIMPLE) return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
-
selectOne
最终会到selectList
-
statement
为 接口 的全路径名,从缓存中获取MappedStatement
对象,包含 接口 的详细信息 -
进入查询之前,还会包装一次参数:
wrapCollection(parameter)
,该参数包装便是处理集合类型,封装为Map
结果,使用collection、list、array
作为 Key,方便在 Mapper 中使用
-
-
return executor.query(...)
// org.apache.ibatis.executor.CachingExecutor#query @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // 1 BoundSql boundSql = ms.getBoundSql(parameterObject); // 2 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); // 3 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
-
BoundSql boundSql = ms.getBoundSql(parameterObject);
BoundSql
对象包含 SQL 的详细信息,其实就是从sqlSource
对象中获取的信息 -
创建缓存 key:
-1289266733:86971797:com.deemo.hard.mapper.GameMapper.get:0:2147483647:SELECT * FROM ``game`` WHERE id = ?:1:dev
-
进入二级缓存查询,查询缓存
// org.apache.ibatis.executor.CachingExecutor#query @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { // ... } // 被包装的 Executor,默认 SimpleExecutor return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
-
-
二级缓存中没有,进入被包装的
SimpleExecutor
中查询// org.apache.ibatis.executor.BaseExecutor#query @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; // 再从一级缓存中获取 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { // 一级缓存中存在 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { // 一级缓存中不存在 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; }
再从一级缓存中获取,获取不到则从数据库中查询
-
queryFromDatabase
// org.apache.ibatis.executor.BaseExecutor#queryFromDatabase private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; // 一级缓存占位 localCache.putObject(key, EXECUTION_PLACEHOLDER); try { // 从数据库中查询 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { // 清缓存? localCache.removeObject(key); } // 放缓存 localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
一级缓存先占位、再查询,最后存放缓存
finally
中的清缓存操作猜测是怕查询出异常时影响缓存数据 -
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
// org.apache.ibatis.executor.SimpleExecutor#doQuery @Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { // JDBC 原生对象 Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // ==注意==,四大对象之二,并调用拦截器链增强 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 创建原生 Statement stmt = prepareStatement(handler, ms.getStatementLog()); // 查询数据库 return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } }
-
==
StatementHandler
,四大对象之二,并调用拦截器链增强==// org.apache.ibatis.session.Configuration#newStatementHandler public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 创建 StatementHandler StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 调用拦截器链增强 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
-
new RoutingStatementHandler(...)
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(...); break; case PREPARED: // statementType 默认 PREPARE delegate = new PreparedStatementHandler(...); break; case CALLABLE: delegate = new CallableStatementHandler(...); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); } }
statementType
默认值为:PREPARE,因此创建PreparedStatementHandler
-
new PreparedStatementHandler(...)
public class PreparedStatementHandler extends BaseStatementHandler // org.apache.ibatis.executor.statement.PreparedStatementHandler#PreparedStatementHandler public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); } // org.apache.ibatis.executor.statement.BaseStatementHandler#BaseStatementHandler protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // ... if (boundSql == null) { // issue #435, get the key before calculating the statement generateKeys(parameterObject); boundSql = mappedStatement.getBoundSql(parameterObject); } this.boundSql = boundSql; // ==注意==,四大对象之三,并调用拦截器链增强 this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); // ==注意==,四大对象之四,并调用拦截器链增强 this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); } ============================================================================================= // org.apache.ibatis.session.Configuration#newParameterHandler // ==注意==,四大对象之三,并调用拦截器链增强 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { // 创建 ParameterHandler ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); // 调用拦截器链增强 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler; } // org.apache.ibatis.session.Configuration#newResultSetHandler // ==注意==,四大对象之四,并调用拦截器链增强 public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { // 创建 ResultSetHandler ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); // 调用拦截器链增强 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; }
==在创建
PreparedStatementHandler
对象时,会创建四大对象之三:ParameterHandler
和四大对象之四:ResultSetHandler
,并且会调用拦截器链增强,这两个对象都会被PreparedStatementHandler
对象持有==
-
-
创建原生
Statement stmt = prepareStatement(handler, ms.getStatementLog());
// org.apache.ibatis.executor.SimpleExecutor#prepareStatement private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; // 获取连接 Connection connection = getConnection(statementLog); // 创建原生 Statement stmt = handler.prepare(connection, transaction.getTimeout()); // 设置参数 handler.parameterize(stmt); return stmt; }
-
stmt = handler.prepare(connection, transaction.getTimeout());
// org.apache.ibatis.executor.statement.BaseStatementHandler#prepare @Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { Statement statement = null; try { statement = instantiateStatement(connection); // ... } catch () { } // ... } // org.apache.ibatis.executor.statement.PreparedStatementHandler#instantiateStatement @Override protected Statement instantiateStatement(Connection connection) throws SQLException { String sql = boundSql.getSql(); if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { String[] keyColumnNames = mappedStatement.getKeyColumns(); if (keyColumnNames == null) { return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); } else { return connection.prepareStatement(sql, keyColumnNames); } } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) { // 原生 JDBC 操作生成 Statement return connection.prepareStatement(sql); } else { return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } }
==在
PreparedStatementHandler#instantiateStatement
中便会通过原生 JDBC 操作生成Statement
== -
handler.parameterize(stmt);
// org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize @Override public void parameterize(Statement statement) throws SQLException { // ParameterHandler 四大对象之三 parameterHandler.setParameters((PreparedStatement) statement); } // org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters @Override public void setParameters(PreparedStatement ps) { // ... typeHandler.setParameter(ps, i + 1, value, jdbcType); }
PreparedStatementHandler
调用ParameterHandler
设置参数,ParameterHandler
解析参数后调用各类型的TypeHandler<T>
完成 JDBCPreparedStatement
参数设置
-
-
查询数据库
return handler.query(stmt, resultHandler);
// org.apache.ibatis.executor.statement.RoutingStatementHandler#query @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { return delegate.<E>query(statement, resultHandler); } // org.apache.ibatis.executor.statement.PreparedStatementHandler#query @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); // ==注意==,四大对象之四 return resultSetHandler.handleResultSets(ps); }
从
Executor.doQuery
到RoutingStatementHandler.query
到PreparedStatementHandler.query
,最终使用 JDBC 原生PreparedStatement
对象完成数据库查询,==最后使用四大对象之四ResultSetHandler
完成对数据的封装==
-
-
return resultSetHandler.handleResultSets(ps);
// org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
// ...
}
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
// ...
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
return typeHandler.getResult(rs, column);
// ...
}
处理响应数据封装的时候,也会使用到 TypeHandler<?>
获取 Java Bean 对象对应属性该类型的值
- 处理缓存、连接,流程结束
-
Executor
在
sqlSessionFactory.openSession()
时创建,被SqlSession
持有:也就是说,每创建一个SqlSession
便会创建一个Executor
被SqlSession
持有 -
StatementHandler
在通过
SqlSession
获取到MapperProxy
对象后调用接口时,最终会进入SqlSession
的查询方法,也将最终调用Executor
的doUpdate/doQuery
方法,此时便会创建StatementHandler
对象:也就是说,没调用一次接口,便会生成一个StatementHandler
对象 -
ParameterHandler
在创建
StatementHandler
对象的构造方法中被初始化创建,被StatementHandler
持有 -
ResultSetHandler
在创建
StatementHandler
对象的构造方法中被初始化创建,被StatementHandler
持有
SqlSessionTemplate:
// 开启事务后,初始化启动:
DataSourceTransactionManager.doBegin() {
// ...省略
if (txObject.isNewConnectionHolder()) {
// TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()):
TransactionSynchronizationManager.bindResource() {
// private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
// ...省略
Map<Object, Object> map = resources.get();
// set ThreadLocal Map if none found
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
// 此时actualKey为连接池对象
Object oldValue = map.put(actualKey, value);
}
}
}
// 当系统初始化启动时,会扫描mapper接口并创建 MapperProxy 对象:
// 此时会为每个mapper接口对象创建一个mapper代理对象(即 MapperProxy :每个代理对象自身包含一个 SqlSession 对象(其实是 SqlSessionTemplate 对象,该对象中包含一个 SqlSessionFactory 对象))
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
// 调用mapper接口的方法时,会携带该代理对象自带的 SqlSession 对象(其实是 SqlSessionTemplate 对象,该对象中包含一个 SqlSessionFactory 对象)
MapperMethod.execute(SqlSession, Object[]) {
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
}
// sqlSession.update(command.getName(), param),此 sqlSession 其实是 SqlSessionTemplate 对象
// TODO 此处是 sqlSessionProxy 动态代理接管,待深入学习代理后再详细写原理
// ~~断点调试时,若调试进入这一步,会自动触发调用 SqlSessionUtils.getSqlSession(SqlSessionFactory, ExecutorType, PersistenceExceptionTranslator),~~
// ~~此时无 holder, 会通过 sessionFactory 创建一个真正的 SqlSession,若开启了事务,会将 SqlSession 存放到 TransactionSynchronizationManager.resources() 中~~
SqlSessionTemplate.update(String, Object) {
// 调用真正 sqlSession代理对象
return this.sqlSessionProxy.update(statement, parameter);
}
// 接上部分
// sqlSession代理对象,内部类:
SqlSessionTemplate.SqlSessionInterceptor.invoke() {
SqlSession sqlSession = getSqlSession(
// SqlSessionTemplate。this:调用者的 SqlSessionTemplate 对象
// 开启事务时,sqlSessionFactory 后续作为 resources(ThreadLocal)的 key 获取 holder
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
}
SqlSessionUtils.getSqlSession(SqlSessionFactory, ExecutorType, PersistenceExceptionTranslator) {
// 从 resources(ThreadLocal) 中获取holder,key 为 sessionFactory
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
// 若 session 为null,new 一个 DefaultSqlSession 对象
LOGGER.debug(() -> "Creating a new SqlSession");
session = sessionFactory.openSession(executorType);
// 若开启事务,将 session 存放到 resources(ThreadLocal),key 为 sessionFactory
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
}
一级缓存: 针对同一个sqlSession
当未开启事务时、或开启事务但未执行更新操作时,一级缓存生效:
org.apache.ibatis.executor.BaseExecutor.localCache(PerpetualCache)
BaseExecutor.query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql) {
// ...省略
// 先从一级缓存中获取,key 与 mapper 相关
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 缓存中获取不到,查询数据库
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
/*List<E> BaseExecutor.queryFromDatabase(MappedStatement, Object parameter, RowBounds, ResultHandler, CacheKey key, BoundSql) {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
// 将查询数据放入缓存中
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}*/
}
}
当开启事务时:
在每次更新操作后,会清空缓存:
BaseExecutor.update(MappedStatement, Object parameter) {
// ...省略
clearLocalCache();
/*@Override
public void clearLocalCache() {
if (!closed) {
localCache.clear();
localOutputParameterCache.clear();
}
}*/
}
// org.apache.ibatis.reflection.ParamNameResolver#getNamedParams
private static final String GENERIC_NAME_PREFIX = "param";
/**
* <p>
* A single non-special parameter is returned without a name.
* Multiple parameters are named using the naming rule.
* In addition to the default names, this method also adds the generic names (param1, param2,
* ...).
* </p>
*/
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
// org.apache.ibatis.session.defaults.DefaultSqlSession#wrapCollection
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
StrictMap<Object> map = new StrictMap<>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
return map;
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<>();
map.put("array", object);
return map;
}
return object;
}
在 Jdk 1.8 之后,MyBatis 引入 useActualParamName
,此后可以直接使用参数名称:
// org.apache.ibatis.reflection.ParamNameResolver#ParamNameResolver
// @Param was not specified.
if (config.isUseActualParamName()) {
name = getActualParamName(method, paramIndex);
}
// org.apache.ibatis.reflection.ParamNameResolver#getActualParamName
private String getActualParamName(Method method, int paramIndex) {
return ParamNameUtil.getParamNames(method).get(paramIndex);
}
// org.apache.ibatis.reflection.ParamNameUtil#getParameterNames
private static List<String> getParameterNames(Executable executable) {
final List<String> names = new ArrayList<>();
// 直接获取参数名称
final Parameter[] params = executable.getParameters();
for (Parameter param : params) {
names.add(param.getName());
}
return names;
}
需要在 Mapper 中指定:
<insert id="insert" parameterType="com.deemo.hard.entity.Game" useGeneratedKeys="true" keyProperty="id">
INSERT INTO `game` (`name`, `price`, `description`) VALUE (#{name}, #{price}, #{description})
</insert>
Java 源码:
// org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator#processBatch
try (ResultSet rs = statement.getGeneratedKeys()) {...}
java.sql.Types
:
/**
* <P>The constant in the Java programming language
* that identifies the generic SQL value
* <code>NULL</code>.
*/
public final static int NULL = 0;
/**
* The constant in the Java programming language that indicates
* that the SQL type is database-specific and
* gets mapped to a Java object that can be accessed via
* the methods <code>getObject</code> and <code>setObject</code>.
*/
public final static int OTHER = 1111;
默认MyBatis将 NULL 类型映射为原生JDBC的OTHER类型,而ORACLE不支持该类型,在应用中就会出现当插入的字段为NULL时,会抛出==无效的数据类型:1111==异常。如果需要支持字段为NULL,需要修改该字段:
#{description, jdbcType=NULL}
- 全局修改:
<setting name="jdbcTypeForNull" value="NULL" />