Análise de código-fonte do pool de conexão de banco de dados Mybatis

Análise de código-fonte do pool de conexão de banco de dados Mybatis

Programação da tecnologia TSMYK Java

O primeiro endereço deste artigo é o endereço do blog pessoal:
https://my.oschina.net/mengyuankan/blog/2664784

Este artigo irá apresentar os seguintes aspectos

  1. Artigos relacionados
  2. Prefácio
  3. Diagrama de Classe
  4. Implementação de classe de fábrica
  5. Realização de conexão de banco de dados
  6. Implementação de pool de conexão
  7. Obtenha uma conexão do pool de conexão (fluxograma)
  8. Coloque a conexão no pool de conexão (fluxograma)

    Artigos relacionados

Análise de código-fonte de arquivos de configuração de análise de
Mybatis Análise de código-fonte de conversão de tipo Mybatis

Prefácio

Ao usar Mybatis, a conexão de banco de dados geralmente usa componentes de fonte de dados de terceiros, como C3P0, DBCP e Druid, etc. Na verdade, Mybatis também tem sua própria implementação de fonte de dados, que pode se conectar ao banco de dados e tem a função de conexão Dê uma olhada em um princípio de implementação da fonte de dados e pool de conexão implementado por Mybatis.

Diagrama de Classe

A realização da fonte de dados Mybatis está principalmente no pacote da fonte de dados:
Análise de código-fonte do pool de conexão de banco de dados Mybatis

Todos os nossos componentes de fonte de dados comuns implementam a interface Javax.sql.DataSource, Mybatis também implementa essa interface e fornece duas classes de implementação UnpooledDataSource e PooledDataSource. Uma usa pool de conexão e a outra não usa pool de conexão. Além disso, para essas duas classes, Mybatis também fornece duas classes de fábrica para criar objetos, que é uma aplicação do padrão de método de fábrica. Primeiro, vamos examinar um diagrama de classe deles:
Análise de código-fonte do pool de conexão de banco de dados Mybatis

Em relação às classes mencionadas acima, PooledDataSource e UnpooledDataSource são a lógica principal da implementação da fonte de dados. O código é mais complicado. Vamos examiná-lo mais tarde. Agora vamos dar uma olhada nas duas classes de fábrica.

DataSourceFactory

Vamos dar uma olhada na classe DataSourceFactory primeiro. Esta classe é a interface de nível superior das classes de fábrica JndiDataSourceFactory e UnpooledDataSourceFactory. Apenas dois métodos são definidos, conforme mostrado abaixo:


public interface DataSourceFactory {
  // 设置 DataSource 的相关属性,一般在初始化完成后进行设置
  void setProperties(Properties props);
  // 获取数据源 DataSource 对象
  DataSource getDataSource();
}

UnpooledDataSourceFactory

UnpooledDataSourceFactory é usado principalmente para criar o objeto UnpooledDataSource, ele inicializará o objeto UnpooledDataSource no método de construção e completará a configuração do objeto UnpooledDataSource no método setProperties


public class UnpooledDataSourceFactory implements DataSourceFactory {
  // 数据库驱动前缀
  private static final String DRIVER_PROPERTY_PREFIX = "driver.";
  private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();
  // 对应的数据源,即 UnpooledDataSource
  protected DataSource dataSource;

