SqlSession的初始化
- 声明周期-SqlSession为回话声明周期,理论上一次用户请求,即一个线程里只开启一次session,使 用后即关闭
- 依赖-DefaultSqlSessionFactory为SqlSesson的工场类,看下面的代码,即可知道Configuration, Executor和Connection为它所依赖对象
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) { try { boolean autoCommit; try { autoCommit = connection.getAutoCommit(); } catch (SQLException e) { // Failover to true, as most poor drivers // or databases won't support transactions autoCommit = true; } connection = wrapConnection(connection); final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); Transaction tx = transactionFactory.newTransaction(connection, autoCommit); Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
- 如何使用:
原始的使用方式如下,一个线程里的所有数据库操作方法中都需要新开启Session,浪费资源
SqlSession session= sqlSessionFactory.openSession(); UserDao userDao = session.getMapper(UserDao.class); UserVo vo = new UserVo(); vo.setName("a"); List<UserVo> users = userDao.queryUsers(user);
那我们来看看Spring是如何管理SqlSession的,SqlSessonUtils中获取Session的方法如下,*注解的代码是获取session的核心,其本质是ThreadLocal绑定资源到当前线程上,
public static SqlSession getSqlSession( SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { Assert.notNull(sessionFactory, "No SqlSessionFactory specified"); Assert.notNull(executorType, "No ExecutorType specified"); //***************************************************** SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); //***************************************************** if (holder != null && holder.isSynchronizedWithTransaction()) { if (holder.getExecutorType() != executorType) { throw new TransientDataAccessResourceException( "Cannot change the ExecutorType when there is an existing transaction"); } holder.requested(); if (logger.isDebugEnabled()) { logger.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction"); } return holder.getSqlSession(); } DataSource dataSource = sessionFactory.getConfiguration().getEnvironment().getDataSource(); // SqlSessionFactoryBean unwraps TransactionAwareDataSourceProxies but // we keep this check for the case that SqlSessionUtils is called from custom code boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy); Connection conn; try { conn = transactionAware ? dataSource.getConnection() : DataSourceUtils.getConnection(dataSource); } catch (SQLException e) { throw new CannotGetJdbcConnectionException("Could not get JDBC Connection for SqlSession", e); } if (logger.isDebugEnabled()) { logger.debug("Creating SqlSession with JDBC Connection [" + conn + "]"); } // Assume either DataSourceTransactionManager or the underlying // connection pool already dealt with enabling auto commit. // This may not be a good assumption, but the overhead of checking // connection.getAutoCommit() again may be expensive (?) in some drivers // (see DataSourceTransactionManager.doBegin()). One option would be to // only check for auto commit if this function is being called outside // of DSTxMgr, but to do that we would need to be able to call // ConnectionHolder.isTransactionActive(), which is protected and not // visible to this class. SqlSession session = sessionFactory.openSession(executorType, conn); // Register session holder and bind it to enable synchronization. // // Note: The DataSource should be synchronized with the transaction // either through DataSourceTxMgr or another tx synchronization. // Further assume that if an exception is thrown, whatever started the transaction will // handle closing / rolling back the Connection associated with the SqlSession. if (TransactionSynchronizationManager.isSynchronizationActive()) { if (!(sessionFactory.getConfiguration().getEnvironment().getTransactionFactory() instanceof SpringManagedTransactionFactory) && DataSourceUtils.isConnectionTransactional(conn, dataSource)) { throw new TransientDataAccessResourceException( "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"); } if (logger.isDebugEnabled()) { logger.debug("Registering transaction synchronization for SqlSession [" + session + "]"); } holder = new SqlSessionHolder(session, executorType, exceptionTranslator); TransactionSynchronizationManager.bindResource(sessionFactory, holder); TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); holder.setSynchronizedWithTransaction(true); holder.requested(); } else { if (logger.isDebugEnabled()) { logger.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active"); } } return session; }
以上代码总结起来, 主要有以下两点
- SessionHolder为SqlSession,ExecutorType和ExceptionTranslator的包装类
- 以SessionFactory为key,在Thread声明周期里缓存SessionHold
具体Spring trasaction的内部解析请见另一片博客