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的代理.