  public UnpooledDataSourceFactory() {
    this.dataSource = new UnpooledDataSource();
  }
  // 对数据源 UnpooledDataSource 进行配置
  @Override
  public void setProperties(Properties properties) {
    Properties driverProperties = new Properties();
    // 创建 DataSource 相应的 MetaObject 
    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
    // 遍历 properties 集合,该集合中存放了数据源需要的信息
    for (Object key : properties.keySet()) {
      String propertyName = (String) key;
      // 以 "driver." 开头的配置项是对 DataSource 的配置,记录到 driverProperties  中
      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
        String value = properties.getProperty(propertyName);
        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
      } else if (metaDataSource.hasSetter(propertyName)) { // 该属性是否有 set 方法
        // 获取对应的属性值
        String value = (String) properties.get(propertyName);
        // 根据属性类型进行类型的转换,主要是 Integer, Long, Boolean 三种类型的转换
        Object convertedValue = convertValue(metaDataSource, propertyName, value);
        // 设置DataSource 的相关属性值
        metaDataSource.setValue(propertyName, convertedValue);
      } else {
        throw new DataSourceException("Unknown DataSource property: " + propertyName);
      }
    }
    // 设置 DataSource.driverProerties 属性值
    if (driverProperties.size() > 0) {
      metaDataSource.setValue("driverProperties", driverProperties);
    }
  }
  // 返回数据源
  @Override
  public DataSource getDataSource() {
    return dataSource;
  }
  // 类型转
  private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
    Object convertedValue = value;
    Class<?> targetType = metaDataSource.getSetterType(propertyName);
    if (targetType == Integer.class || targetType == int.class) {
      convertedValue = Integer.valueOf(value);
    } else if (targetType == Long.class || targetType == long.class) {
      convertedValue = Long.valueOf(value);
    } else if (targetType == Boolean.class || targetType == boolean.class) {
      convertedValue = Boolean.valueOf(value);
    }
    return convertedValue;
  }
}

JndiDataSourceFactory depende do DataSource configurado pelo usuário no servidor JNDI, o que não é necessário aqui.

PooledDataSourceFactory

PooledDataSourceFactory é usado principalmente para criar objetos PooledDataSource. Ele herda a classe UnpooledDataSource. O método de configuração de parâmetros DataSource reutiliza o método setProperties em UnpooledDataSource, exceto que a fonte de dados retorna um objeto PooledDataSource.


public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }
}

Acima são as classes de fábrica usadas por Mybatis para criar a fonte de dados.Vamos ver a implementação principal da fonte de dados.

Realização da fonte de dados

UnpooledDataSource

UnpooledDataSource não usa o pool de conexão para criar uma conexão de banco de dados, cada vez que uma conexão de banco de dados é obtida, uma nova conexão é criada e retornada;


public class UnpooledDataSource implements DataSource {
  // 加载 Driver 类的类加载器
  private ClassLoader driverClassLoader;
  // 数据库连接驱动的相关配置
  private Properties driverProperties;
  // 缓存所有已注册的数据库连接驱动
  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();

  private String driver;
  private String url;
  private String username;
  private String password;
  // 是否自动提交
  private Boolean autoCommit;
  // 事物隔离级别
  private Integer defaultTransactionIsolationLevel;

  // 静态块,在初始化的时候,从 DriverManager 中获取所有的已注册的驱动信息,并缓存到该类的 registeredDrivers集合中
  static {
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
      Driver driver = drivers.nextElement();
      registeredDrivers.put(driver.getClass().getName(), driver);
    }
  }

  public UnpooledDataSource() {
  }

  public UnpooledDataSource(String driver, String url, String username, String password) {
    this.driver = driver;
    this.url = url;
    this.username = username;
    this.password = password;
  }
}

A seguir, vamos examinar o método para obter a conexão:


  // 获取一个新的数据库连接
  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return doGetConnection(username, password);
  }

  // 根据 properties 获取一个新的数据库连接
  private Connection doGetConnection(Properties properties) throws SQLException {
    // 初始化数据库驱动
    initializeDriver();
    // 通过 DriverManager 来获取一个数据库连接
    Connection connection = DriverManager.getConnection(url, properties);
    // 配置数据库连接的 autoCommit 和隔离级别
    configureConnection(connection);
    // 返回新连接
    return connection;
  }

  // 初始化数据库驱动
  private synchronized void initializeDriver() throws SQLException {
    // 如果当前的驱动还没有注册,则进行注册
    if (!registeredDrivers.containsKey(driver)) {
      Class<?> driverType;
      try {
        if (driverClassLoader != null) {
          driverType = Class.forName(driver, true, driverClassLoader);
        } else {
          driverType = Resources.classForName(driver);
        }
        // 创建驱动
        Driver driverInstance = (Driver)driverType.newInstance();
        // 向  JDBC 的 DriverManager 注册驱动
        DriverManager.registerDriver(new DriverProxy(driverInstance));
        // 向本类的 registeredDrivers 注册驱动
        registeredDrivers.put(driver, driverInstance);
      } catch (Exception e) {
        throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
      }
    }
  }

  // 设置数据库连接的 autoCommit 和隔离级别
  private void configureConnection(Connection conn) throws SQLException {
    if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
      conn.setAutoCommit(autoCommit);
    }
    if (defaultTransactionIsolationLevel != null) {
      conn.setTransactionIsolation(defaultTransactionIsolationLevel);
    }
  }

