前面说到DefaultSqlSession不是线程安全的,所以在MyBatis和spring项目整合的时候不能直接使用DefaultSqlSession,而是自己封装了一个线程安全的SqlSessionTemplate,通过spring管理的SqlSessionTemplate来创建SqlSession,而SqlSessionTemplate又是单例的,那么它是怎么保证线程安全的呢?
SqlSessionTemplate
最容易想到的就是ThreadLocal,在每个线程保存各自的SqlSession,这样执行commit,close操作就不需要担心线程安全的问题了。
那么就看看spring里到底是怎么实现的吧
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;
//代理模式,使用SqlSessionInterceptor来增强
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}
这里的核心处理就在增强的SqlSessionInterceptor的invoke方法里
private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过一个工具类获取SqlSession(参数有SqlSessionFactory,Executor类型等)
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
Object result = method.invoke(sqlSession, args);
//判断当前SqlSession是否是Spring管理的,如果不是,就直接调用SqlSession的commit方法
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方法,这里执行后finally就不会再执行closeSqlSession了 因为sqlSession 被置为null了
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 {
if (sqlSession != null) {
//最后都会调用一次closeSqlSession来释放资源
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
不难看出,保证线程安全的关键代码在SqlSessionUtils里,最重要的是其中的getSqlSession方法
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
Assert.notNull(executorType, "No ExecutorType specified");
//根据SqlSessionFactory从TransactionSynchronizationManager中获取SqlSessionHolder,这里的SqlSession就是从TransactionSynchronizationManager的ThreadlLocal以sqlSessionFactory为key获取到的
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
//通过holder获取SqlSession,并且将其引用计数referenceCount + 1
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
//如果holder里没有SqlSession,那么就通过SqlSessionFactory创建一个新的DefaultSqlSession
session = sessionFactory.openSession(executorType);
//注册生成的SqlSession
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
}
---- TransactionSynchronizationManager
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
public static Object getResource(Object key) {
//根据sqlSessionFactory获取一个实际的key--如果是代理对象,actual就是目标Key对象
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doGetResource(actualKey);
if (value != null && logger.isTraceEnabled()) {
logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
}
return value;
}
private static Object doGetResource(Object actualKey) {
//从ThreadLocal里获取当前线程的map,然后从map里以sqlSessionFactory为key获取对应的value也就是SqlSessionHolder
Map<Object, Object> map = (Map)resources.get();
if (map == null) {
return null;
} else {
Object value = map.get(actualKey);
if (value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {
map.remove(actualKey);
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
return value;
}
}
从上面的源码不难猜出:
registerSessionHolder的方法其实就是将SqlSessionFactory和SqlSessionHolder以key-value的方式存储在TransactionSynchronizationManager 的ThreadLocal里,这样我们才能够从ThreadLocal里获取到对应的SqlSessionHolder
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
//获取对应的环境信息
Environment environment = sessionFactory.getConfiguration().getEnvironment();
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
}
//通过session,executorType,exceptionTranslator创建SqlSessionHolder
SqlSessionHolder holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
//将sessionFactory,holder以key-value的方式保存在TransactionSynchronizationManager的ThreadLocal里
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
TransactionSynchronizationManager.registerSynchronization(new SqlSessionUtils.SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
if (TransactionSynchronizationManager.getResource(environment.getDataSource()) != null) {
throw new TransientDataAccessResourceException("SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
}
}
} else if (LOGGER.isDebugEnabled()) {
LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
}
}
------TransactionSynchronizationManager 类源码
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
Map<Object, Object> map = (Map)resources.get();
if (map == null) {
map = new HashMap();
resources.set(map);
}
//存储sqlSessionFactory和sqlSessionHolder到map中
Object oldValue = ((Map)map).put(actualKey, value);
if (oldValue instanceof ResourceHolder && ((ResourceHolder)oldValue).isVoid()) {
oldValue = null;
}
if (oldValue != null) {
throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
} else {
if (logger.isTraceEnabled()) {
logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]");
}
}
}
closeSqlSession方法如下:先从TransactionSynchronizationManager中获取holder,然后判断当前SqlSession是否是从holder获取的,如果是说明session由spring管理的,那么直接调用holder.released(),将其引用计数-1(这跟重入锁的涉及有点像);如果不是则直接关闭session
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
Assert.notNull(session, "No SqlSession specified");
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
if (holder != null && holder.getSqlSession() == session) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
}
//spring管理,则使用holder进行释放,这里只是减少了一下引用计数,这样后面在本线程中可以复用
holder.released();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
}
session.close();
}
}
//判断session是否由spring管理
public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
Assert.notNull(session, "No SqlSession specified");
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
return holder != null && holder.getSqlSession() == session;
}
SqlSessionManager
SqlSession除了默认的实现类DefaultSqlSession,还有一个线程安全的实现SqlSessionManager
从其源码不难看出,其线程安全也是通过ThreadLocal实现的,SqlSessionManager中存储了SqlSessionFactory,通过其创建SqlSession的代理对象
//SqlSessionManager提供了很多重载的newInstance方法,通过SqlSessionFactoryBuilder会话工厂
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;
//用于保存与当前线程绑定的SqlSession
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
//通过jdk动态代理创建代理SqlSession
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
public void startManagedSession() {
//绑定当前线程创建的SqlSession到ThreadLocal里
this.localSqlSession.set(openSession());
}
@Override
public SqlSession openSession() {
return sqlSessionFactory.openSession();
}
private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
// Prevent Synthetic Access
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取当前线程绑定的SqlSession
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {
try {
//执行数据库操作
return method.invoke(sqlSession, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} else {
//如果localSqlSessio中没有SqlSession,即当前线程中没有创建SqlSession,则调用openSession(这里直接使用SqlSessionFactory来创建SqlSession)
final SqlSession autoSqlSession = openSession();
try {
//使用新创建的SqlSession执行数据库操作
final Object result = method.invoke(autoSqlSession, args);
autoSqlSession.commit();
return result;
} catch (Throwable t) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(t);
} finally {
autoSqlSession.close();
}
}
}
}
SqlSessionManager在使用的时候需要先调用startManagedSession来创建会话并存入ThreadLocal,如果没有开启的话那就直接使用SqlSessionFactory创建的SqlSession(此时会话不放入ThreadLocal,就无法保证线程安全饿了)
SqlSessionManager还实现了SqlSession的接口方法,比如:insert,update,select,会直接调用sqlSessionProxy代理对象中相应的方法。实际执行就会执行SqlSessionInterceptor里的invoke方法
总结:
-
DefaultSqlSession 不是线程安全的,不要使用单例模式
-
SqlSessionTemplate 和 SqlSessionManager 都持有 SqlSessionFactory,并且都是线程安全的,都通过ThreadLocal来实现SqlSession的生命周期管理
-
SqlSessionTemplate 由 Spring 事务管理器决定是否共用 SqlSession。事务内部共用 SqlSession,非事务不共用 SqlSession。SqlSessionManager 由使用者决定是否共用 SqlSession