為什么說(shuō)MyBatis默認(rèn)的DefaultSqlSession是線程不安全?
1 環(huán)境準(zhǔn)備
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testjpa?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123123"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/UsersMapper.xml"/>
</mappers>
</configuration>
UsersMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pack.mapper.UsersMapper">
<select id="selectList" resultType="com.pack.domain.Users">
select * from t_users
</select>
</mapper>
UsersMapper.java
package com.pack.mapper;
import java.util.List;
import com.pack.domain.Users;
public interface UsersMapper {
List<Users> selectList() ;
}
Users.java
public class Users{
private String id ;
private String username ;
private String password ;
}
UsersMapperTest.java測(cè)試類
public class UsersMapperTest {
private static final int MAX = 100 ;
private SqlSessionFactory sqlSessionFactory ;
private Thread[] threads = new Thread[MAX] ;
private CountDownLatch cdl = new CountDownLatch(MAX) ;
@Before
public void init() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testSelectList() throws Exception {
SqlSession session = sqlSessionFactory.openSession() ;
UsersMapper mapper = session.getMapper(UsersMapper.class) ;
for (int i = 0; i < MAX; i++) {
threads[i] = new Thread(() -> {
try {
cdl.await() ;
System.out.println(mapper.selectList()) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}) ;
}
for (int i = 0; i < MAX; i++) {
threads[i].start() ;
cdl.countDown() ;
}
System.in.read() ;
}
}
啟動(dòng)100個(gè)線程同時(shí)查詢,結(jié)果如下:
### Cause: java.lang.ClassCastException: org.apache.ibatis.executor.ExecutionPlaceholder cannot be cast to java.util.List
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:153)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:145)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
at com.sun.proxy.$Proxy8.selectList(Unknown Source)
at test.UsersMapperTest.lambda$0(UsersMapperTest.java:39)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassCastException: org.apache.ibatis.executor.ExecutionPlaceholder cannot be cast to java.util.List
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:152)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:89)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
... 9 more
[Users [id=1, username=admin, password=123123], Users [id=2, username=guest, password=111111]]
[Users [id=1, username=admin, password=123123], Users [id=2, username=guest, password=111111]]
程序拋出了異常ClassCastException類型轉(zhuǎn)換異常。也就是在多個(gè)線程同時(shí)使用SqlSession時(shí)出現(xiàn)了類型轉(zhuǎn)換錯(cuò)誤。
2 錯(cuò)誤分析
根據(jù)錯(cuò)誤信息,把錯(cuò)誤定位到DefaultSqlSession.java:153
public class DefaultSqlSession implements SqlSession {
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); // 這里拋出的異常
} finally {
ErrorContext.instance().reset();
}
}
}
繼續(xù)根據(jù)錯(cuò)誤日志,確定是執(zhí)行下面這行代碼出現(xiàn)錯(cuò)誤
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
而executor根據(jù)錯(cuò)誤日志確定為BaseExecutor類
public abstract class BaseExecutor implements Executor {
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// ...
List<E> list;
try {
queryStack++;
// 從本地緩存中獲取數(shù)據(jù),如果有會(huì)強(qiáng)制轉(zhuǎn)換為L(zhǎng)ist對(duì)象
// 位置1:
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 如果緩存中沒有,則會(huì)進(jìn)入該方法執(zhí)行
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
// ...
return list;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// 先將一個(gè)枚舉值存入到緩存中ExecutionPlaceholder
localCache.putObject(key, EXECUTION_PLACEHOLDER);
// 位置2
try {
// 做實(shí)際的查詢
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
// 刪除上面存入的值
localCache.removeObject(key);
}
// 將查詢出來(lái)的數(shù)據(jù)緩存起來(lái)
localCache.putObject(key, list);
// 位置3
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
}
public enum ExecutionPlaceholder {
EXECUTION_PLACEHOLDER
}
分析:當(dāng)線程1執(zhí)行到‘位置2’時(shí),此時(shí)緩存中緩存了ExecutionPlaceholder枚舉值,這是線程2開始執(zhí)行‘位置1’此時(shí)線程2從緩存中是能獲取值,此值是ExecutionPlaceholder枚舉值,該值怎么可能轉(zhuǎn)換為L(zhǎng)ist,所以這里就會(huì)拋出類型轉(zhuǎn)換異常了。
如果想正確執(zhí)行,只能是每個(gè)線程創(chuàng)建一個(gè)新的SqlSession對(duì)象。
3 默認(rèn)SqlSession實(shí)現(xiàn)
// 獲取SqlSession對(duì)象
// SqlSessionFactory的實(shí)現(xiàn)是DefaultSqlSessionFactory對(duì)象
sqlSessionFactory.openSession()
進(jìn)入openSession()方法
public class DefaultSqlSessionFactory implements SqlSessionFactory {
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
// SqlSession默認(rèn)實(shí)現(xiàn)使用的DefaultSqlSession。
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
DefaultSqlSession.java
/**
* The default implementation for {@link SqlSession}.
* Note that this class is not Thread-Safe.
*
* @author Clinton Begin
*/
public class DefaultSqlSession implements SqlSession {
// ...
}
// 在這注釋中已經(jīng)提到了該類is not Thread-Safe.
4 Spring如何處理
在Springboot中是如何保證線程安全的呢?
4.1 引入依賴
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
@SpringBootApplication
@MapperScan({"com.pack.mapper"})
public class SpringBootTransactionalApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootTransactionalApplication.class, args);
}
}
// 重點(diǎn)在這@Import上
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
}
4.2 自動(dòng)配置
public class MybatisAutoConfiguration implements InitializingBean {
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean() ;
// ...
return factory.getObject() ;
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
}
@MapperScan注解中應(yīng)用了@Import(MapperScannerRegistrar.class)
在這Import類中會(huì)注冊(cè)一個(gè)MapperScannerConfigurer配置類
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
// ...
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
// ...
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
}
這里注冊(cè)了一個(gè)核心類MapperScannerConfigurer該類用來(lái)掃描Mapper接口,并注冊(cè)為Bean。
4.3 掃描Mapper
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
// 實(shí)際Mapper接口注冊(cè)的是MapperFactoryBean對(duì)象一個(gè)FactoryBean對(duì)象
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// ...
// 該類用來(lái)掃描指定包下的類,并如果符合條件(是接口類)將其注冊(cè)為Bean(FactoryBean)
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
// 為null
scanner.setSqlSessionFactory(this.sqlSessionFactory);
// 為null
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
// 為null
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
// 為null
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
if (StringUtils.hasText(defaultScope)) {
scanner.setDefaultScope(defaultScope);
}
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 調(diào)用父類的doSan方法進(jìn)行查找所有符合條件的類,并將其注冊(cè)到容器中
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
// 對(duì)找到的BeanDefinition對(duì)象進(jìn)行處理
processBeanDefinitions(beanDefinitions);
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
AbstractBeanDefinition definition;
BeanDefinitionRegistry registry = getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
boolean scopedProxy = false;
if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
definition = (AbstractBeanDefinition) Optional
.ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
.map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
"The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
scopedProxy = true;
}
String beanClassName = definition.getBeanClassName();
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
// 重點(diǎn)是這里指定BeanClass對(duì)象,一個(gè)FactoryBean工廠Bean。
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
// ...
if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
definition.setScope(defaultScope);
}
// ...
}
}
}
通過(guò)上面的源碼可知,所有的Mapper接口都會(huì)通過(guò)MapperFactoryBean(是個(gè)FactoryBean)來(lái)注冊(cè)的Bean對(duì)象,在注入Mapper Bean的時(shí)候?qū)嶋H注入的是FactoryBean#getObject的返回值類型。
4.4 Mapper實(shí)例化
通過(guò)上面知道了所有的Mapper都是通過(guò)FactoryBean來(lái)構(gòu)建的。
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
public T getObject() throws Exception {
// getSqlSession()方法返回的是SqlSessionTemplate對(duì)象
return getSqlSession().getMapper(this.mapperInterface);
}
}
MapperFactoryBean類繼承了SqlSessionDaoSupport對(duì)象
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
}
在該類中提供了幾個(gè)setter方法,當(dāng)在注冊(cè)當(dāng)前MapperFactoryBean對(duì)象的時(shí)候就會(huì)注入在MybatisAutoConfiguration自動(dòng)配置類中注冊(cè)的SqlSessionFactory和SqlSessionTemplate兩個(gè)對(duì)象。
SqlSessionTemplate對(duì)象實(shí)現(xiàn)了SqlSession接口。
到這里你應(yīng)該知道了,在Spring環(huán)境下使用的SqlSession對(duì)象實(shí)際是SqlSessionTemplate對(duì)象。
接下來(lái)查看SqlSessionTemplate是如何保證線程安全的。
4.5 線程安全的SqlSession
在Spring環(huán)境下使用的SqlSessionTemplate對(duì)象。
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSession sqlSessionProxy;
// ...
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
// 實(shí)際的執(zhí)行是InvocationHandler#invoke方法
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
// 這里隨便列出一個(gè)方法
// 實(shí)現(xiàn)的SqlSession接口中的所有方法,實(shí)際都是有一個(gè)Proxy代理對(duì)象執(zhí)行的
// 該代理對(duì)象在構(gòu)造方法中被創(chuàng)建
public <T> T selectOne(String statement) {
return this.sqlSessionProxy.selectOne(statement);
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 重點(diǎn)是這里的getSqlSession方法了
// 該方法是調(diào)用SqlSessionUtils#getSqlSession(這里靜態(tài)導(dǎo)入)
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
// ...
return result;
}
}
}
}
SqlSessionUtils#getSqlSession方法
public final class SqlSessionUtils {
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
// ...
// 重點(diǎn)來(lái)了,先從同步事物管理器TransactionSynchronizationManager
// 中通過(guò)sessionFactory為key獲取SqlSessionHolder對(duì)象
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 如果存在執(zhí)行返回(保證同一個(gè)線程使用同一個(gè)SqlSession對(duì)象)
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
// 通過(guò)SqlSessionFactory對(duì)象獲取SqlSession對(duì)象
session = sessionFactory.openSession(executorType);
// 將獲取的SqlSession對(duì)象保存到ThreadLocal中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return 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) {
// 創(chuàng)建SqlSessionHolder對(duì)象,將創(chuàng)建的SqlSession對(duì)象保存
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
// 將當(dāng)前的SqlSessionHolder對(duì)象綁定到ThreadLocal中
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
// 注冊(cè)事務(wù)回調(diào)事件
TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
// 將資源標(biāo)記為與事務(wù)同步。
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
// ...
}
}
// ...
}
}
public abstract class TransactionSynchronizationManager {
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
// 將資源綁定到當(dāng)前的線程對(duì)象中
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Map<Object, Object> map = resources.get();
// set ThreadLocal Map if none found
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
Object oldValue = map.put(actualKey, value);
// Transparently suppress a ResourceHolder that was marked as void...
if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
oldValue = null;
}
// ...
}
public static Object getResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doGetResource(actualKey);
return value;
}
@Nullable
private static Object doGetResource(Object actualKey) {
// 從當(dāng)前的ThreadLocal中獲取對(duì)象
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;
}
}
通過(guò)上面的源碼分析清楚的知道,在Spring中SqlSession的線程安全是通過(guò)ThreadLocal來(lái)保證的,通過(guò)Spring提供的事務(wù)通過(guò)管理器來(lái)保存SqlSession對(duì)象,這樣就使得同一個(gè)線程獲取的是同一個(gè)SqlSession。
4.6 事務(wù)管理
在事務(wù)管理方法在Spring環(huán)境下使用的是SpringManagedTransactionFactory事務(wù)管理器工廠
public class SpringManagedTransactionFactory implements TransactionFactory {
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
return new SpringManagedTransaction(dataSource);
}
@Override
public Transaction newTransaction(Connection conn) {
throw new UnsupportedOperationException("New Spring transactions require a DataSource");
}
@Override
public void setProperties(Properties props) {
}
}
事務(wù)對(duì)象
public class SpringManagedTransaction implements Transaction {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringManagedTransaction.class);
private final DataSource dataSource;
private Connection connection;
private boolean isConnectionTransactional;
private boolean autoCommit;
public SpringManagedTransaction(DataSource dataSource) {
notNull(dataSource, "No DataSource specified");
this.dataSource = dataSource;
}
@Override
public Connection getConnection() throws SQLException {
if (this.connection == null) {
openConnection();
}
return this.connection;
}
private void openConnection() throws SQLException {
// 在Spring環(huán)境下,事務(wù)由Spring管理,所以這里先從Spring的ThreadLocal中獲取連接對(duì)象
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.autoCommit = this.connection.getAutoCommit();
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
}
@Override
public void commit() throws SQLException {
if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
this.connection.commit();
}
}
@Override
public void rollback() throws SQLException {
if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
this.connection.rollback();
}
}
@Override
public void close() throws SQLException {
DataSourceUtils.releaseConnection(this.connection, this.dataSource);
}
@Override
public Integer getTimeout() throws SQLException {
ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (holder != null && holder.hasTimeout()) {
return holder.getTimeToLiveInSeconds();
}
return null;
}
}
public abstract class DataSourceUtils {
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
}
}
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
conHolder.setConnection(fetchConnection(dataSource));
}
return conHolder.getConnection();
}
Connection con = fetchConnection(dataSource);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
try {
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
} else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
//...catch
}
return con;
}
}
如果SqlSession沒有被Spring管理(也就是事務(wù)是自行處理沒有用Spring的事務(wù)管理@Transactional)那么Spring會(huì)強(qiáng)制提交事務(wù)。如果沒有在Spring環(huán)境下,Mybatis事務(wù)是不會(huì)自動(dòng)提交的(的看你openSession方法參數(shù)如何傳)。