最近在看Mybatis源码,对于理解SqlSessionTemplate是如何保证线程安全的网上的文章不多。希望通过本文能够帮助大家清楚理解,类关系图如下:
DefaultSqlSession与SqlSessionManager解析
在Mybatis中SqlSession默认有DefaultSqlSession和SqlSessionManager两个实现类
DefaultSqlSession是真正的实现类调用Executor,但不是线程安全的。
Mybatis又实现了对SqlSession和SQLSessionFactory的封装类SqlSessionManager,线程安全并通过localSqlSession实现复用从而提高性能。
private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal();
SqlSessionManager通过SqlSessionInterceptor实现对DefaultSqlSession代理调用。
private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//由调用者决定当前线程是否复用 SqlSession
SqlSession sqlSession = (SqlSession)SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {
try {
return method.invoke(sqlSession, args);
} catch (Throwable var12) {
throw ExceptionUtil.unwrapThrowable(var12);
}
} else {
//如果不复用,则每次调用都新建 SqlSession 并使用后销毁
SqlSession autoSqlSession = SqlSessionManager.this.openSession();
Object var7;
try {
Object result = method.invoke(autoSqlSession, args);
autoSqlSession.commit();
var7 = result;
} catch (Throwable var13) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(var13);
} finally {
autoSqlSession.close();
}
return var7;
}
}
}
DefaultSqlSession和SqlSessionManager之间的区别:
1、单例模式下DefaultSqlSession是线程不安全的,而SqlSessionManager是线程安全的;
2、SqlSessionManager可以选择通过localSqlSession这个ThreadLocal变量,记录与当前线程绑定的SqlSession对象,供当前线程循环使用,从而避免在同一个线程多次创建SqlSession对象造成的性能损耗;
3、使用DefaultSqlSession为了保证线程安全需要为每一个操作都创建一个SqlSession对象,其性能可想而知;
SqlSessionTemplate是怎么保证线程安全
SqlSessionTemplate是MyBatis专门为Spring提供的,支持Spring框架的一个SqlSession获取接口。主要是为了继承Spring,并同时将是否共用SqlSession的权限交给Spring去管理。
1、通过创建sqlSessionProxy代理类,将调用导向SqlSessionInterceptor的invoke方法。
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 SqlSessionTemplate.SqlSessionInterceptor());
}
2、获取线程私有的SqlSession,调用DefaultSqlSession对应的实际方法上
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 {
//实际调用DefaultSession的对应方法
Object result = method.invoke(sqlSession, args);
//判断当前sqlSession是否被Spring管理,如果没有直接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(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) {
//正常返回将线程私有sqlSession调用次数减一
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
3、查看getSqlSession()方法就知道每个线程对应的SqlSession都是私有的不会被共用,所以SqlSessionTemplate是线程安全的。
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
Assert.notNull(executorType, "No ExecutorType specified");
//从线程私有存储中获取SqlSession
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
//没有则新建一个DefaultSqlSession
session = sessionFactory.openSession(executorType);
//存到线程私有存储中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
}
总结
究其根本SqlSession真正的实现类只有DefaultSqlSession,SqlSessionManager和SqlSessionTemplate都是通过代理转发到DefaultSqlSession对应方法。
单例模式下的DefaultSqlSession不是线程安全的,SqlSessionManager和SqlSessionTemplate线程安全的根本就是每一个线程对应的SqlSession都是不同的。如果每一个操作都创建一个SqlSession对象,操作完又进行销毁导致性能极差。通过线程私有ThreadLocal存储SqlSession进行复用,从而提高性能。
参考:https://blog.csdn.net/bntx2jsqfehy7/article/details/79441545#commentsedit