Análisis del código fuente del grupo de conexiones de la base de datos Mybatis

Análisis del código fuente del grupo de conexiones de la base de datos Mybatis

Programación de tecnología Java TSMYK

La primera dirección de este artículo es la dirección del blog personal:
https://my.oschina.net/mengyuankan/blog/2664784

Este artículo presentará los siguientes aspectos

  1. Artículos relacionados
  2. Prefacio
  3. Diagrama de clase
  4. Implementación de clase de fábrica
  5. Realización de conexión de base de datos
  6. Implementación de pool de conexiones
  7. Obtener una conexión del grupo de conexiones (diagrama de flujo)
  8. Coloque la conexión en el grupo de conexiones (diagrama de flujo)

    Artículos relacionados

Análisis de código fuente de archivos de configuración de análisis de
Mybatis Análisis de código fuente de conversión de tipo Mybatis

Prefacio

Cuando se usa Mybatis, la conexión a la base de datos generalmente usa componentes de fuente de datos de terceros, como C3P0, DBCP y Druid, etc. De hecho, Mybatis también tiene su propia implementación de fuente de datos, que puede conectarse a la base de datos y tiene la función de conexión. Pool Eche un vistazo a un principio de implementación de la fuente de datos y el pool de conexiones implementado por Mybatis.

Diagrama de clase

La realización de la fuente de datos Mybatis se encuentra principalmente bajo el paquete de fuente de datos:
Análisis del código fuente del grupo de conexiones de la base de datos Mybatis

Todos nuestros componentes de fuentes de datos comunes implementan la interfaz Javax.sql.DataSource, Mybatis también implementa esta interfaz y proporciona dos clases de implementación UnpooledDataSource y PooledDataSource. Una usa el pool de conexiones y el otro no usa el pool de conexiones. Además, para estas dos clases, Mybatis también proporciona dos clases de fábrica para crear objetos, que es una aplicación del patrón del método de fábrica. Primero, veamos un diagrama de clases de ellos:
Análisis del código fuente del grupo de conexiones de la base de datos Mybatis

En cuanto a las clases mencionadas anteriormente, PooledDataSource y UnpooledDataSource son la lógica principal de la implementación de la fuente de datos. El código es más complicado. Veámoslo más adelante. Ahora echemos un vistazo a las dos clases de fábrica.

DataSourceFactory

Primero echemos un vistazo a la clase DataSourceFactory. Esta clase es la interfaz de nivel superior de las clases de fábrica JndiDataSourceFactory y UnpooledDataSourceFactory. Solo se definen dos métodos, como se muestra a continuación:


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

UnpooledDataSourceFactory

UnpooledDataSourceFactory se utiliza principalmente para crear el objeto UnpooledDataSource, inicializará el objeto UnpooledDataSource en el método de construcción y completará la configuración del objeto UnpooledDataSource en el 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 se basa en el DataSource configurado por el usuario en el servidor JNDI, que no es necesario aquí.

PooledDataSourceFactory

PooledDataSourceFactory se utiliza principalmente para crear objetos PooledDataSource. Hereda la clase UnpooledDataSource. El método para configurar los parámetros de DataSource reutiliza el método setProperties en UnpooledDataSource, excepto que la fuente de datos devuelve un objeto PooledDataSource.


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

Las anteriores son las clases de fábrica utilizadas por Mybatis para crear la fuente de datos. Veamos la implementación principal de la fuente de datos.

Realización de fuentes de datos

UnpooledDataSource

UnpooledDataSource no utiliza el grupo de conexiones para crear una conexión de base de datos, cada vez que se obtiene una conexión de base de datos, se crea y devuelve una nueva conexión;


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 continuación, veamos el método para obtener la conexión:


  // 获取一个新的数据库连接
  @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);
    }
  }

El código anterior es la lógica de implementación principal de la clase UnpooledDataSource. Cada vez que se obtiene una conexión, se crea una nueva conexión desde la base de datos para regresar, y debido a que la creación de una conexión a la base de datos es una operación que requiere mucho tiempo, y la conexión a la base de datos es un recurso muy valioso. Crear una conexión para cada adquisición puede causar cuellos de botella en el sistema y ralentizar la velocidad de respuesta. En este momento, se necesita un grupo de conexiones de base de datos. Mybatis también proporciona su propia implementación de grupo de conexiones de base de datos, que es la clase PooledDataSource.

Implementación de pool de conexiones

PooledDataSource

PooledDataSource es una clase más compleja. Crea una nueva conexión de base de datos utilizando UnpooledDataSource. No administra objetos java.sql.Connection, sino que administra objetos PooledConnection. Encapsula los objetos de conexión de base de datos reales y su objeto proxy; además, porque es un grupo de conexiones, también necesita administrar el estado del grupo de conexiones, como cuántas conexiones están inactivas y cuántas conexiones se pueden crear. En este momento, se necesita una clase para administrar los objetos del grupo de conexiones. Esa es la Objeto PoolState; primer vistazo a un diagrama UML de PooledDataSource:
Análisis del código fuente del grupo de conexiones de la base de datos Mybatis

