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<Connection> list=<span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList<Connection><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 < 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 < 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<Connection><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<Connection> list=<span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList<Connection><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 < 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 < 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 }