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

 

posted @ 2019-06-19 11:11  随意的马蒂洛克  阅读(4104)  评论(1编辑  收藏  举报