Mybatisデータベース接続プールのソースコード分析

Mybatisデータベース接続プールのソースコード分析

TSMYKJavaテクノロジープログラミング

この記事の最初のアドレスは個人のブログアドレスです:https
//my.oschina.net/mengyuankan/blog/2664784

この記事では、以下の側面から紹介します

  1. 関連記事
  2. 序文
  3. クラス図
  4. ファクトリクラスの実装
  5. データベース接続の実現
  6. 接続プールの実装
  7. 接続プールから接続を取得する(フローチャート)
  8. 接続を接続プールに入れます(フローチャート)

    関連記事

Mybatis解析構成ファイルのソースコード分析
Mybatis型変換のソースコード分析

序文

Mybatisを使用する場合、データベース接続は通常、C3P0、DBCP、Druidなどのサードパーティのデータソースコンポーネントを使用します。実際、Mybatisには、データベースに接続でき、接続機能を備えた独自のデータソース実装もあります。プール。Mybatisによって実装されたデータソースと接続プールの実装原則を見てください。

クラス図

Mybatisデータソースの実現は、主にデータソースパッケージの下にあります。
Mybatisデータベース接続プールのソースコード分析

共通のデータソースコンポーネントはすべてJavax.sql.DataSourceインターフェースを実装し、Mybatisもこのインターフェースを実装し、UnpooledDataSourceとPooledDataSourceの2つの実装クラスを提供します。1つは接続プールを使用し、もう1つは接続プールを使用しません。さらに、これら2つのクラスでは、 Mybatisには、オブジェクトを作成するための2つのファクトリクラスも用意されています。これは、ファクトリメソッドパターンのアプリケーションです。まず、それらのクラス図を見てみましょう。
Mybatisデータベース接続プールのソースコード分析

上記のクラスに関しては、PooledDataSourceとUnpooledDataSourceがデータソース実装のメインロジックです。コードはより複雑です。後で見てみましょう。次に、2つのファクトリクラスを見てみましょう。

DataSourceFactory

最初にDataSourceFactoryクラスを見てみましょう。このクラスは、JndiDataSourceFactoryおよびUnpooledDataSourceFactoryファクトリクラスの最上位インターフェイスです。以下に示すように、2つのメソッドのみが定義されています。


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

UnpooledDataSourceFactory

UnpooledDataSourceFactoryは、主にUnpooledDataSourceオブジェクトを作成するために使用され、構築メソッドでUnpooledDataSourceオブジェクトを初期化し、setPropertiesメソッドでUnpooledDataSourceオブジェクトの構成を完了します。


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は、JNDIサーバーでユーザーが構成したデータソースに依存しますが、ここでは必要ありません。

PooledDataSourceFactory

PooledDataSourceFactoryは、主にPooledDataSourceオブジェクトを作成するために使用され、UnpooledDataSourceクラスを継承します。DataSourceパラメーターを設定するメソッドは、データソースがPooledDataSourceオブジェクトを返すことを除いて、UnpooledDataSourceのsetPropertiesメソッドを再利用します。


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

上記は、Mybatisがデータソースを作成するために使用するファクトリクラスです。データソースの主な実装を見てみましょう。

データソースの実現

UnpooledDataSource

UnpooledDataSourceは、接続プールを使用してデータベース接続を作成しません。データベース接続が取得されるたびに、新しい接続が作成されて返されます。


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;
  }
}

次に、接続を取得する方法を見てみましょう。


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

上記のコードは、UnpooledDataSourceクラスの主要な実装ロジックです。接続が取得されるたびに、データベースから新しい接続が作成されて返されます。データベース接続の作成は時間のかかる操作であるため、データベース接続は非常に貴重なリソースです。取得ごとに1つの接続を作成すると、システムのボトルネックが発生し、応答速度が低下する可能性があります。現時点では、データベース接続プールが必要です。Mybatisは、PooledDataSourceクラスである独自のデータベース接続プール実装も提供します。

接続プールの実装

PooledDataSource

