Java学习之数据库连接池&DBCP&C3P0

一、数据库连接池

出现缘由:数据库的连接对象创建工作,比较消耗性能。

工作原理:在内存中开辟一块空间(集合),并放置多个连接对象。需要连接的话,直接从内存空间中获取。不需要创建连接对象。使用完毕,归还连接对象。确保连接对象能循环利用。

二、自定义数据库连接池

通过数据库连接池工作原理,自定义数据库连接池需要以下几步:

①、内存中开辟空间

②、创建数据库连接对象,并放置到内存中

③、定义获取数据库连接对象的方法

④、定义释放(归还)数据库连接对象的方法

代码实现:

/**
* @Title: MyDataSource
* @Description: sun 公司提供数据库连接池规范是以 DataSource 命名的
* @author: marw
* @date 2020/03/26 16:16:58
*/
public class MyDataSource implements DataSource {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">①、开辟空间</span>
List&lt;Connection&gt; list=<span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;Connection&gt;<span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * ②、创建数据库连接对象,并放置到内存中
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> MyDataSource() {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; 10; i++<span style="color: rgba(0, 0, 0, 1)">) {
    Connection connection </span>=<span style="color: rgba(0, 0, 0, 1)"> JDBCUtil.getConnection();
    list.add(connection);
}
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * ③、 sun公司 提供数据库连接池规范中包含了获取数据库连接对象的方法
 * 只需要重写此方法
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> Connection getConnection() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SQLException {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">内存扩容</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (list.size() == 0<span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; 3; i++<span style="color: rgba(0, 0, 0, 1)">) {
    Connection connection </span>=<span style="color: rgba(0, 0, 0, 1)"> JDBCUtil.getConnection();
    list.add(connection);
    }
}
</span><span style="color: rgba(255, 0, 0, 1)">// 移除集合中第一个元素(数据库连接对象),并获取移除的元素</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> list.remove(0<span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * ④、释放(归还)数据库连接对象
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Liberate(Connection connection) {
list.add(connection);
}

... ...

}

使用自定义数据库连接池

public class TestPool {
    @Test
    public void testPool() {
    Connection connection=null;
    PreparedStatement ps =null;
    MyDataSource dataSource=null;
    try {
        MyDataSource dataSource=new MyDataSource();
        connection=dataSource.getConnection();
    String sql</span>="insert into account values(?,?)"<span style="color: rgba(0, 0, 0, 1)">;
    ps </span>=<span style="color: rgba(0, 0, 0, 1)">connection.prepareStatement(sql);
    ps.setString(</span>1, "qian"<span style="color: rgba(0, 0, 0, 1)">);
    ps.setInt(</span>2, 1000<span style="color: rgba(0, 0, 0, 1)">);
    ps.executeUpdate();
    
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) {
    
    e.printStackTrace();
}
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)"> {<br>      <span style="color: rgba(255, 0, 0, 1)"><span data-mce-="">dataSource</span>.Liberate(connection);//释放连接对象</span>
    JDBCUtil.release(connection, ps);
}
}

}

使用自定义数据库连接池,有以下几个问题

①、每次使用都需要自己创建新的连接池对象,那样的话每一次访问都会创建 10 连接池对象,这个需要使用单例解决

②、无法面向接口编程,Liberate 方法,不想自定义方法可以重写 Connection 接口的 close 方法,让它实现归还连接对象。

 想要扩展 Connection 接口的 close 方法

  Ⅰ、直接修改方法

  Ⅱ、继承,必须知道 Connection 接口的实现类

  Ⅲ、装饰模式

使用装饰模式代码:

public class ConnectionWrap implements Connection {
    Connection connection=null;
    List<Connection> list=null;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> ConnectionWrap(Connection connection,List&lt;Connection&gt;<span style="color: rgba(0, 0, 0, 1)"> list) {
</span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.connection=<span style="color: rgba(0, 0, 0, 1)">connection;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.list=<span style="color: rgba(0, 0, 0, 1)">list;
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> close() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SQLException {
<span style="color: rgba(255, 0, 0, 1)">list.add(connection</span>);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> PreparedStatement prepareStatement(String sql) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SQLException {

</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> connection.prepareStatement(sql);
}

... ...

}

使用装饰模式对象

public class MyDataSource implements DataSource {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">①、开辟空间</span>
List&lt;Connection&gt; list=<span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;Connection&gt;<span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(255, 0, 0, 1)">/* 单例 start */</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> MyDataSource dataSource=<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MyDataSource();
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * ②、创建数据库连接对象,并放置到内存中
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> MyDataSource() {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; 10; i++<span style="color: rgba(0, 0, 0, 1)">) {
    Connection connection </span>=<span style="color: rgba(0, 0, 0, 1)"> JDBCUtil.getConnection();
    list.add(connection);
}
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> MyDataSource getInstance() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> dataSource;
}
</span><span style="color: rgba(255, 0, 0, 1)">/* 单例 end */</span>

<span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * ③、 sun公司 提供数据库连接池规范中包含了获取数据库连接对象的方法
 * 只需要重写此方法
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> Connection getConnection() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SQLException {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">内存扩容</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (list.size() == 0<span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; 3; i++<span style="color: rgba(0, 0, 0, 1)">) {
    Connection connection </span>=<span style="color: rgba(0, 0, 0, 1)"> JDBCUtil.getConnection();
    list.add(connection);
    }
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 移除集合中第一个元素(数据库连接对象),并获取移除的元素</span>
Connection conn=list.remove(0<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"><span style="color: rgba(255, 0, 0, 1)"> 装饰模式</span> 返回Connection对象,进行装饰,使其对象的close方法,有归还功能</span>
Connection connection = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConnectionWrap(conn, list);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> connection;
}

... ...

}

测试代码

public class TestPool {
    @Test
    public void testPool() {
    Connection connection=null;
    PreparedStatement ps =null;
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
    DataSource dataSource</span>=<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)">MyDataSource.getInstance();</span>
    connection</span>=<span style="color: rgba(0, 0, 0, 1)">dataSource.getConnection();
    
    String sql</span>="insert into account values(?,?)"<span style="color: rgba(0, 0, 0, 1)">;
    ps </span>=<span style="color: rgba(0, 0, 0, 1)">connection.prepareStatement(sql);
    ps.setString(</span>1, "qian"<span style="color: rgba(0, 0, 0, 1)">);
    ps.setInt(</span>2, 1000<span style="color: rgba(0, 0, 0, 1)">);
    ps.executeUpdate();
    
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) {
    
    e.printStackTrace();
}
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)"> {
    JDBCUtil.release(connection, ps);
}
}

}

三、开源连接池

 Ⅰ、DBCP 使用

①、下载 jar 文件(下载两个 jar 文件:commons-dbcp 和 commons-pool)

  commons-dbcp 下载:http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi

  commons-pool 下载:http://commons.apache.org/proper/commons-pool/download_pool.cgi

  commons-logging 下载:http://commons.apache.org/proper/commons-logging/download_logging.cgi

②、导入 jar 文件

  根据解压后的文件找到对应 jar 文件,复制到工程的 lib 文件中,Java 工程需(右击 jar 文件 =>Build Path => Add to Build Path)

  

 

   错误信息:java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory,导入 commons-logging-1.2.jar 辅助类包即可

 ③、不使用配置文件

 1 @Test
 2 public void testDBCP01() {
 3     Connection conn = null;
 4     PreparedStatement ps = null;
 5     try {
 6 
 7         // 1. 构建数据源对象
 8         BasicDataSource dataSource = new BasicDataSource();
 9         // 连的是什么类型的数据库, 访问的是哪个数据库 , 用户名, 密码。。
10         // jdbc:mysql://localhost/bank 主协议:子协议 ://本地 / 数据库
11         dataSource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
12         dataSource.setUrl("jdbc:sqlserver://localhost:1433;databaseName=Bank;");
13         dataSource.setUsername("sa");
14         dataSource.setPassword("XXXX");
15 
16         // 2. 得到连接对象
17         conn = dataSource.getConnection();
18         String sql = "insert into account values(? , ?)";
19         ps = conn.prepareStatement(sql);
20         ps.setString(1, "admin");
21         ps.setInt(2, 1000);
22 
23         ps.executeUpdate();
24 
25     } catch (SQLException e) {
26         e.printStackTrace();
27     } finally {
28         JDBCUtil.release(conn, ps);
29     }
30 }

