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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM