zoukankan      html  css  js  c++  java
  • 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的代理.

  • 相关阅读:
    Centos7安装Zabbix4.0步骤
    linux异常处理:selinux配置错误导致无法重启
    自学Python5.4-类 _init_方法
    自学Python5.3-类和对象的简单操作
    自学Linux Shell9.3-基于Red Hat系统工具包:RPM属性依赖的解决方式-YUM在线升级
    自学Linux Shell9.4-基于Red Hat系统工具包存在两种方式之二:源码包
    自学Linux Shell9.2-基于Red Hat系统工具包存在两种方式之一:RPM包
    JS 对象API之修改、删除对象的属性
    JS 对象API之判断自有属性、共有属性
    JS 对象API之判断父对象是否在子对象的原型链上
  • 原文地址:https://www.cnblogs.com/lishuaiqi/p/11050187.html
Copyright © 2011-2022 走看看