④、使用配置文件

##dbcpconfig.properties 文件内容

 1 #连接设置
 2 driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
 3 url=jdbc:sqlserver://localhost:1433;databaseName=Bank;
 4 username=sa
 5 password=XXXX
 6 
 7 #<!-- 初始化连接 -->
 8 initialSize=10
 9 
10 #最大连接数量
11 maxActive=50
12 
13 #<!-- 最大空闲连接 -->
14 maxIdle=20
15 
16 #<!-- 最小空闲连接 -->
17 minIdle=5
18 
19 #<!-- 超时等待时间以毫秒为单位 6000 毫秒 /1000 等于 60 秒 -->
20 maxWait=60000
21 
22 
23 #JDBC 驱动建立连接时附带的连接属性属性的格式必须为这样:[ 属性名 =property;] 
24 #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
25 connectionProperties=useUnicode=true;characterEncoding=gbk
26 
27 #指定由连接池所创建的连接的自动提交(auto-commit)状态。
28 defaultAutoCommit=true
29 
30 #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
31 #可用值为下列之一:(详情可见 javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
32 defaultTransactionIsolation=READ_UNCOMMITTED

## 将 dbcpconfig.properties 文件放到 src 目录下

  

 1 @Test
 2 public void testDBCP() {
 3     Connection conn = null;
 4     PreparedStatement ps = null;
 5     try {
 6 
 7         BasicDataSourceFactory factory = new BasicDataSourceFactory();
 8         Properties properties = new Properties();
 9         InputStream is =this.getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties");
10         properties.load(is);
11         DataSource dataSource = factory.createDataSource(properties);
12 
13         // 2. 得到连接对象
14         conn = dataSource.getConnection();
15         String sql = "insert into account values(? , ?)";
16         ps = conn.prepareStatement(sql);
17         ps.setString(1, "liangcw");
18         ps.setInt(2, 100);
19 
20         ps.executeUpdate();
21 
22     } catch (Exception e) {
23         e.printStackTrace();
24     } finally {
25         JDBCUtil.release(conn, ps);
26     }
27 }

Ⅱ、C3P0 使用

①、下载 jar

  C3P0 下载:https://sourceforge.net/projects/c3p0/

②、导入 jar 文件

  

  错误信息:java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector,把上图中的 mchange-commons-java-0.2.19.jar 文件导入项目中

③、不使用配置文件  

 1 @Test
 2 public void testC3P0() {
 3     Connection conn = null;
 4     PreparedStatement ps = null;
 5     try {
 6         //1. 创建 datasource
 7         ComboPooledDataSource dataSource = new ComboPooledDataSource();
 8         //2. 设置连接数据的信息
 9         dataSource.setDriverClass("com.microsoft.sqlserver.jdbc.SQLServerDriver");
10         dataSource.setJdbcUrl("jdbc:sqlserver://localhost:1433;databaseName=Bank;");
11         dataSource.setUser("sa");
12         dataSource.setPassword("AAA@111");
13         
14         //2. 得到连接对象
15         conn = dataSource.getConnection();
16         String sql = "insert into account values(? , ?)";
17         ps = conn.prepareStatement(sql);
18         ps.setString(1, "admin1");
19         ps.setInt(2, 103200);
20         
21         ps.executeUpdate();
22         
23     } catch (Exception e) {
24         e.printStackTrace();
25     }finally {
26         JDBCUtil.release(conn, ps);
27     }
28 }

