# 数据库连接池

# 基本概念

与数据库建立连接(Connection)和断开连接是有时间开销的,而且在某一个时间点『同时』连接到数据库的客户端可能并没有想象中那么多,这就意味着数据库连接(Connection)对象是可以『重用』的。

数据库连接池的概念就是,预先准备好若干个与数据库建立好的连接,未来谁需要使用,就直接获取一个连接,在使用完毕后,再将该连接『还给』数据库连接池(而非真正断开连接)。如此就提高程序性能。

使用它们,对于 Connection 对象的管理工作,就完全转交到了它们的手里,Connection 对象不再由我们创建和销毁。

现在常见的数据库连接池有:DBCP2、C3P0、Druid 和 HikariCP(以出现先后顺序排序)。建议使用 Druid 和 HikariCP,原因见最后。

# 选择哪个数据库连接池

  • DBCP2 是 Appache 基金会下的项目,是最早出现的数据库连接池 DBCP 的第二个版本。

  • C3P0 最早出现时是作为 Hibernate 框架的默认数据库连接池而进入市场。

  • Druid 是阿里巴巴公司开源的一款数据库连接池,其特点在于有丰富的附加功能。

  • HikariCP 相较而言比较新,它最近两年才出现,据称是速度最快的数据库连接池。最近更是被 Spring 设置为默认数据库连接池。另外,Driud 的附加功能,HikariCP 基本也有。

# 不选择 C3P0 的原因:

  • C3P0 的 Connection 是异步释放。这个特性会导致释放的在某些情况下 Connection 实际上 still in use ,并未真正释放掉,从而导致连接池中的 Connection 耗完,等待状况。

  • Hibernate 现在对所有数据库连接池一视同仁,官方不再指定『默认』数据库连接池。因此 C3P0 就失去了『官方』光环。

  • C3P0 最后一次更新是在 2015 年,和最近蓬勃发展的 Druid 和 HikariCP ,以及持续更新的 DBCP2 相比,C3P0 显得不是那么“欣欣向荣”。

# 不选择 DBCP2 的原因:

相较于 Druid 和 HikariCP,DBCP2 没有什么特色功能/卖点。基本上属于 能用,没毛病 的情况,地位显得略有尴尬。

# Druid

Druid 阿里巴巴公司开源的一款数据库连接池。在功能、性能、扩展性方面,都超过它的先辈。

虽然晚于 Druid 的 HikariCP 数据库对外宣称是性能最快的数据库连接池,而且 Druid 方面也并未对此说法作出反驳。但一般看法是 HikariCP 数据库的性能对于 Druid 不具备压倒性优势。

# 简单使用

  • pom.xml

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version> <!-- 8.0.21 -->
    </dependency>
    
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.20</version>
    </dependency>
    

有两种方式创建 Druid 数据库:通过 DruidDataSource 构造器,或者使用 DruidDataSourceFactory 工厂类。

  • 使用 DruidDataSource 构造器

    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/scott?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai");
    dataSource.setUsername("******");
    dataSource.setPassword("******");
    
    DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
    Connection connection = dataSource.getConnection();
    
    System.out.println(connection == null ? "not connected" : "connected");
    
  • 使用 DruidDataSourceFactory 工厂类

    Properties properties = new Properties();
    properties.setProperty("driver", "com.mysql.jdbc.Driver");
    properties.setProperty("url", "jdbc:mysql://127.0.0.1:3306/scott?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false");
    properties.setProperty("username", "******");
    properties.setProperty("password", "******");
    
    DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
    Connection connection = dataSource.getConnection();
    
    System.out.println(connection == null ? "not connected" : "connected");
    

# 结合配置文件使用

配置文件:

  • jdbc.properties

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/scott?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username=root
    password=123456
    

从配置文件中加载配置信息,并创建数据库连接池:

  • 代码:

    InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties");
    
    Properties properties = new Properties();
    properties.load(is);
    
    DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); // Druid
    Connection connection = dataSource.getConnection();
    
    System.out.println(connection == null ? "not connected" : "connected");
    

# Druid 中的工具类:JdbcUtils

Druid 的 com.alibaba.druid.util 包下有若干工具类,其中 JdbcUtils 中提供了我们常见 JDBC 操作的封装。

其中,

  • 增删改 SQL 操作,可以使用 .executeUpdate() 方法简化代码。

  • 查询 SQL 操作,可以使用 .executeQuery() 方法简化代码。

另外,.close() 方法 .printResultSet() 也很有实用价值。

# HikariCP

HikariCP 是几个常见数据库连接池中出现的最晚的一个。它口号是“快速、简单、可靠”,官方宣传是性能最快的数据库连接池(貌似也没有其它方对此表示异议)。

# 简单使用

  • pom.xml

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version> <!-- 8.0.21 -->
    </dependency>
    
    <dependency>
      <groupId>com.zaxxer</groupId>
      <artifactId>HikariCP</artifactId>
      <version>${hikaricp.version}</version> <!-- 3.2.0 -->
    </dependency>
    
  • HikariCP 专门定义了一个 HikariConfig 的配置类,用于创建 HikariCP 数据库连接池。

    HikariConfig config = new HikariConfig();
    config.setDriverClassName("com.mysql.cj.jdbc.Driver");
    config.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/******?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false");
    config.setUsername("******");
    config.setPassword("******");
    
    HikariDataSource dataSource = new HikariDataSource(config);
    
  • 当然,因为某种原因,你也可以强行用上 Properties 对象,Properties 对象获得 HikariConfig 对象。

    Properties properties = new Properties();
    
    properties.setProperty("driverClassName", "com.mysql.cj.jdbc.Driver");
    properties.setProperty("jdbcUrl", "jdbc:mysql://127.0.0.1:3306/******?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false");
    properties.setProperty("username", "******");
    properties.setProperty("password", "******");
    
    DataSource dataSource = new HikariDataSource(new HikariConfig(properties)); // Druid
    Connection connection = dataSource.getConnection();
    
    System.out.println(connection == null ? "not connected" : "connected");
    

这里需要注意的是,在配置数据库连接四大属性时,HikariCP 和其它数据库连接池用到了不同的单词。它使用到的是 driverClassNamejdbcUrl 。其它数据库连接池通常用的是 driverurl

# 配合配置文件使用

  • jdbc.properties

    driverClassName=com.mysql.cj.jdbc.Driver
    jdbcUrl=jdbc:mysql://127.0.0.1:3306/scott?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
    username=root
    password=123456
    
  • 代码使用示例

    InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties");
    Properties properties = new Properties();
    properties.load(is);
    
    DataSource dataSource = new HikariDataSource(new HikariConfig(properties));
    Connection connection = dataSource.getConnection();
    
    System.out.println(connection == null ? "not connected" : "connected");