
DefaultSqlSession --> CacheExecutor(二级缓存) --> BaseExecutor(PerpetualCache一级缓存) SimpleExecutor
//DefaultSqlSession.java public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; private final boolean autoCommit; private boolean dirty; private List<Cursor<?>> cursorList; } // BaseExecutor.java public abstract class BaseExecutor implements Executor { private static final Log log = LogFactory.getLog(BaseExecutor.class); protected Transaction transaction; protected Executor wrapper; protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads; protected PerpetualCache localCache; protected PerpetualCache localOutputParameterCache; protected Configuration configuration; protected int queryStack; private boolean closed; }
可以整理出 PerpetualCache 的调用关系
所以 一级缓存的生命周期是和 SqlSession 对象绑定在一起的,如果 sqlSession 不一样,是不会走缓存的
@Test //@Transactional public void cacheTest1() { AccountModel accountModel = accountDao.selectByPrimaryKey(1); System.out.println(accountModel); AccountModel accountModel1 = accountDao.selectByPrimaryKey(1); System.out.println(accountModel1); }
如果不加 @Transactional 注解,是不会使用一级缓存的,也就是创建了两个不同的 SqlSession 对象, 那 @Transaction 是如何保证获取到的是同一个 SqlSession 对象呢?
首先,spring 是通过 SqlsessionTemplate
创建 SqlSession 代理对象操作 mybatis 中的 SqlSession 对象
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); }
其中 SqlSessionInterceptor
是创建 代理 sqlSession 的过程
private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取 SqlSession 关键 SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator .translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }
在 getSqlSession
来创建 sqlSession
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED); // 从 threadLocal 中获取 SessionHolder SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); // 从 SessionHolder 获取 sqlSession SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } LOGGER.debug(() -> "Creating a new SqlSession"); session = sessionFactory.openSession(executorType); // 将 SqlSessionHolder 与当前线程绑定 registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }
这里分三个步骤:
**这样 从 SqlSessionHolder 中获取的 SqlSession 就是同一个对象了 **
TransactionSynchronizationManager.getResource(sessionFactory)
/** * Retrieve a resource for the given key that is bound to the current thread. */ @Nullable public static Object getResource(Object key) { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Object value = doGetResource(actualKey); return value; } //从 threadLocal 中获取 SqlSessionHolder private static Object doGetResource(Object actualKey) { // private static final ThreadLocal<Map<Object, Object>> // resources = new NamedThreadLocal<>("Transactional resources"); Map<Object, Object> map = resources.get(); if (map == null) { return null; } Object value = map.get(actualKey); // Transparently remove ResourceHolder that was marked as void... if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) { map.remove(actualKey); // Remove entire ThreadLocal if empty... if (map.isEmpty()) { resources.remove(); } value = null; } return value; }
SqlSession session = sessionHolder(executorType, holder)
private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) { SqlSession session = null; // 如果 holder 不为空 // 并且 holder 开启了事务 if (holder != null && holder.isSynchronizedWithTransaction()) { if (holder.getExecutorType() != executorType) { throw new TransientDataAccessResourceException( "Cannot change the ExecutorType when there is an existing transaction"); } holder.requested(); session = holder.getSqlSession(); } return session; }
从 SqlSessionHolder 获取到 同一个 SqlSession 的条件是:
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { SqlSessionHolder holder; if (TransactionSynchronizationManager.isSynchronizationActive()) { Environment environment = sessionFactory.getConfiguration().getEnvironment(); if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { // 用SqlSession 构建SqlSessionHolder holder = new SqlSessionHolder(session, executorType, exceptionTranslator); // 将SqlSessionHolder与 当前线程绑定 // 通过ThreadLocal 传递 TransactionSynchronizationManager.bindResource(sessionFactory, holder); TransactionSynchronizationManager .registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); holder.setSynchronizedWithTransaction(true); holder.requested(); } else { if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { LOGGER.debug(() -> "SqlSession [" + session + "] was not registered DataSource is not transactional"); } else { throw new TransientDataAccessResourceException( "SqlSessionFactory must be using a SpringManagedTransactionFactory ..."); } } } else { LOGGER.debug(() -> "SqlSession [" + session); } }
再回到刚才的问题开启事务为什么可以保证获取到的是同一个 SqlSession 对象?
spring 当且仅当在开启事务的场景下,通过 ThreadLocal 传递 同一个 SqlSession 对象