Mybtais
- 在同一个方法中,Mybatis多次请求数据库是否要创建多个sqlsession会话
- 在加了注解的事务的情况下,Mapper的每次请求数据库都会创建一个sqlsession与数据库交互
- 在加了事务的方法中,多次请求只创建一个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,用于事务每次状态发生时回调处理
网友评论