④、使用配置文件

## 创建 c3p0-config.xml 文件,文件内容如下

 1 <c3p0-config>
 2   <!-- ComboPooledDataSource dataSource = new ComboPooledDataSource(); 
 3        对应 default-config
 4            默认数据库连接
 5   -->
 6   <default-config>
 7     <property name="driverClass">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
 8     <property name="jdbcUrl">jdbc:sqlserver://localhost:1433;databaseName=Bank;</property>
 9     <property name="user">sa</property>
10     <property name="password">AAA@111</property>
11     
12     <property name="initialPoolSize">10</property>
13     <property name="maxIdleTime">30</property>
14     <property name="maxPoolSize">100</property>
15     <property name="minPoolSize">10</property>
16     <property name="maxStatements">200</property>
17   </default-config>
18   <!-- ComboPooledDataSource dataSource = new ComboPooledDataSource("intergalactoApp:); 
19        对应 named-config
20            指定数据库连接
21   -->
22   <named-config name="intergalactoApp"> 
23     <property name="driverClass">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
24     <property name="jdbcUrl">jdbc:sqlserver://localhost:1433;databaseName=Bank;</property>
25     <property name="user">sa</property>
26     <property name="password">AAA@111</property>
27     
28     <property name="initialPoolSize">10</property>
29     <property name="maxIdleTime">30</property>
30     <property name="maxPoolSize">100</property>
31     <property name="minPoolSize">10</property>
32     <property name="maxStatements">200</property>
33   </named-config>
34 </c3p0-config>

## 将 c3p0-config.xml 文件放到 src 目录下

  

 1 @Test
 2 public void testC3P0() {
 3     Connection conn = null;
 4     PreparedStatement ps = null;
 5     try {
 6         //1. 创建 datasource
 7         ComboPooledDataSource dataSource = new ComboPooledDataSource();
 8                 
 9         //2. 得到连接对象
10         conn = dataSource.getConnection();
11         String sql = "insert into account values(? , ?)";
12         ps = conn.prepareStatement(sql);
13         ps.setString(1, "admin2");
14         ps.setInt(2, 103200);
15         
16         ps.executeUpdate();
17         
18     } catch (Exception e) {
19         e.printStackTrace();
20     }finally {
21         JDBCUtil.release(conn, ps);
22     }
23 }

推荐使用 C3P0 数据库连接池。

自定义 JDBCUtil 使用 C3P0 改造

 1 public class JDBCUtil {
 2     static ComboPooledDataSource dataSource=null;
 3     static {
 4     dataSource=new ComboPooledDataSource();
 5     }
 6 
 7     public static Connection getConnection() {
 8     Connection connection = null;
 9     try {
10         connection = dataSource.getConnection();
11     } catch (SQLException e) {
12 
13         e.printStackTrace();
14     }
15     return connection;
16     }
17 
18     public static void release(Connection conn, Statement st, ResultSet rs) {
19     closeResultSet(rs);
20     closeStatement(st);
21     closeConnection(conn);
22     }
23     
24     public static void release(Connection conn, Statement st) {
25 
26     closeStatement(st);
27     closeConnection(conn);
28     }
29     private static void closeResultSet(ResultSet rs) {
30     if (rs != null) {
31         try {
32         rs.close();
33         } catch (SQLException e) {
34 
35         e.printStackTrace();
36         }
37         rs = null;
38     }
39     }
40 
41     private static void closeStatement(Statement st) {
42     if (st != null) {
43         try {
44         st.close();
45         } catch (SQLException e) {
46 
47         e.printStackTrace();
48         }
49         st = null;
50     }
51     }
52 
53     private static void closeConnection(Connection conn) {
54     if (conn != null) {
55         try {
56         conn.close();
57         } catch (SQLException e) {
58 
59         e.printStackTrace();
60         }
61         conn = null;
62     }
63     }
64 }
View Code