基本概念
- SqlSession:
- 數據庫CRUD及事務操作接口
- 線程不安全,常用於Request範圍或method範圍
// Request範圍,4次sql執行共用一個SqlSession
sqlSessionManager.startManagedSession();
try {
sqlSessionManager.query1();
sqlSessionManager.query2();
sqlSessionManager.update1();
sqlSessionManager.update2();
//...
}catch (Throwable t) {
sqlSessionManager.rollback();
} finally {
sqlSessionManager.close();
}
// 方法範圍,兩次調用創建了兩個session
SqlSessionManager.query1();
SqlSessionManager.query2();
- SqlSessionFactory
- 創建SqlSession的工廠類
- SqlSessionManager/SqlSessionTemplate[裝飾器模式]
- SqlSession裝飾器
- 增強SqlSession生命週期管理
SqlSessionManager/SqlSessionTemplate源碼分析
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
// SqlSession動態代理,增強SqlSession生命週期管理
private final SqlSession sqlSessionProxy;
// 線程局部變量localSqlSession,每個線程內部都持有一個sqlSession副本,互不干擾
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
// 創建代理對象SqlSessionProxy
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {
// 如果線程內已存在sqlSession,直接共用,方法調用後由程序員手動關閉
try {
return method.invoke(sqlSession, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} else {
// 1. 創建方法級sqlSession
final SqlSession autoSqlSession = openSession();
try {
// 2. 調用SqlSession方法執行sql
final Object result = method.invoke(autoSqlSession, args);
// 3.1 執行成功,提交事務
autoSqlSession.commit();
return result;
} catch (Throwable t) {
// 3.2 執行異常,回滾事務
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(t);
} finally {
// 4. 關閉SqlSession
autoSqlSession.close();
}
}
}
}
}
/**
* 1. Thread safe, Spring managed, {@code SqlSession} that works with Spring
* transaction management to ensure that that the actual SqlSession used is the
* one associated with the current Spring transaction.
* spring管理,線程安全,確保SqlSession是當前spring事務的sqlSession
* 2. SqlSession生命週期管理,包括commit, rollback, close
* 3. 單例,所有DAO可以共用一個
*/
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
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());
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
// 非事務的sqlSession,執行完立即commit
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
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);
}
}
}
}
}
/**
* 1. 非事務SqlSession,close
* 2. 事務sqlsession, 更新引用計數器,當計數器爲0,即事務結束時,spring調用close callback
*/
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
......
}
- Spring SqlSessionTemplate SqlSession生命週期管理
- SqlSessionTemplate與SqlSessionManager實現SqlSession生命週期管理的方式相同
- SqlSessionManager由使用者決定是否共用SqlSession
- SqlSessionTemplate由Spring事務管理器決定是否共用SqlSession。
- 事務內部共用SqlSession
- 非事務不共用SqlSession
Spring創建SqlSessionFactory, SqlSessionTemplate, SqlSession流程
spring配置
<!-- define the SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="*.dao" />
</bean>
MapperProxy
- DAO動態代理類:攔截DAO方法調用,調用SqlSession執行SQL,返回結果
- Spring中採用SqlSessionTemplate執行SQL,對SqlSession的生命週期管理進行增強
// 攔截器
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 執行sql,處理結果,並返回
return mapperMethod.execute(sqlSession, args);
}
......
}
// MapperProxyFactory newInstance()生成動態代理對象
Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, new MapperProxy<T>(sqlSession, mapperInterface, methodCache))
JAVA動態代理
- 詳見代理模式(暫無)