mybatis 如何关闭connection
1.前言
最开始操作数据库是使用jdbc操作数据库,每次创建一个连接都需要关闭连接,避免占用资源.比如
Class.forName("com.jdbc.mysql.Driver"); Connection con = DriverManager.getConnection("url", "username", "password"); Statement sta = null; try { //执行sql语句 sta = con.createStatement(); sta.execute(""); } finally { //关闭连接 sta.close(); con.close(); }
最后需要通过 close 关闭连接;
2.mybatis 是如何管理连接资源的
public class DefaultSqlSession implements SqlSession { @Override public void close() { try { executor.close(isCommitOrRollbackRequired(false));//关闭执行器 closeCursors(); dirty = false; } finally { ErrorContext.instance().reset(); } } }
这里只列举出了sqlsession中的close方法,可以看到sqlsession通过执行executor的close方法关闭连接.
public abstract class BaseExecutor implements Executor { @Override public void close(boolean forceRollback) { try { try { rollback(forceRollback);//回滚事务 } finally { if (transaction != null) { transaction.close();关闭事务 } } } catch (SQLException e) { // Ignore. There's nothing that can be done at this point. log.warn("Unexpected exception on closing transaction. Cause: " + e); } finally { transaction = null; deferredLoads = null; localCache = null; localOutputParameterCache = null; closed = true; } } }
在BaseExecutor中,通过关闭事务来进行关闭的.我们继续往下挖.
public class ManagedTransaction implements Transaction { private static final Log log = LogFactory.getLog(ManagedTransaction.class); private DataSource dataSource; private TransactionIsolationLevel level; private Connection connection;//java.sql.Connection 就是我们常用的jdbc连接 Connection private final boolean closeConnection; @Override public void close() throws SQLException { if (this.closeConnection && this.connection != null) { if (log.isDebugEnabled()) { log.debug("Closing JDBC Connection [" + this.connection + "]"); } this.connection.close();//在close方法中进行关闭 } } }
在Transaction这个类中,通过执行Connection中的close方法关闭连接,但是这个方法需要通过自己手动写sqlsession.close(),那么为什么在具体的开发中不需要自己管理close()的调用呢?
答案就在spring与mybatis整合的jar包中.
public class SqlSessionTemplate implements SqlSession, DisposableBean { //这里实现了sqlSession,一般可以把SqlSessionTemplate当作sqlsession来使用 public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor());//在构造方法中,我们看到sqlSessionProxy这个代理类是通过内部类SqlSessionInterceptor来生成 } /** * {@inheritDoc} */ @Override public <T> T selectOne(String statement, Object parameter) { return this.sqlSessionProxy.<T> selectOne(statement, parameter); } /** * {@inheritDoc} */ @Override public <K, V> Map<K, V> selectMap(String statement, String mapKey) { return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey); //而且sql执行大部分都是通过代理类来调用,所以关键就是这个内部类 } private class SqlSessionInterceptor implements InvocationHandler { //这个就是内部类,实现了InvocationHandler接口,因为要通过代理方式完成关闭连接 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = getSqlSession( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); //这里执行被代理的方法 if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); //可以看到如果报错在catch语句中会关闭sqlsession,也就是我们刚刚分析的一系列类最终关闭Connection sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);//如果不报错,finally也关闭sqlsession } } } } }
spring一般生成SqlSessionTemplate用来实现SqlSession接口,可以把SqlSessionTemplate看成是SqlSession,在这个类中通过代理来关闭session,所以就不需要我们手动去执行close方法
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(); //关闭session } }
上面的代码就是SqlSessionUtils封装的关闭sqlsession的静态方法,因为是通过 import static 方法导入的,所以就不需要通过类名 SqlSessionUtils.closeSqlSession调用.
3.总结
mybatis 和 spring 是通过代理方式完成 connection连接的关闭.而且是通过jdk的代理.