一、数据库连接池
1.数据库的连接对象创建工作,比较消耗性能。
2.一开始现在内存中开辟一块空间(集合) , 一开先往池子里面放置 多个连接对象。 后面需要连接的话,直接从池子里面去。不要去自己创建连接了。 使用完毕,要记得归还连接。确保连接对象能循环利用。
二、自定义数据库连接池 步骤分析: 使用单元Junit测试
首先导入之前写好的JDBCUtil类和外部文件jdbc.properties还有jar文件。
新建一个MyDataSource类: 需要继承DataSource类–实现所有方法(我们只需要完成getConnection()方法)–只需要管连接池对外公布的获取连接的方法getConnection()。 2.1 建立MyDataSource方法:先往池子里面放10个连接(建立list集合存放),使用JDBCUtil.getConn()去获取连接,通过循环和add方法连续添加10个连接。 2.2 在连接池对外公布的获取连接的方法中去通过remove移除(取连接) 2.3 最后写一个方法addBack():用完之后归还。
如果现在链接不够的话,就要在取之前判断是否为0,如果不够就要for循环继续add()。
在项目处右键–构建路径–右边选Add Library–添加Junit4选项
新建测试的TestPool类: 5.1 需要建立MyDataSource对象然后调用getConnection()连接 5.2 写sql语句 –MyDataSource对象调用prepareStatement去检测SQL语句 5.3 检测sql语句之后调用对应的executeUpdate()语句 5.4 finally里面要写关于sql操作之后关闭对象–还需要通过对象调用addBack()方法
具体代码: JDBCUtil类代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 package util; import java.io.FileInputStream; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class JDBCUtil { static String driverClass = null; static String url = null; static String name = null; static String password= null; static{ try { //1. 创建一个属性配置对象 Properties properties = new Properties(); InputStream is = new FileInputStream("jdbc.properties"); //使用类加载器,去读取src底下的资源文件。 后面在servlet // InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); //导入输入流。 properties.load(is); //读取属性 driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); name = properties.getProperty("name"); password = properties.getProperty("password"); } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接对象 * @return */ public static Connection getConn(){ Connection conn = null; try { Class.forName(driverClass); //静态代码块 ---> 类加载了,就执行。 java.sql.DriverManager.registerDriver(new Driver()); //DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&password=greatsqldb"); //2. 建立连接 参数一: 协议 + 访问的数据库 , 参数二: 用户名 , 参数三: 密码。 conn = DriverManager.getConnection(url, name, password); } catch (Exception e) { e.printStackTrace(); } return conn; } /** * 释放资源 * @param conn * @param st * @param rs */ public static void release(Connection conn , Statement st , ResultSet rs){ closeRs(rs); closeSt(st); closeConn(conn); } public static void release(Connection conn , Statement st ){ closeSt(st); closeConn(conn); } private static void closeRs(ResultSet rs){ try { if(rs != null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ rs = null; } } private static void closeSt(Statement st){ try { if(st != null){ st.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ st = null; } } private static void closeConn(Connection conn){ try { if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ conn = null; } } }
jdbc.properties代码(只需要更改数据库的名字)
1 2 3 4 driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/bank name=root password=njdxrjgc7777777.
MyDataSource(实现DataSource类)代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 package util; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import javax.sql.DataSource; /* * 这是一个数据库连接池 * 一开始先往池子放10个连接 * * 1.开始创建10个连接 * * 2.取出去 * * 3.拿回来 * * 4.扩容 * */ public class MyDataSource implements DataSource{ //继承之后需要实现下面的方法(点击弹出) List <Connection> list=new ArrayList<Connection>(); //建立list集合存放连接 public MyDataSource() { //添加10个连接 for(int i=0;i<10;i++) { Connection conn=JDBCUtil.getConn(); //提前拷贝JDBCUtil类和jdbc.properties外部文件和外部jar文件 list.add(conn); //add往里添加 } } //连接池对外公布的获取连接的方法 @Override public Connection getConnection() throws SQLException { //来拿连接的时候,先看看池子里面是否还有 if(list.size()==0) //如果没有了继续添加5个 { for(int i=0;i<5;i++) { Connection conn=JDBCUtil.getConn(); //提前拷贝JDBCUtil类和jdbc.properties外部文件和外部jar文件 list.add(conn); //扩容 } } Connection conn = list.remove(0); //移除第一个(自动往前补) 相当于取连接 return conn; } public void addBack(Connection conn) { //自己写的在用完之后要归还的方法 list.add(conn); //add在加回去 } //-------只需要看上面的连接方法---------- @Override public PrintWriter getLogWriter() throws SQLException { // TODO 自动生成的方法存根 return null; } @Override public int getLoginTimeout() throws SQLException { // TODO 自动生成的方法存根 return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { // TODO 自动生成的方法存根 return null; } @Override public void setLogWriter(PrintWriter arg0) throws SQLException { // TODO 自动生成的方法存根 } @Override public void setLoginTimeout(int arg0) throws SQLException { // TODO 自动生成的方法存根 } @Override public boolean isWrapperFor(Class<?> arg0) throws SQLException { // TODO 自动生成的方法存根 return false; } @Override public <T> T unwrap(Class<T> arg0) throws SQLException { // TODO 自动生成的方法存根 return null; } @Override public Connection getConnection(String username, String password) throws SQLException { // TODO 自动生成的方法存根 return null; } }
测试JUnit的TestPool代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package util; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import org.junit.Test; public class TestPool { @Test public void testPool(){ Connection conn=null; PreparedStatement ps =null; MyDataSource my=new MyDataSource(); //调用我们写好的MyDataPool类 try { conn = my.getConnection(); //第一步获取连接 String sql="insert into account values(4,'xilali',10)"; //第二步写sql语句 ps = conn.prepareStatement(sql); //第三步调用方法检测sql语句 ps.executeUpdate(); //调用方法对sql执行 } catch (SQLException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); }finally { try { ps.close(); //关闭对象 替换了之前的JDBCUtil.release(conn,ps,rs); } catch (SQLException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } //归还连接 my.addBack(conn); //用完之后一定要记得addBack归还连接 } } }
单元测试只是显示绿色/红色不通过—具体结果去看看数据库:
三、自定义数据库连接池存在的问题
1 2 3 4 5 6 7 8 9 //面向对象编程 MyDataSource my=new MyDataSource(); //调用我们写好的MyDataSource类 //现在面向接口编程 DataSource dataSource = new MyDataSource(); //改成这样因为接口里面没有定义addBack方法 dataSource.addBack(); //会报错 //而且原来sun公司的Connection对于close()方法直接是关闭 ---我们需要直接改成返回连接 conn.colse();
四、解决存在的问题
修改接口中的那个close方法
原来的Connection对象的close方法,是真的关闭连接。
打算修改这个close方法,以后在调用close,并不是真的关闭,而是归还连接对象。
五、扩展方法
原有的方法逻辑,不是我们想要的。 想修改自己的逻辑
直接改源码 —-无法实现。
继承 —-必须得知道这个接口具体实现是谁。
使用装饰者模式(扩展一个类去更改方法)
先是一个接口Waiter里面有service()方法,最后有一个Waitress类实现刚才的接口输出方法内容,但是现在可以通过建立WaitressWrap()方法去用构造方法获取接口对象,this获取对象,然后通过实现接口方法更改以前的操作。
六、装饰者模式–具体操作 我们新建ConnectionWrap类实现Connection接口的方法,但是只需要去更改close()和prepareStatement()方法。
1.只展示实现类ConnectionWrap类部分代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class ConnectionWrap implements Connection{ //继承Connnection方法 修改它的close更改为 返回连接 Connection connection=null; //创建连接 List<java.sql.Connection> list; public ConnectionWrap(java.sql.Connection conn, List<java.sql.Connection> list) { //构造方法 super(); this.connection =connection; this.list=list; } @Override public void close() throws SQLException { //更改了close()方法 ---变成了归还连接 list.add(connection); } @Override public PreparedStatement prepareStatement(String sql) throws SQLException { //更改了里面的检验sql语句 return connection.prepareStatement(sql); }
2.在MyDataSource类里面在取连接之前对connection对象进行调用COnnectionWrap类包装,
1 2 3 //抛出前,对对象进行包装 Connection connection=new ConnectionWrap(conn,list); return connection;
3.在测试类之中以后就不用写归还的方法,而是直接写JDBCUtil的方法:
1 2 //归还连接 JDBCUtil.release(conn,ps);
七、开源连接池(DBCP/C3PO) DBCP使用步骤:
###1. 导入对应的dbcp和pool的jar文件:
###2.1 不使用配置文件( BasicDataSource类):
思路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1.通过BasicDataSource类构件数据源对象 BasicDataSource dataSource = new BasicDataSource(); 2.不使用配置文件---使用set方法添加那四行材料 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost/bank"); //jdbc:mysql://localhost/bank 主协议:子协议 ://本地/数据库 dataSource.setUsername("root"); dataSource.setPassword("njdxrjgc7777777."); 3.通过对象调用连接 conn = dataSource.getConnection(); //使用dataSource调用连接 4.书写SQL语句,然后去检验,添加,方法上传 String sql = "insert into account values(null , ? , ?)"; //sql插入语句 ps = conn.prepareStatement(sql); //检验sql语句 ps.setString(1, "admin"); //给下标1和2的设定具体的值 ps.setInt(2, 1000); ps.executeUpdate(); //调用插入操作方法 5.finally最后使用JDBCUtil类的方法抓异常 JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package kaiyuan; import java.sql.SQLException; import org.apache.commons.dbcp.BasicDataSource; import org.junit.Test; import com.mysql.jdbc.Connection; import com.mysql.jdbc.PreparedStatement; import util.JDBCUtil; public class DBCPDemo { @Test public void testDBCP01(){ java.sql.Connection conn = null; java.sql.PreparedStatement ps = null; try { //1. BasicDataSource类构建数据源对象 BasicDataSource dataSource = new BasicDataSource(); //连的是什么类型的数据库, 访问的是哪个数据库 , 用户名, 密码。。 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost/bank"); //jdbc:mysql://localhost/bank 主协议:子协议 ://本地/数据库 dataSource.setUsername("root"); dataSource.setPassword("njdxrjgc7777777."); //2. 得到连接对象 conn = dataSource.getConnection(); //使用dataSource调用连接 String sql = "insert into account values(null , ? , ?)"; //sql插入语句 ps = conn.prepareStatement(sql); //检验sql语句 ps.setString(1, "admin"); //给下标1和2的设定具体的值 ps.setInt(2, 1000); ps.executeUpdate(); //调用插入操作方法 } catch (SQLException e) { e.printStackTrace(); }finally { JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法 } } }
###2.2 使用配置文件( BasicDataSourceFactory类):
思路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 1.通过BasicDataSourceFactory类构件数据源对象 BasicDataSourceDactory dataSource = new BasicDataSourceDactory(); 2.使用配置文件- //构造配置文件对象 Properties properties=new Properties(); //输入流加载文件(找到配置文件) InputStream is=new FileInputStream("src//dbcpconfig.properties"); properties.load(is); //对象调用load加载文件(加载好) DataSource dataSource = factory.createDataSource(properties); //输入流加载文件之后要用对象调用方法加载配置文件(加载到对象去) 3.通过对象调用连接(sun公司的DataSource) conn = dataSource.getConnection(); //使用dataSource调用连接 4.书写SQL语句,然后去检验,添加,方法上传 String sql = "insert into account values(null , ? , ?)"; //sql插入语句 ps = conn.prepareStatement(sql); //检验sql语句 ps.setString(1, "liangchaowei"); //给下标1和2的设定具体的值 ps.setInt(2, 1020); ps.executeUpdate(); //调用插入操作方法 5.finally最后使用JDBCUtil类的方法抓异常 JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package kaiyuan; import java.io.FileInputStream; import java.io.InputStream; import java.sql.SQLException; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; import org.junit.Test; import util.JDBCUtil; public class DBCPDemo02 { @Test public void testDBCP02() throws Exception{ //和之前的代码一样 java.sql.Connection conn = null; java.sql.PreparedStatement ps = null; try { //1. BasicDataSourceFactory类构建数据源对象(因为有可以加载配置文件的方法) BasicDataSourceFactory factory=new BasicDataSourceFactory(); //构造配置文件对象 Properties properties=new Properties(); //输入流加载文件 InputStream is=new FileInputStream("src//dbcpconfig.properties"); properties.load(is); //输入流加载配置文件 DataSource dataSource = factory.createDataSource(properties); //输入流加载文件之后要用对象调用方法加载配置文件 //2. 得到连接对象 conn = dataSource.getConnection(); //使用dataSource调用连接 String sql = "insert into account values(null , ? , ?)"; //sql插入语句 ps = conn.prepareStatement(sql); //检验sql语句 ps.setString(1, "liangchaowei"); //给下标1和2的设定具体的值 ps.setInt(2, 1020); ps.executeUpdate(); //调用插入操作方法 } catch (SQLException e) { e.printStackTrace(); }finally { JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法 } } }
dbcpconfig.properties配置文件(放在src下面设置好连接池的连接初始化/最大数等等):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #连接设置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/bank username=root password=njdxrjgc7777777. #<!-- 初始化连接 --> initialSize=10 #最大连接数量 maxActive=50 #<!-- 最大空闲连接 --> maxIdle=20 #<!-- 最小空闲连接 --> minIdle=5 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --> maxWait=60000 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 connectionProperties=useUnicode=true;characterEncoding=gbk #指定由连接池所创建的连接的自动提交(auto-commit)状态。 defaultAutoCommit=true #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=READ_UNCOMMITTED
J3P0使用步骤: ###1. 导入对应的c3p0-0.9.1.2.jar文件:
###2.1 不使用配置文件方式(ComboPooledDataSource类)
思路:
1 和DBCP数据池的不实用配置文件方式一样,只不过一个是BasicDataSource类、另外一个是ComboPooledDataSource类
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Connection conn = null; PreparedStatement ps = null; try { //1. 创建datasource ComboPooledDataSource dataSource = new ComboPooledDataSource(); //2. 设置连接数据的信息 dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost/bank"); dataSource.setUser("root"); dataSource.setPassword("root"); //2. 得到连接对象 conn = dataSource.getConnection(); String sql = "insert into account values(null , ? , ?)"; ps = conn.prepareStatement(sql); ps.setString(1, "admi234n"); ps.setInt(2, 103200); ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtil.release(conn, ps); }
###2.2 使用配置文件方式(ComboPooledDataSource)
思路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1.需要导入c3p0-config.xml解析文件(名字不能更改!!!!) 2.跟以前的相比只需要第一步new一个ComboPooledDataSource对象 ComboPooledDataSource dataSource=new ComboPooledDataSource(); 3.然后通过对象getConnection()获得连接。 conn = dataSource.getConnection(); 4.新建sql语句,通过连接检验sql语句,添加下标内容,调用插入语句 String sql = "insert into account values(null , ? , ?)"; //sql插入语句 ps = conn.prepareStatement(sql); //检验sql语句 ps.setString(1, "adminsan"); //给下标1和2的设定具体的值 ps.setInt(2, 1050); ps.executeUpdate(); //调用插入操作方法 5.finally最后调用JDBCUtil类方法 JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法
c3p0-config.xml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!-- default-config 默认的配置, --> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <!-- 要和不用配置文件的set方法的后面一样 --> <property name="jdbcUrl">jdbc:mysql://localhost/bank</property> <property name="user">root</property> <property name="password">njdxrjgc7777777.</property> <property name="initialPoolSize">10</property> <property name="maxIdleTime">30</property> <property name="maxPoolSize">100</property> <!-- 关于连接池的细节设计 --> <property name="minPoolSize">10</property> <property name="maxStatements">200</property> </default-config> <!-- This app is massive! --> <named-config name="oracle"> <!-- oracle数据库用 --> <property name="acquireIncrement">50</property> <property name="initialPoolSize">100</property> <property name="minPoolSize">50</property> <property name="maxPoolSize">1000</property> <!-- intergalactoApp adopts a different approach to configuring statement caching --> <property name="maxStatements">0</property> <property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him --> <user-overrides user="master-of-the-universe"> <property name="acquireIncrement">1</property> <property name="initialPoolSize">1</property> <property name="minPoolSize">1</property> <property name="maxPoolSize">5</property> <property name="maxStatementsPerConnection">50</property> </user-overrides> </named-config> </c3p0-config>
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package kaiyuaner; import java.sql.SQLException; import javax.sql.CommonDataSource; import org.apache.commons.dbcp.BasicDataSource; import org.junit.Test; import com.mchange.v2.c3p0.ComboPooledDataSource; import util.JDBCUtil; public class C3P0Demo { @Test public void C3P0Demo01(){ java.sql.Connection conn = null; java.sql.PreparedStatement ps = null; try { //1.只需要new一个对象(找路径什么的C3P0帮你完成) ComboPooledDataSource dataSource=new ComboPooledDataSource(); //xml里面有两种数据库(默认会找default-config分支) 如果需要用oracle数据库就("oracle") //2. 得到连接对象 conn = dataSource.getConnection(); //使用dataSource调用连接 String sql = "insert into account values(null , ? , ?)"; //sql插入语句 ps = conn.prepareStatement(sql); //检验sql语句 ps.setString(1, "adminsan"); //给下标1和2的设定具体的值 ps.setInt(2, 1050); ps.executeUpdate(); //调用插入操作方法 } catch (SQLException e) { e.printStackTrace(); }finally { JDBCUtil.release(conn, ps); //调用JDBCUtil类的方法 } } }