数据库连接池 数据库连接池
数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现在的数据库连接,而不是再重新建立一个. 这项技术能明显提高对数据库操作的性能 DataSource接口概述
javax.sql.DataSource接口: 数据源(数据库连接池). Java官方提供的数据库连接池规范(接口) 如果想完成数据库连接池技术, 就必须实现DataSource接口 核心功能: 获取数据库连接对象: Connection getConnection() 自定义数据库连接池
定义一个类, 实现DataSource接口 定义一个容器, 用于保存多个Connection连接对象 定义静态代码块, 通过JDBC工具类获取10个连接保存到容器中 重写getConnection方法, 从容器中获取一个连接并返回 定义getSize方法, 用于获取窗口的大小并返回 package com.test001; import com.lizicai.utils.JDBCUtils; import javax.sql.DataSource; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.Logger; public class MyDataSource implements DataSource { // 1. 准备窗口, 用于保存多个连接对象, 变成线程安全的 private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>()); // 2. 定义代码块, 通过工具类获取10个连接对象 static { for(int i=0;i<10;i++){ Connection con = JDBCUtils.getConnection(); pool.add(con); } } //3 重写getConnection() , 用于获取一个连接对象 @Override public Connection getConnection() throws SQLException { if( pool.size()> 0){ Connection con = pool.remove(0); return con; } else { throw new RuntimeException("连接数量已用尽"); } } // 4. 定义getSize方法, 获取连接池窗口的大小 public int getSize(){ return pool.size(); } } package com.test001; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class MyDataSourceTest { public static void main(String[] args) throws SQLException { MyDataSource pool = new MyDataSource(); System.out.println(pool.getSize()); Connection con = pool.getConnection(); String sql = "SELECT * FROM student"; PreparedStatement pst = con.prepareStatement(sql); ResultSet rs = pst.executeQuery(); while (rs.next()){ System.out.println(rs.getInt("sid")+"\t"+rs.getString("NAME") +"\t"+rs.getInt("age")+"\t"+rs.getDate("birthday")); } rs.close(); pst.close(); con.close(); System.out.println(pool.getSize()); } } 归还方式 归还数据库连接的方式 继承方式 装饰设计模式 我在适配器设计模式 动态 代理方式 归还方式 - 继承方式 继承方式归还数据库连接的思想。 通过打印连接对象,发现 DriverManager 获取的连接实现类是 JDBC4Connection 那我们就可以自定义一个类,继承JDBC4Connection这个类,重写closeQ 方法,完成连接对象的归还 继承方式归还数据库连接的实现步骤。 定义一个类,继承JDBC4Connection。 定义 Connection 连接对象和连接池容器对象的成员变量。 通过有参构造方法完成对成员变量的赋值。 重写cose 方法,将连接对象添加到池中。 继承方式日还数据库连接存任的问题。 通过查看 JDBC 工具类获取连接的方法发现:我们星然白定义了一个子类,完成了归还连接的操作。但是 DriverManager 获取的还是JDBC4Connection这个对象,并不是我们的子类对象,而我们又不能整体去修改驱动包中类的功能,所继承这种方式行不通! 归还连接 - 装饰设计模式 装饰设计模式日还数据库连接的思想。 我们可以自定义一个类,实现 Connection接口。这样就具备了和 JDBC4Connection相同的行为了 重写close()方法,完成连接的归还。其余的功能还调用 mysql驱动包实现类原有的方法即可 装饰设计模式归还数据库连接的实现步骤。 定义一个类,实现 Connection 接口 定义 Connection 连接对象和连接池容器对象的成员变量 通过有参构造方法完成对成员变量的赋值 重写close()方法,将连接对象添加到池中 剩余方法,只需要调用mysql驱动包的连接对象完成即可 在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装 装饰设计模式归还数据库连接存在的问题。 实现 Connection接口后,有大量的方法需要在自定义类中进行重写 package com.test002; import java.sql.*; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; /** * 1. 定义一个类, 实现Connection接口 * 2. 定义连接对象和连接池容器对象的成员变量 * 3. 通过有参构造方法为成员变量赋值 * 4. 重写close()方法, 完成归还连接 * 5. 剩余方法, 还是调用原有的连接对象中的功能即可 */ public class MyConnection2 implements Connection { private Connection con; private List<Connection> pool; public MyConnection2(Connection con, List<Connection> pool) { this.con = con; this.pool = pool; } @Override public void close() throws SQLException { pool.add(con); } } public class MyDataSource implements DataSource { //3 重写getConnection() , 用于获取一个连接对象 @Override public Connection getConnection() throws SQLException { if( pool.size()> 0){ Connection con = pool.remove(0); MyConnection2 myCon = new MyConnection2(con,pool); return myCon; } else { throw new RuntimeException("连接数量已用尽"); } } } 归还连接 - 适配器设计模式 适配器设计模式归还数据库连接的思想。 我们可以提供一个适配器类,实现 Connection 接口,将所有方法进行实现(除了close方法) 自定义连接类只需要继承这个适配器类,重写需要改进的close0 方法即可 适配器设计模式归还数据库连接的实现步骤。 定义一个适配器类, 实现 Connection 接口。 定义 Connection 连接对象的成员变量。 通过有参构造方法完成对成员变量的赋值。 重写所有方法(除了close), 调用mysq驱动包的连接对象完成即可。 定义一个连接类,继承适配器类。 定义 Connection 连接对象和连接池容器对象的成员变量,并通过有参构造进行赋值。 重写close()方法,完成归还连接。 在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装。 适配器设计模式归还数据库连接存在的问题。 自定义连接类虽然很简洁, 但我在楼下器还是自己编写的, 也比较麻烦 public abstract class MyAdapter implements Connection { private Connection con; public MyAdapter(Connection con){ this.con = con; } //省略其他实现的方法 } package com.test002; import java.sql.Connection; import java.sql.SQLException; import java.util.List; public class MyConnection3 extends MyAdapter{ private Connection con; private List<Connection> pool; public MyConnection3(Connection con, List<Connection> pool) { super(con); this.con = con; this.pool = pool; } @Override public void close() throws SQLException { pool.add(con); } } 动态代理 动态代理: 在不改变目标对象方法的情况下对方法进行增强 组成 被代理对象: 真实的对象 代理对象: 内存的一个对象 要求 代理对象必须和被代理对象实现相同的接口 实现 Proxy.newProxyInstance() package com.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { Student stu = new Student(); stu.eat("饭"); stu.study(); /** * 要求: 在不改Student类中任何代码前提下, 通过study方法输出一句话 * 类加载器: 和被代理对象使用相同的类加载器 * 接口类型Class数组: 和被代理对象使用相同接口 * 代理规则: 完成代理增加的功能 */ StudentInterface proxyStu = (StudentInterface) Proxy.newProxyInstance(stu.getClass().getClassLoader(), new Class[]{StudentInterface.class}, new InvocationHandler() { /** * 执行student类中所有方法都会经过invoke方法 * 对method方法进行判断 * 如果是study, 则对其增强 * 如果不是, 还调用学生对象原的功能即可 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("study")){ System.out.println("学快点"); return null; } else { return method.invoke(stu, args); } } }); proxyStu.eat("米饭"); proxyStu.study(); } } 归还连接 - 动态代理方式 动态代理方式归还数据库连接的思想。 我们可以通过 Proxy 来完成对 Connection 实现类对象的代理 代理过程中判断如果执行的是 close 方法,就将连接归还池中。如果是其他方法则调用连接对象原来 的功能即可 动态代理方式归还数据库连接的实现步骤。 定义一个类,实现 DataSource接口 定义一个容器,用于保存多个Connection连接对象 定义静态代码块,通过JDBC工具类获取 10 个连接保存到容器中 重马getConnection 方法,从容器中获取一个连接 通过 Proxy 代理,如果是close 方法,就将连接归还池中。如果是其他方法则调用原有功能 定义 getsize 方法,用于获取容器的大小并返回 动态代理方式归还数据库连接存在的问题。 我们自己写的连接池技术不够完善,功能也不够强大 package com.test001; import com.lizicai.utils.JDBCUtils; import com.test002.MyConnection2; import com.test002.MyConnection3; import javax.sql.DataSource; import java.io.PrintWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.Logger; public class MyDataSource implements DataSource { // 1. 准备窗口, 用于保存多个连接对象, 更改List为线程安全的 private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>()); // 2. 定义代码块, 通过工具类获取10个连接对象 static { for(int i=0;i<10;i++){ Connection con = JDBCUtils.getConnection(); pool.add(con); } } // 动态代理归还Connection @Override public Connection getConnection() throws SQLException { if( pool.size()> 0){ Connection con = pool.remove(0); Connection proxyCon =(Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() { /** 执行Connection 实现类连接对象所有的方法都会经过invoke * 如果是close方法, 归还连接 * 如果不是, 直接执行连接对象原有的功能即可 * */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("close")){ pool.add(con); return null; } else { return method.invoke(con, args); } } }); return proxyCon; } else { throw new RuntimeException("连接数量已用尽"); } } // 4. 定义getSize方法, 获取连接池窗口的大小 public int getSize(){ return pool.size(); } @Override public Connection getConnection(String username, String password) throws SQLException { return null; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } @Override public PrintWriter getLogWriter() throws SQLException { return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { } @Override public void setLoginTimeout(int seconds) throws SQLException { } @Override public int getLoginTimeout() throws SQLException { return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; } } 使用C3P0数据库连接池的使用步骤 导入Jar包, c3p0-0.9.5.5.jar和mchange-commons-java-0.2.19.jar 导入配置文件到src目录下 创建C3P0连接池对象 获取数据库连接进行使用 C3P0下载, 开头/c3p0-bin/c3p0-0.9.5.5/c3p0-0.9.5.5.bin.zip ...