美文网首页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