自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為什么說(shuō)MyBatis默認(rèn)的DefaultSqlSession是線程不安全?

開發(fā) 前端
如果SqlSession沒有被Spring管理(也就是事務(wù)是自行處理沒有用Spring的事務(wù)管理@Transactional)那么Spring會(huì)強(qiáng)制提交事務(wù)。如果沒有在Spring環(huán)境下,Mybatis事務(wù)是不會(huì)自動(dòng)提交的(的看你openSession方法參數(shù)如何傳)。

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ù)如何傳)。

責(zé)任編輯:武曉燕 來(lái)源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2024-03-22 12:29:03

HashMap線程

2024-01-19 08:42:45

Java線程字符串

2020-04-22 20:35:02

HashMap線程安全

2023-09-07 10:50:22

2023-06-01 19:24:16

2012-04-16 10:12:54

Java線程

2017-06-12 15:48:26

2015-07-01 14:48:51

2019-08-01 17:38:39

2021-12-17 11:06:39

linux設(shè)計(jì)高可用

2024-04-03 08:32:30

2010-06-13 16:43:59

2021-05-17 07:51:44

SimpleDateF線程安全

2021-02-19 10:40:48

HashMap線程Java

2018-06-11 23:44:03

2024-09-29 15:36:25

2009-08-03 16:58:59

C#不安全代碼

2014-09-12 17:44:23

2010-04-01 09:24:36

Windows 7管理員權(quán)限

2020-11-03 12:32:25

影子物聯(lián)網(wǎng)物聯(lián)網(wǎng)IOT
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)