O código acima é a principal lógica de implementação da classe UnpooledDataSource. Cada vez que uma conexão é obtida, uma nova conexão é criada a partir do banco de dados para retornar, e porque a criação de uma conexão de banco de dados é uma operação demorada, e a conexão do banco de dados é um recurso muito precioso. Criar uma conexão para cada aquisição pode causar gargalos no sistema e diminuir a velocidade de resposta. Neste momento, um pool de conexão de banco de dados é necessário. Mybatis também fornece sua própria implementação de pool de conexão de banco de dados, que é a classe PooledDataSource.

Implementação de pool de conexão

PooledDataSource

PooledDataSource é uma classe mais complexa. Ela cria uma nova conexão de banco de dados usando UnpooledDataSource. Ele não gerencia objetos java.sql.Connection, mas, em vez disso, gerencia objetos PooledConnection. Encapsula os objetos de conexão de banco de dados reais e Seu objeto proxy; além disso, porque ele é um pool de conexão, ele também precisa gerenciar o estado do pool de conexão, como quantas conexões estão ociosas e quantas conexões podem ser criadas. Neste momento, uma classe é necessária para gerenciar os objetos do pool de conexão. Objeto PoolState; primeiro olhe para um diagrama UML de PooledDataSource:
Análise de código-fonte do pool de conexão de banco de dados Mybatis

PooledConnection

Vamos dar uma olhada na classe PooledConnection primeiro. Ela é usada principalmente para gerenciar conexões de banco de dados. É uma classe de proxy que implementa a interface InvocationHandler.


class PooledConnection implements InvocationHandler {
  // close 方法
  private static final String CLOSE = "close";
  // 记录当前的 PooledConnection 对象所在的 PooledDataSource 对象,
  // 该 PooledConnection 对象是从 PooledDataSource 对象中获取的,
  // 当调用 close 方法时会将 PooledConnection 放回该 PooledDataSource 中去
  private PooledDataSource dataSource;
  // 真正的数据库连接
  private Connection realConnection;
  // 数据库连接的代理对象
  private Connection proxyConnection;
  // 从连接池中取出该连接的时间戳
  private long checkoutTimestamp;
  // 该连接创建的时间戳
  private long createdTimestamp;
  // 该连接最后一次被使用的时间戳
  private long lastUsedTimestamp;
  // 用于标识该连接所在的连接池,由URL+username+password 计算出来的hash值
  private int connectionTypeCode;
  // 该连接是否有效
  private boolean valid;

  // 创建连接
  public PooledConnection(Connection connection, PooledDataSource dataSource) {
    this.hashCode = connection.hashCode();
    this.realConnection = connection;
    this.dataSource = dataSource;
    this.createdTimestamp = System.currentTimeMillis();
    this.lastUsedTimestamp = System.currentTimeMillis();
    this.valid = true;
    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
  }

  // 废弃该连接
  public void invalidate() {
    valid = false;
  }

  // 判断该连接是否有效,
  // 1.判断 valid 字段
  // 2.向数据库中发送检测测试的SQL,查看真正的连接还是否有效
  public boolean isValid() {
    return valid && realConnection != null && dataSource.pingConnection(this);
  }
 //     setter / getter  方法
}

