Mybtais我想大家已经很熟悉了,毕竟算是国内最火的orm框架。Mybtais 自带双层缓存机制,其中第一层缓存是默认开启的。
我们使用 Mybatis 的时候,通常会配置 SqlSessionFactory 与 SqlSessionTemplate,这两个类在 Mybatis 中担任着非常重要的角色。
/**
* 配置 SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
Resource[] resource = new PathMatchingResourcePatternResolver().getResources("classpath:xml/*.xml");
sqlSessionFactoryBean.setMapperLocations(resource);
// 生成 SqlSessionFactory,默认实现为 DefaultSqlSessionFactory
return sqlSessionFactoryBean.getObject();
}
/**
* 配置 SqlSessionTemplate
*/
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
使用 Mybatis 时,我们只需要定义一个 Mapper(也有习惯叫Dao的)接口类,在里面定义方法,并在指定目录生成 XML 编写 sql 语句。在项目启动时,Mybatis 会为我们定义的接口类生成代理实现类。而生成代理实现类的方法是 MapperFactoryBean 里面的 getObject 方法。
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() {
}
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
protected void checkDaoConfig() {
super.checkDaoConfig();
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = this.getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception var6) {
this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
throw new IllegalArgumentException(var6);
} finally {
ErrorContext.instance().reset();
}
}
}
/**
* 此方法生成代理实现类
*/
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
public Class<T> getObjectType() {
return this.mapperInterface;
}
public boolean isSingleton() {
return true;
}
public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
}
public boolean isAddToConfig() {
return this.addToConfig;
}
}
可以看到,MapperFactoryBean 继承了 SqlSessionDaoSupport,而 getObject 方法里面的 this.getSqlSession() 则是父类中的,此方法的返回值正是 SqlSessionTemplate。
public abstract class SqlSessionDaoSupport extends DaoSupport {
/* 省略其他方法 */
private SqlSessionTemplate sqlSessionTemplate;
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
}
生成代理类的逻辑不在本文章范围内,就不说了。只需要知道生成代理类是调用的 SqlSessionTemplate 内的方法,最终执行 sql 的也是 SqlSessionTemplate 就行了。
让我们看看这个 SqlSessionTemplate 它到底是什么呢?
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
/**
* 动态生成的代理 SqlSession,执行 sql 方法全靠它
*/
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
/**
* 我们在配置 SqlSessionTemplate 类的时候调用的构造方法
*/
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
Assert.notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
// 生成代理类
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionInterceptor());
}
}
可以看到它实现了 SqlSession,要我看它其实就是 SqlSession 的装饰器。SqlSessionTemplate 内部定义了一个 sqlSessionProxy,这个 sqlSessionProxy 才是用于执行 sql 方法的东西。可以在构造方法里面看到 sqlSessionProxy 其实是一个代理类,代理对象为 SqlSessionInterceptor。
private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取 sqlSession
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
// 代理方法 -- 执行
Object result = method.invoke(sqlSession, args);
// 事务相关
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw (Throwable)unwrapped;
} finally {
// 最终需要关闭 SqlSession
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
很明显,sqlSessionProxy 在执行方法时靠得就是它。
方法的第一步需要通过 SqlSessionUtils.getSqlSession 方法获取 SqlSession,只有得到了 SqlSession 才能去实际执行 sql 语句。而 Mybatis 的第一级缓存,就是 SqlSession 级别的缓存。SqlSessionUtils.getSqlSession 方法的第一个入参是 SqlSessionTemplate 内的 sqlSessionFactory,这个 SqlSessionFactory 是我们配置的时候定义的,也就是 DefaultSqlSessionFactory。
/**
* SqlSessionUtils.getSqlSession 实现
*/
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
Assert.notNull(executorType, "No ExecutorType specified");
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
} else {
LOGGER.debug(() -> {
return "Creating a new SqlSession";
});
// 打开 sqlSession
session = sessionFactory.openSession(executorType);
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
}
让我们看一下 sessionFactory.openSession 方法。sessionFactory 类是 DefaultSqlSessionFactory。openSession 在 DefaultSqlSessionFactory 的实现中,调用的是 openSessionFromDataSource 方法。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 下面创建的 DefaultSqlSession 中,靠的就是 executor 执行 sql 方法
Executor executor = this.configuration.newExecutor(tx, execType);
// 可以看到,最终的 SqlSession 就是这个 DefaultSqlSession
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
看下 this.configuration.newExecutor 方法 ,创建 Executor 时使用的实现类是 CachingExecutor。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
// 如果没开启二级缓存,默认创建 SimpleExecutor
executor = new SimpleExecutor(this, transaction);
}
// cacheEnabled 默认是 true,所以 Executor 的实现类是 CachingExecutor
if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
SqlSessionUtils.getSqlSession 方法得到的 SqlSession,就是这个 DefaultSqlSession,而 DefaultSqlSession 中真正去执行 Sql 方法的,就是 Executor 这个类。
以 DefaultSqlSession 中的 selectList 方法为例,可以看到最终执行的其实是 executor.query。
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
List var6;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
// 实际上执行 sql 语句的方法
var6 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, handler);
} catch (Exception var10) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var10, var10);
} finally {
ErrorContext.instance().reset();
}
return var6;
}
executor 实现类是 CachingExecutor,看下 CachingExecutor 的 query 方法。
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) {
this.flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, boundSql);
List<E> list = (List)this.tcm.getObject(cache, key);
if (list == null) {
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
this.tcm.putObject(cache, key, list);
}
return list;
}
}
// 没开启二级缓存,执行此方法。
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
在默认没开启二级缓存的情况下,执行的是 this.delegate.query 方法,delegate 是构造方法里面传入的 Executor,通过上面的 newExecutor 方法可以看到这个 Executor 默认是 SimpleExecutor。
SimpleExecutor 是 BaseExecutor 的子类, SimpleExecutor并未对 BaseExecutor 内的 query 方法进行重载,所以 CachingExecutor 内 query 方法里面的 this.delegate.query ,就是 BaseExecutor 里面的 query 方法。
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
// 将本次查询方法解析成第一级缓存的key
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
// this.localCache 就是第一级缓存
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 第一级缓存中没有,去数据库查询数据
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
DeferredLoad deferredLoad = (DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
至此,我们终于见到了 第一级缓存 的芳容。
我们再看一下 this.queryFromDatabase 方法。
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
// 查询数据库,由子类 SimpleExecutor 实现
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
// 查到数据后,放入第一级缓存
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
那么第一级缓存里面的数据何时会被清空呢?答案是关闭 SqlSession,或者增删改的时候都会情况第一级缓存中的数据。
/**
* 增删改都会掉用到 BaseExecutor 内的此方法
*/
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
// 清空第一级缓存数据
this.clearLocalCache();
return this.doUpdate(ms, parameter);
}
}
在执行完 sql 查询后,SqlSessionTemplate 内的代理类的方法会调用 SqlSessionUtils.closeSqlSession 关闭 SqlSession,也会清空第一级缓存。所以使用 SqlSessionTemplate 装饰的 SqlSession时,第一级缓存是不生效的。在我们用 SqlSessionFactory 创建 SqlSession 时,第一级缓存才会生效。
@Resource
private TUserInfoMapper tUserInfoMapper;
@Resource
private SqlSessionFactory sqlSessionFactory;
@Test
void test3() {
// 此时 第一级缓存不生效,因为执行方法的是 SqlSessionTemplate 内的 代理 SqlSession
// 代理方法会在执行完 Sql 方法的时候关闭 SqlSession,顺带清空第一级缓存
TUserInfo tUserInfo = tUserInfoMapper.getById(1L);
System.out.println(tUserInfo);
tUserInfo = tUserInfoMapper.getById(1L);
System.out.println(tUserInfo);
// 使用 sqlSessionFactory 创建 SqlSession,此时 SqlSession 就是纯粹的 DefaultSqlSession
// 这个时候只有执行增删改方法,第一级缓存内的数据才会被情空
SqlSession sqlSession = sqlSessionFactory.openSession();
TUserInfoMapper mapper = sqlSession.getMapper(TUserInfoMapper.class);
TUserInfo byId = mapper.getById(1L);
byId = mapper.getById(1L);
System.out.println(byId);
sqlSession.close();
}
-- 我是 Keguans,一名生于 99 年的菜鸡研发