Mybtais

  1. 在同一个方法中,Mybatis多次请求数据库是否要创建多个sqlsession会话

    1. 在加了注解的事务的情况下,Mapper的每次请求数据库都会创建一个sqlsession与数据库交互
    2. 在加了事务的方法中,多次请求只创建一个sqlsession。

什么是SqlSession?

sqlsession是Mybatis工作的最顶层API会话接口,所有的数据库操作都由它实现。一个Sqlsession对应一次数据库会话,它不是永久存活,每次访问数据库的时候进行创建。一个SqlSession应仅存活于一个业务请求当中。

SqlSession是否是线程安全的?

SqlSession不是线程安全的,每个线程都应该有它的Sqlsession,不能将一个SqlSession搞成单例模式,或者静态域和实例变量的形式都会导致sqlsession出现事务问题。

SqlSession的创建过程:

  • 从Configuration配置拿到Environment数据源
  • 从数据源中获取TransactionFactory和DataSource并且创建一个Transaction连接管理对象
  • 创建Executor对象(JDBC的操作封装)
  • 创建Sqlsession会话。

由此证明创建一个SqlSession,都会创建一个sqlsession的连接管理对象。如果出现Sqlsession共享,则Transaction会出问题。

从源码的角度分析

  1. public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
  2. Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  3. Assert.notNull(executorType, "Property 'executorType' is required");
  4. this.sqlSessionFactory = sqlSessionFactory;
  5. this.executorType = executorType;
  6. this.exceptionTranslator = exceptionTranslator;
  7. this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
  8. }

从源码当中可以看到此处采用了jdk的动态代理,实际处理的是SqlSessionInterceptor

```java public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取sqlsession 该session可能是上一次的 也可能是新建的 SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

  1. Object unwrapped;
  2. try {
  3. //执行mapper方法
  4. Object result = method.invoke(sqlSession, args);
  5. //判断Sqlsession是否是一个事务 如果不是一个事务则commit 如果是一个事务 则不commit
  6. if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
  7. sqlSession.commit(true);
  8. }
  9. unwrapped = result;
  10. } catch (Throwable var11) {
  11. unwrapped = ExceptionUtil.unwrapThrowable(var11);
  12. if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
  13. SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
  14. sqlSession = null;
  15. Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
  16. if (translated != null) {
  17. unwrapped = translated;
  18. }
  19. }
  20. throw (Throwable)unwrapped;
  21. } finally {
  22. //当前会话不为空
  23. if (sqlSession != null) {
  24. //根据当前会话是否有事务来决定释放还是直接关闭
  25. SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
  26. }
  27. }
  28. return unwrapped;
  29. }
  1. > Mapper的所有方法最终都会用这个方法来完成数据库的所有操作。
  2. > ```java
  3. public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
  4. PersistenceExceptionTranslator exceptionTranslator) {
  5. notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  6. notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
  7. //TransactionSynchronizationManager 是Spring的一个事务管理器 将资源存储到当前线程中
  8. SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
  9. SqlSession session = sessionHolder(executorType, holder);
  10. if (session != null) {
  11. return session;
  12. }
  13. LOGGER.debug(() -> "Creating a new SqlSession");
  14. session = sessionFactory.openSession(executorType);
  15. //
  16. registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
  17. return session;
  18. }

```java private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { SqlSessionHolder holder; //Spring TX 是否激活 判断事务管理器是否激活的条件喂 synchronizations的事务管理器是否为空 if (TransactionSynchronizationManager.isSynchronizationActive()) { //获取Enviorment环境 Environment environment = sessionFactory.getConfiguration().getEnvironment(); //当前的环境是否是Spring事务管理工厂进行管理 if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { LOGGER.debug(() -> “Registering transaction synchronization for SqlSession [“ + session + “]”); holder = new SqlSessionHolder(session, executorType, exceptionTranslator); // 绑定当前SqlSessionHolder到线程ThreadLocal中 TransactionSynchronizationManager.bindResource(sessionFactory, holder); // // 注册SqlSession同步回调器 TransactionSynchronizationManager .registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); holder.setSynchronizedWithTransaction(true); //会话次数+1 holder.requested(); } else { if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { LOGGER.debug(() -> “SqlSession [“ + session

  1. + "] was not registered for synchronization because DataSource is not transactional");
  2. } else {
  3. throw new TransientDataAccessResourceException(
  4. "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
  5. }
  6. }
  7. } else {
  8. LOGGER.debug(() -> "SqlSession [" + session
  9. + "] was not registered for synchronization because synchronization is not active");
  10. }

}

  1. >

//当程序启动的时候 执行 该作用当前线程激活事务。 由事务管理上的事务开始时调用 public static void initSynchronization() throws IllegalStateException { if (isSynchronizationActive()) { throw new IllegalStateException(“Cannot activate transaction synchronization - already active”); } logger.trace(“Initializing transaction synchronization”); synchronizations.set(new LinkedHashSet<>()); }

  1. > //Mybatis自定义实现了一个事务同步回调器,在注册SqlSession的同时会将SqlSessionSynchronization注册到当前线程事务管理器中,它的作用是根据事务的完成状态回调来处理线程资源,即当前如果有事务,那么每次状态发生就回调用事务同步器。
  2. > ```java
  3. public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
  4. notNull(session, NO_SQL_SESSION_SPECIFIED);
  5. notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  6. SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
  7. return (holder != null) && (holder.getSqlSession() == session);
  8. }

取决于当前SqlSession是否为空并且判断当前SqlSession是否与ThreadLocal的SqlSession相等。如果当前没有事务,SqlSession是不会保存到事务管理器,没有事务。会话提交

  1. public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
  2. notNull(session, NO_SQL_SESSION_SPECIFIED);
  3. notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  4. SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
  5. if ((holder != null) && (holder.getSqlSession() == session)) {
  6. if (LOGGER.isDebugEnabled()) {
  7. LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
  8. }
  9. holder.released();
  10. } else {
  11. if (LOGGER.isDebugEnabled()) {
  12. LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
  13. }
  14. session.close();
  15. }
  16. }

Spring的自定义事务的一些机制,当前有事务时,会初始化当前线程事务管理器的synchronizations,激活了当前线程同步管理器,当Mybatis访问数据库会首先从当前线程事务管理器获取SqlSession,如果不存在就会创建一个会话,接着注册会话到当前线程事务管理器中,如果当前有事务,则会话不关闭也不commit,Mybatis还自定义了一个TransactionSynchronization,用于事务每次状态发生时回调处理