Em seguida, observe o método invoke. Este método é a lógica de proxy real do objeto proxy proxyConnection. Ele fará o proxy do método close e verificará a conexão antes de chamar a conexão real.


  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    // 如果执行的方法是 close 方法,则会把当前连接放回到 连接池中去,供下次使用,而不是真正的关闭数据库连接
    if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
      dataSource.pushConnection(this);
      return null;
    } else {
      try {
        // 如果不是 close 方法,则 调用 真正的数据库连接执行
        if (!Object.class.equals(method.getDeclaringClass())) {
          // 执行之前,需要进行连接的检测
          checkConnection();
        }
        // 调用数据库真正的连接进行执行
        return method.invoke(realConnection, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
  }

PoolState

A classe PoolState é usada principalmente para gerenciar o estado do pool de conexão, como quais conexões estão ociosas, quais estão ativas e quantas conexões podem ser criadas. Esta classe define apenas algumas propriedades para controlar o estado do pool de conexão e não há nenhum método.


public class PoolState {
  // 该 PoolState 属于哪个 PooledDataSource 
  protected PooledDataSource dataSource;

  // 来用存放空闲的 pooledConnection 连接
  protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();

  // 用来存放活跃的 PooledConnection 连接
  protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();

  // 请求数据库连接的次数
  protected long requestCount = 0;
  // 获取连接的累计时间
  protected long accumulatedRequestTime = 0;
  // checkoutTime 表示从连接池中获取连接到归还连接的时间
  // accumulatedCheckoutTime 记录了所有连接的累计 checkoutTime 时长
  protected long accumulatedCheckoutTime = 0;
  // 连接超时的连接个数
  protected long claimedOverdueConnectionCount = 0;
  // 累计超时时间
  protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
  // 累计等待时间
  protected long accumulatedWaitTime = 0;
  // 等待次数
  protected long hadToWaitCount = 0;
  // 无效的连接数
  protected long badConnectionCount = 0;

  //  setter / getter  方法
}

PooledDataSource

PooledDataSource É um pool de conexão de banco de dados simples, sincronizado e seguro para thread. Depois de
saber que UnpooledDataSource é usado para criar novas conexões com o banco de dados, PooledConnection é usado para gerenciar conexões no pool de conexão e PoolState é usado para gerenciar o estado do pool de conexão. Uma lógica de PooledDataSource,
existem principalmente os seguintes métodos nesta classe:

  1. Método para obter popConnection de conexão de banco de dados
  2. Coloque a conexão de volta no método pushConnection do pool de conexão,
  3. O método pingConnection para verificar se a conexão do banco de dados é válida,
  4. Também existe o método forceCloseAll para fechar todas as conexões no pool de conexão. A
    seguir, vamos ver como esses métodos são implementados. Antes de examiná-lo, vamos dar uma olhada em alguns dos atributos definidos por esta classe:

public class PooledDataSource implements DataSource {

  // 连接池的状态
  private final PoolState state = new PoolState(this);

  // 用来创建真正的数据库连接对象
  private final UnpooledDataSource dataSource;

  // 最大活跃的连接数,默认为 10 
  protected int poolMaximumActiveConnections = 10;

  // 最大空闲连接数,默认为 5
  protected int poolMaximumIdleConnections = 5;

  // 最大获取连接的时长 
  protected int poolMaximumCheckoutTime = 20000;

  // 在无法获取到连接时,最大等待的时间
  protected int poolTimeToWait = 20000;

  // 在检测一个连接是否可用时,会向数据库发送一个测试 SQL 
  protected String poolPingQuery = "NO PING QUERY SET";
  // 是否允许发送测试 SQL
  protected boolean poolPingEnabled;

  // 当连接超过 poolPingConnectionsNotUsedFor 毫秒未使用时,会发送一次测试 SQL 语句,测试连接是否正常
  protected int poolPingConnectionsNotUsedFor;

  // 标志着当前的连接池,是 url+username+password 的 hash 值
  private int expectedConnectionTypeCode;

  // 创建连接池
  public PooledDataSource(String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driver, url, username, password);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

   // 生成 hash 值
   private int assembleConnectionTypeCode(String url, String username, String password) {
    return ("" + url + username + password).hashCode();
  }
  //   setter /  getter 方法
}

Obter conexão popConnection

A seguir
, vamos dar uma olhada na lógica de implementação de obtenção de conexões do pool de conexão do banco de dados: o método de obtenção de conexões do pool de conexão é implementado principalmente em popConnection. Vamos primeiro olhar um fluxograma dele:
Análise de código-fonte do pool de conexão de banco de dados Mybatis


 // 获取连接
  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return popConnection(username, password).getProxyConnection();
  }

  // 从连接池中获取连接
  private PooledConnection popConnection(String username, String password) throws SQLException {
    // 等待的个数
    boolean countedWait = false;
    // PooledConnection 对象
    PooledConnection conn = null;
    long t = System.currentTimeMillis();
    // 无效的连接个数
    int localBadConnectionCount = 0;

    while (conn == null) {
      synchronized (state) {
        // 检测是否还有空闲的连接
        if (!state.idleConnections.isEmpty()) {
          // 连接池中还有空闲的连接,则直接获取连接返回
          conn = state.idleConnections.remove(0);
        } else {
          // 连接池中已经没有空闲连接了
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            // 活跃的连接数没有达到最大值,则创建一个新的数据库连接
            conn = new PooledConnection(dataSource.getConnection(), this);
          } else {
            // 如果活跃的连接数已经达到允许的最大值了,则不能创建新的数据库连接
            // 获取最先创建的那个活跃的连接
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            // 检测该连接是否超时
            if (longestCheckoutTime > poolMaximumCheckoutTime) {
              // 如果该连接超时,则进行相应的统计
              state.claimedOverdueConnectionCount++;
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              state.accumulatedCheckoutTime += longestCheckoutTime;
              // 将超时连接移出 activeConnections 集合
              state.activeConnections.remove(oldestActiveConnection);
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                  // 如果超时未提交,则自动回滚
                  oldestActiveConnection.getRealConnection().rollback();
              }
              // 创建新的 PooledConnection 对象,但是真正的数据库连接并没有创建
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
              conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
              // 设置该超时的连接为无效
              oldestActiveConnection.invalidate();
            } else {
               // 如果无空闲连接,无法创建新的连接且无超时连接,则只能阻塞等待
              // Must wait
              try {
                if (!countedWait) {
                  state.hadToWaitCount++; // 等待次数
                  countedWait = true;
                }
                long wt = System.currentTimeMillis();
                // 阻塞等待
                state.wait(poolTimeToWait);
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
        // 已经获取到连接
        if (conn != null) {
          if (conn.isValid()) {
            // 如果连连接有效,事务未提交则回滚
            if (!conn.getRealConnection().getAutoCommit()) {
              conn.getRealConnection().rollback();
            }
            // 设置 PooledConnection 相关属性
            conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            conn.setCheckoutTimestamp(System.currentTimeMillis());
            conn.setLastUsedTimestamp(System.currentTimeMillis());
            // 把连接加入到活跃集合中去
            state.activeConnections.add(conn);
            state.requestCount++;
            state.accumulatedRequestTime += System.currentTimeMillis() - t;
          } else {
            // 无效连接
            state.badConnectionCount++;
            localBadConnectionCount++;
            conn = null;
          }
        }
      }
    }
    return conn;
  }

O acima é a lógica principal para obter a conexão do pool de conexão.

Liberar link pushConnection

Agora vamos ver quando o método close é executado, a conexão será colocada no pool de conexão para reutilização na próxima vez. O método de colocar a conexão no pool de conexão é o método pushConnection, que também é um método principal da classe PooledDataSource . Primeiro, olhe para seu fluxograma:

Análise de código-fonte do pool de conexão de banco de dados Mybatis


// 把不用的连接放入到连接池中
  protected void pushConnection(PooledConnection conn) throws SQLException {
    synchronized (state) {
      // 首先从活跃的集合中移除掉该连接
      state.activeConnections.remove(conn);
      // 检测连接是否有效
      if (conn.isValid()) {
         // 如果空闲连接数没有达到最大值,且 PooledConnection 为该连接池的连接
        if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
          // 累计 checkout 时长
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          // 事务回滚
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          // 为返还的连接创建新的 PooledConnection 对象
          PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
          // 把该连接添加的空闲链表中
          state.idleConnections.add(newConn);
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          // 设置该连接为无效状态
          conn.invalidate();
          // 唤醒阻塞等待的线程
          state.notifyAll();
        } else {
          // 如果空闲连接数已经达到最大值
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          // 则关闭真正的数据库链接
          conn.getRealConnection().close();
          // 设置该连接为无效状态
          conn.invalidate();
        }
      } else {
        // 无效连接个数加1
        state.badConnectionCount++;
      }
    }
  }

O código acima é para colocar conexões não utilizadas no pool de conexão para o próximo uso,

Nos dois métodos acima, o método isValid é chamado para verificar se a conexão está disponível. Além de verificar o campo válido, este método também chama o método pingConnection para tentar fazer com que o banco de dados execute a instrução SQL de teste, a fim de verificar se o objeto de conexão de banco de dados real ainda é normal Disponível.


  // 检测连接是否可用
  public boolean isValid() {
    return valid && realConnection != null && dataSource.pingConnection(this);
  }
  // 向数据库发送测试 SQL 来检测真正的数据库连接是否可用
  protected boolean pingConnection(PooledConnection conn) {
    // 结果
    boolean result = true;

    try { 
      // 检测真正的数据库连接是否已经关闭
      result = !conn.getRealConnection().isClosed();
    } catch (SQLException e) {    
      result = false;
    }
     // 如果真正的数据库连接还没关闭
    if (result) {
      // 是否执行测试 SQL 语句
      if (poolPingEnabled) {
         // 长时间(poolPingConnectionsNotUsedFor 指定的时长)未使用的连接,才需要ping操作来检测连接是否正常
        if (poolPingConnectionsNotUsedFor >= 0 && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
          try {
            // 发送测试 SQL 语句执行
            Connection realConn = conn.getRealConnection();
            Statement statement = realConn.createStatement();
            ResultSet rs = statement.executeQuery(poolPingQuery);
            rs.close();
            statement.close();
            if (!realConn.getAutoCommit()) {
              realConn.rollback();
            }
            result = true;
          } catch (Exception e) {
            try {
              conn.getRealConnection().close();
            } catch (Exception e2) {
            }
            result = false;
          }
        }
      }
    }
    return result;
  }

Além disso, ao modificar os campos correspondentes de PooledDataSource, como a URL do banco de dados, nome de usuário ou senha, você precisa fechar todas as conexões no pool de conexão e, em seguida, reinicializar ao obter uma conexão. O método para fechar todas as conexões no pool de conexão é forceCloseAll.

Fechar conexão forceCloseAll


  public void forceCloseAll() {
    synchronized (state) {
      expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
      // 处理活跃的连接
      for (int i = state.activeConnections.size(); i > 0; i--) {
        try {
          PooledConnection conn = state.activeConnections.remove(i - 1);
          // 设置连接为无效状态
          conn.invalidate();
          // 获取数据库真正的连接
          Connection realConn = conn.getRealConnection();
          // 事物回滚
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          // 关闭数据库连接
          realConn.close();
        } catch (Exception e) {
          // ignore
        }
      }
      // 处理空闲的连接
      for (int i = state.idleConnections.size(); i > 0; i--) {
        try {
          PooledConnection conn = state.idleConnections.remove(i - 1);
          // 设置为无效状态
          conn.invalidate();
          Connection realConn = conn.getRealConnection();
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          realConn.close();
        } catch (Exception e) {

        }
      }
    }
  }

Resumindo

No pool de conexão, o número máximo de conexões e o número máximo de conexões livres no pool de conexão são mencionados.Há julgamentos na obtenção de conexões e colocação de conexões no pool de conexão.

  1. Obter conexão: primeiro obtenha-a do pool de conexão. Se não houver conexões livres no pool de conexão, ele julgará se o número atual de conexões ativas atingiu o valor máximo permitido. Caso contrário, você também pode criar uma nova conexão. Em seguida, coloque-o no conjunto ativo para uso, se o ativo atual atingiu o valor máximo, ele irá bloquear.

  2. Retorne a conexão ao pool de conexão. Ao retornar a conexão, faça um julgamento. Se o número de conexões livres atingir o valor máximo permitido, a conexão real do banco de dados será fechada diretamente, caso contrário, a conexão será colocada no conjunto gratuito por próximo uso.

Na fonte de dados Mybatis, a lógica do código principal ainda está na classe de pool de conexão PooledDataSource. Para que o método popConnection obtenha a conexão e o método pushConnection para retornar a conexão, você precisa combinar a figura acima para vê-la claramente.

Acho que você gosta

Origin blog.51cto.com/15077536/2608575
Recomendado
Clasificación