PooledConnection

Primero echemos un vistazo a la clase PooledConnection. Se usa principalmente para administrar conexiones de bases de datos. Es una clase de proxy que implementa la interfaz 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  方法
}

A continuación, observe el método de invocación. Este método es la lógica de proxy real del objeto proxy proxyConnection. Aprovechará el método de cierre y comprobará la conexión antes de llamar a la conexión 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

La clase PoolState se usa principalmente para administrar el estado del grupo de conexiones, como qué conexiones están inactivas, cuáles están activas y cuántas conexiones se pueden crear. Esta clase solo define algunas propiedades para controlar el estado del grupo de conexiones y no hay ningún 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 Es un grupo de conexiones de base de datos simple, sincronizado y seguro para subprocesos. Después de
saber que UnpooledDataSource se usa para crear nuevas conexiones a la base de datos, PooledConnection se usa para administrar las conexiones en el grupo de conexiones y PoolState se usa para administrar el estado del pool de conexiones. Una lógica de PooledDataSource,
existen principalmente los siguientes métodos en esta clase:

  1. Método para obtener la conexión a la base de datos popConnection
  2. Vuelva a poner la conexión en el método pushConnection del grupo de conexiones
  3. El método pingConnection para verificar si la conexión a la base de datos es válida,
  4. También existe el método forceCloseAll para cerrar todas las conexiones en el grupo de conexiones.
    A continuación, veamos cómo se implementan estos métodos. Antes de verlo, echemos un vistazo a algunos de los atributos definidos por esta clase:

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 方法
}

Obtener conexión popConnection

A continuación
, veamos la lógica de implementación para obtener conexiones del grupo de conexiones de la base de datos: el método para obtener conexiones del grupo de conexiones se implementa principalmente en popConnection. Veamos primero un diagrama de flujo del mismo:
Análisis del código fuente del grupo de conexiones de la base de datos 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;
  }

Lo anterior es la lógica principal para obtener la conexión del grupo de conexiones.

Enlace de liberación pushConnection

Ahora veamos cuándo se ejecuta el método de cierre, la conexión se colocará en el grupo de conexiones para su reutilización la próxima vez. El método para colocar la conexión en el grupo de conexiones es el método pushConnection, que también es un método principal de la clase PooledDataSource . Primero mira su diagrama de flujo:

Análisis del código fuente del grupo de conexiones de la base de datos 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++;
      }
    }
  }

El código anterior es para poner las conexiones no utilizadas en el grupo de conexiones para el próximo uso,

En los dos métodos anteriores, se llama al método isValid para verificar si la conexión está disponible. Además de verificar el campo válido, este método también llama al método pingConnection para intentar que la base de datos ejecute la declaración SQL de prueba, a fin de verificar si el objeto de conexión de base de datos real sigue siendo normal Disponible.


  // 检测连接是否可用
  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;
  }

Además, al modificar los campos correspondientes de PooledDataSource, como la URL de la base de datos, el nombre de usuario o la contraseña, debe cerrar todas las conexiones en el grupo de conexiones y luego reinicializar cuando obtenga una conexión. El método para cerrar todas las conexiones en el grupo de conexiones es forceCloseAll.

Cerrar la fuerza de conexiónCerrarTodo


  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) {

        }
      }
    }
  }

para resumir

En el grupo de conexiones, se mencionan el número máximo de conexiones y el número máximo de conexiones libres en el grupo de conexiones Hay juicios para obtener conexiones y poner conexiones en el grupo de conexiones.

  1. Obtener conexión: primero obténgalo del grupo de conexiones. Si no hay conexiones libres en el grupo de conexiones, juzgará si el número actual de conexiones activas ha alcanzado el valor máximo permitido. De lo contrario, también puede crear una nueva conexión. Luego póngalo en el conjunto activo para su uso, si el activo actual ha alcanzado el valor máximo, se bloqueará.

  2. Devuelva la conexión al grupo de conexiones. Cuando devuelva la conexión, haga un juicio. Si el número de conexiones libres ha alcanzado el valor máximo permitido, la conexión de la base de datos real se cerrará directamente; de ​​lo contrario, la conexión se colocará en el conjunto libre para próximo uso.

En la fuente de datos Mybatis, la lógica del código principal todavía está en la clase PooledDataSource del grupo de conexiones. Para que el método popConnection obtenga la conexión y el método pushConnection devuelva la conexión, debe combinar la figura anterior para verla claramente.

Supongo que te gusta

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