mybatis 为什么每次插入的时候总会创建一个SqlSession?
问题记录:
工作环境是使用spring boot,使用用的mybatis,在一次调试中。发现每一次插入一条 数据都会创建一个SqlSession。如图:
图1:
问题可能的原因:
原因分析:#1 没有使用缓存
因为这个是插入,不是查询,所以这里不存在什么缓存的问题。
后来百度了一波,网上说是没有使用事务。
加上@Transactional
图2:
发现“Creating a new SqlSession”这两个烦人的东西居然还在。
不管了,直接分析源码
直接分析源码,老子还不信了,搞不定你我还混什么: 1.开启debug 2.打上断点
图3:
发现session为空
继续走...
图2 #分析
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { //从从前线程的threadLocal 中获取sqlSessionHolder SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); //调用静态方法sessionHoler 判断是否存在符合要求的sqlSession SqlSession session = sessionHolder(executorType, holder); // 判断当前sqlSessionHolder 中是否持有sqlSession (即当前操作是否在事务当中) if (session != null) { //如果持有sqlSesison 的引用,则直接获取 return session; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Creating a new SqlSession"); } //获取新的sqlSession 对象。这里由sessionFacory产生的defaultSqlSession session = sessionFactory.openSession(executorType); //判断判断,当前是否存在事务,将sqlSession 绑定到sqlSessionHolder 中,并放到threadLoacl 当中 registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }
图3 #分析
private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) { SqlSession session = null; if (holder != null && holder.isSynchronizedWithTransaction()) { //hodler保存的执行类型和获取SqlSession的执行类型不一致,就会抛出异常,也就是说在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用 if (holder.getExecutorType() != executorType) { throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction"); } //增加该holder,也就是同一事务中同一个sqlSessionFactory创建的唯一sqlSession,其引用数增加,被使用的次数增加 holder.requested(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction"); } //返回sqlSession session = holder.getSqlSession(); } return session; }
当然,这里还少了一个注册的方法,贴上:
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { SqlSessionHolder holder; //判断事务是否存在 if (TransactionSynchronizationManager.isSynchronizationActive()) { Environment environment = sessionFactory.getConfiguration().getEnvironment(); //加载环境变量,判断注册的事务管理器是否是SpringManagedTransaction,也就是Spring管理事务 if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]"); } holder = new SqlSessionHolder(session, executorType, exceptionTranslator); //如果当前回话处在事务当中,则将holder 绑定到ThreadLocal 中 //以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>> resources中 TransactionSynchronizationManager.bindResource(sessionFactory, holder); //将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); //设置当前holder和当前事务同步 holder.setSynchronizedWithTransaction(true); //holder 引用次数+1 holder.requested(); } else { if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { if (LOGGER.isDebugEnabled()) { 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 { if (LOGGER.isDebugEnabled()) { LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active"); } }
}
补充
sqlSessionHolder 继承了spring的ResourceHolderSupport
public abstract class ResourceHolderSupport implements ResourceHolder { //事务是否开启private boolean synchronizedWithTransaction = false; private boolean rollbackOnly = false; private Date deadline; // 引用次数 private int referenceCount = 0; private boolean isVoid = false; }
在sqlSession 关闭session 的时候, 使用了工具了sqlSessionUtils的closeSqlSession 方法。sqlSessionHolder 也是做了判断,如果回话在事务当中,则减少引用次数,没有真实关闭session。如果回话不存在事务,则直接关闭session
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); //如果holder 中持有sqlSession 的引用,(即会话存在事务) if ((holder != null) && (holder.getSqlSession() == session)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Releasing transactional SqlSession [" + session + "]"); } //每当一个sqlSession 执行完毕,则减少holder 持有引用的次数 holder.released(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Closing non transactional SqlSession [" + session + "]"); } //如果回话中,不存在事务,则直接关闭session session.close(); } }
到了这一步,问题已经很明显了。
出现原因 与 分析
- SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); 这一句:他从从前线程的threadLocal 中获取sqlSessionHolder。但是在在sqlSession 关闭session 的时候,sqlSessionHolder也是做了判断。如果会话在事务中,就减少引用次数,没有真实关闭session。如果会话不存在事务,则直接关闭session。也就是说,必须开启事务,但这个问题好像只是插入了一下,事务已经执行完成了,下一次插入的时候,由于上一个事务执行完成了, 如果不存在holder或没有被事务锁定,则会创建新的sqlSession,即 Creating a new SqlSession,通过sessionFactory.openSession()方法。如果会话不存在事务,就直接把session关闭了,同时,也减少了引用次数。
- 换一句话来说:如果在插入的代码块中,再加上一个查询的代码,或者再插入一条数据的代码,这样就不会出现Creating a new SqlSession这个烦人的家伙。好了,祝大家好运!!!
引用:
SqlSessionHolder作用
如果有侵权,马上删除
相关推荐
houmenghu 2020-11-17
我心似明月 2020-11-09
oraclemch 2020-11-06
ltd00 2020-09-12
康慧欣 2020-09-10
waveclouds 2020-09-04
蓝色深海 2020-09-15
jincheng 2020-09-01
思君夜未眠 2020-08-25
取个好名字真难 2020-08-06
歆萌 2020-08-03
阳光之吻 2020-08-03
婷婷小屋 2020-07-28
solarspot 2020-07-28
MLXY 2020-07-26
dxbjfu0 2020-07-26
songbinxu 2020-07-19
83520298 2020-07-06