Java学习之数据库连接池&DBCP&C3P0
一、数据库连接池
出现缘由:数据库的连接对象创建工作,比较消耗性能。
工作原理:在内存中开辟一块空间(集合),并放置多个连接对象。需要连接的话,直接从内存空间中获取。不需要创建连接对象。使用完毕,归还连接对象。确保连接对象能循环利用。
二、自定义数据库连接池
通过数据库连接池工作原理,自定义数据库连接池需要以下几步:
①、内存中开辟空间
②、创建数据库连接对象,并放置到内存中
③、定义获取数据库连接对象的方法
④、定义释放(归还)数据库连接对象的方法
代码实现:
/** * @Title: MyDataSource * @Description: sun公司提供数据库连接池规范是以DataSource命名的 * @author: marw * @date 2020/03/26 16:16:58 */ public class MyDataSource implements DataSource { //①、开辟空间 List<Connection> list=new ArrayList<Connection>(); /** * ②、创建数据库连接对象,并放置到内存中 */ public MyDataSource() { for (int i = 0; i < 10; i++) { Connection connection = JDBCUtil.getConnection(); list.add(connection); } } /** * ③、 sun公司 提供数据库连接池规范中包含了获取数据库连接对象的方法 * 只需要重写此方法 */ @Override public Connection getConnection() throws SQLException { //内存扩容 if (list.size() == 0) { for (int i = 0; i < 3; i++) { Connection connection = JDBCUtil.getConnection(); list.add(connection); } } // 移除集合中第一个元素(数据库连接对象),并获取移除的元素 return list.remove(0); } /** * ④、释放(归还)数据库连接对象 */ public void 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="insert into account values(?,?)"; ps =connection.prepareStatement(sql); ps.setString(1, "qian"); ps.setInt(2, 1000); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally {
dataSource.Liberate(connection);//释放连接对象 JDBCUtil.release(connection, ps); } } }
使用自定义数据库连接池,有以下几个问题
①、每次使用都需要自己创建新的连接池对象,那样的话每一次访问都会创建10连接池对象,这个需要使用单例解决
②、无法面向接口编程,Liberate方法,不想自定义方法可以重写Connection接口的close方法,让它实现归还连接对象。
想要扩展Connection接口的close方法
Ⅰ、直接修改方法
Ⅱ、继承,必须知道Connection接口的实现类
Ⅲ、装饰模式
使用装饰模式代码:
public class ConnectionWrap implements Connection { Connection connection=null; List<Connection> list=null; public ConnectionWrap(Connection connection,List<Connection> list) { super(); this.connection=connection; this.list=list; } @Override public void close() throws SQLException { list.add(connection); } @Override public PreparedStatement prepareStatement(String sql) throws SQLException { return connection.prepareStatement(sql); } ... ... }
使用装饰模式对象
public class MyDataSource implements DataSource { //①、开辟空间 List<Connection> list=new ArrayList<Connection>(); /* 单例 start */ private static MyDataSource dataSource=new MyDataSource(); /** * ②、创建数据库连接对象,并放置到内存中 */ private MyDataSource() { for (int i = 0; i < 10; i++) { Connection connection = JDBCUtil.getConnection(); list.add(connection); } } public static MyDataSource getInstance() { return dataSource; } /* 单例 end */ /** * ③、 sun公司 提供数据库连接池规范中包含了获取数据库连接对象的方法 * 只需要重写此方法 */ @Override public Connection getConnection() throws SQLException { //内存扩容 if (list.size() == 0) { for (int i = 0; i < 3; i++) { Connection connection = JDBCUtil.getConnection(); list.add(connection); } } // 移除集合中第一个元素(数据库连接对象),并获取移除的元素 Connection conn=list.remove(0); // 装饰模式 返回Connection对象,进行装饰,使其对象的close方法,有归还功能 Connection connection = new ConnectionWrap(conn, list); return connection; } ... ... }
测试代码
public class TestPool { @Test public void testPool() { Connection connection=null; PreparedStatement ps =null; try { DataSource dataSource=MyDataSource.getInstance(); connection=dataSource.getConnection(); String sql="insert into account values(?,?)"; ps =connection.prepareStatement(sql); ps.setString(1, "qian"); ps.setInt(2, 1000); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { 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 }