美文网首页Java
Mybatis的会话机制

Mybatis的会话机制

作者: TZX_0710 | 来源:发表于2021-07-01 10:14 被阅读0次

    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会出问题。

    从源码的角度分析

        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());
        }
    

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

     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);
    
                Object unwrapped;
                try {
                    //执行mapper方法
                    Object result = method.invoke(sqlSession, args);
                    //判断Sqlsession是否是一个事务  如果不是一个事务则commit  如果是一个事务 则不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) {
                        //根据当前会话是否有事务来决定释放还是直接关闭
                        SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    }
    
                }
    
                return unwrapped;
            }
    

    Mapper的所有方法最终都会用这个方法来完成数据库的所有操作。

     public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
          PersistenceExceptionTranslator exceptionTranslator) {
    
        notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
        notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
      //TransactionSynchronizationManager 是Spring的一个事务管理器 将资源存储到当前线程中
        SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    
        SqlSession session = sessionHolder(executorType, holder);
        if (session != null) {
          return session;
        }
      
        LOGGER.debug(() -> "Creating a new SqlSession");
        session = sessionFactory.openSession(executorType);
      //
        registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
        return session;
      }
    
    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
                  + "] was not registered for synchronization because DataSource is not transactional");
            } else {
              throw new TransientDataAccessResourceException(
                  "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
            }
          }
        } else {
          LOGGER.debug(() -> "SqlSession [" + session
              + "] was not registered for synchronization because synchronization is not active");
        }
    
      }
    
    //当程序启动的时候 执行 该作用当前线程激活事务。 由事务管理上的事务开始时调用
    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<>());
      }
    

    //Mybatis自定义实现了一个事务同步回调器,在注册SqlSession的同时会将SqlSessionSynchronization注册到当前线程事务管理器中,它的作用是根据事务的完成状态回调来处理线程资源,即当前如果有事务,那么每次状态发生就回调用事务同步器。

    public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
      notNull(session, NO_SQL_SESSION_SPECIFIED);
      notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
      SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
      return (holder != null) && (holder.getSqlSession() == session);
    }
    

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

    public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
      notNull(session, NO_SQL_SESSION_SPECIFIED);
      notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
      SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
      if ((holder != null) && (holder.getSqlSession() == session)) {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
        }
        holder.released();
      } else {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
        }
        session.close();
      }
    }
    

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

    相关文章

      网友评论

        本文标题:Mybatis的会话机制

        本文链接:https://www.haomeiwen.com/subject/nbkjultx.html