PooledDataSourceは、より複雑なクラスです。UnpooledDataSourceを使用して新しいデータベース接続を作成します。java.sql.Connectionオブジェクトを管理せず、代わりにPooledConnectionオブジェクトを管理します。さらに、実際のデータベース接続オブジェクトとそのプロキシオブジェクトをカプセル化します。は接続プールであるため、アイドル状態の接続の数や作成できる接続の数など、接続プールの状態も管理する必要があります。現時点では、接続プールオブジェクトを管理するためのクラスが必要です。 PoolStateオブジェクト;最初にPooledDataSourceのUML図を見てください。
Mybatisデータベース接続プールのソースコード分析

PooledConnection

最初にPooledConnectionクラスを見てみましょう。これは主にデータベース接続の管理に使用されます。これは、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  方法
}

次に、invokeメソッドを見てください。このメソッドはproxyConnectionプロキシオブジェクトの実際のプロキシロジックです。実際の接続を呼び出す前に、closeメソッドをプロキシし、接続を確認します。


  @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

PoolStateクラスは主に、アイドル状態の接続、アクティブな接続、作成できる接続の数など、接続プールの状態を管理するために使用されます。このクラスは、接続プールの状態を制御するための一部のプロパティのみを定義し、メソッドはありません。


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これは、単純で同期されたスレッドセーフなデータベース接続プールです
。UnpooledDataSourceがデータベースへの新しい接続の作成に使用されることを知った、PooledConnectionは接続プール内の接続の管理に使用され、PoolStateはの状態の管理に使用されます。接続プール。PooledDataSourceのロジック。
このクラスには主に次のメソッドがあります。

  1. データベース接続を取得するメソッドpopConnection
  2. 接続を接続プールメソッドpushConnectionに戻します。
  3. データベース接続が有効かどうかを確認するためのメソッドpingConnection、
  4. 接続プール内のすべての接続を閉じるためのメソッドforceCloseAllもあります。
    次に、これらのメソッドがどのように実装されているかを見てみましょう。それを見る前に、このクラスで定義されている属性のいくつかを見てみましょう。

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

接続を取得するpopConnection

次に
データベース接続プールから接続を取得する実装ロジックを見てみましょう。接続プールから接続を取得する方法は、主にpopConnectionに実装されています。まず、そのフローチャートを見てみましょう。
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;
  }

上記は、接続プールから接続を取得するための主なロジックです。

リンクpushConnectionを解放します

ここで、closeメソッドが実行されると、接続は次回再利用するために接続プールに入れられます。接続を接続プールに入れるメソッドは、pushConnectionメソッドです。これはPooledDataSourceクラスのメインメソッドでもあります。 。最初にそのフローチャートを見てください。

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++;
      }
    }
  }

上記のコードは、未使用の接続を次の使用のために接続プールに入れるためのものです。

上記の2つのメソッドでは、isValidメソッドを呼び出して、接続が使用可能かどうかを確認します。このメソッドは、有効なフィールドを確認するだけでなく、pingConnectionメソッドを呼び出して、データベースにテストSQLステートメントを実行させて確認します。実際のデータベース接続オブジェクトがまだ正常であるかどうか。


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

さらに、データベースのURL、ユーザー名、パスワードなど、PooledDataSourceの対応するフィールドを変更する場合は、接続プール内のすべての接続を閉じて、接続を取得したときに再初期化する必要があります。接続プール内のすべての接続を閉じる方法はforceCloseAllです。

接続を閉じる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) {

        }
      }
    }
  }

総括する

接続プールには、接続プール内の最大接続数と最大空き接続数が記載されています。接続の取得と接続プールへの接続には判断があります。

  1. 接続の取得:最初に接続プールから取得します。接続プールに空き接続がない場合は、アクティブな接続の現在の数が最大許容値に達しているかどうかを判断します。そうでない場合は、新しい接続を作成することもできます。次に、それをアクティブセットに入れて使用します。現在のアクティブが最大値に達すると、ブロックされます。

  2. 接続を接続プールに戻します。接続を返すときは、判断を下します。空き接続の数が最大許容値に達した場合、実際のデータベース接続は直接閉じられます。それ以外の場合、接続はの空きセットに配置されます。次の使用。

Mybatisデータソースでは、メインコードロジックはまだ接続プールクラスPooledDataSourceにあります。メソッドpopConnectionで接続を取得し、メソッドpushConnectionで接続を返すには、上の図を組み合わせて明確に表示する必要があります。

おすすめ

転載: blog.51cto.com/15077536/2608575
おすすめ