由于使用jdbc的时候,每操作一次都需要获取连接(创建),用完之后把连接释放掉了(销毁)。所以我们可以通过连接池来优化curd操作。
作用:管理数据库的连接,提高项目的性能。
思路:就是在连接池初始化的时候存入一定数量的连接,用的时候通过方法获取,不用的时候归还连接即可。注意:所有的连接池都必须实现javax.sql.DataSource接口。
获取连接方法:Connection getConnection()。
归还连接方法:connection.close(); //注意,这里会使用装饰者模式,让连接只是归还在了连接池中而不是真正的销毁。
下面我们开始自定义一个自己的连接池。
1.初始化连接池。
首先我们需要定义一个泛型为Connection的List集合,用来存放连接。在这里我们初始容量定义3个。
然后通过JDBCUtills类来获取连接,并将连接放入List集合中。(关于JDBCUtils工具类的封装,见如下链接)
public class MyDataSource { //初始化只需要一次就好,所以放在static静态代码块中 static LinkedList<Connection> list = new LinkedList<>(); static{ //static不能抛异常,只能try...catch try { for(int i = 0; i < 3; i++){ Connection conn = JDBCUtils_plus.getConnection(); list.add(conn); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
2.从连接池中获取连接
先判断连接池是否为空,若为空,通过循环,创建新的连接放入连接池。若不为空,从List集合中取出第一个连接(removeFirst),然后j将连接返回。
不过,这里通过装饰者模式包装了一个ConnectionWrapper类,包装类的作用是包装了Connection的close()方法,使得它不会销毁连接,只是将连接放回。
关于ConnectionWrapper类的创建,后面会详述。
public static Connection getConnection(){ if(list.isEmpty()){ //如果为空,再获取连接进去 for(int i = 0; i < 3;i++){ try { list.add(JDBCUtils_plus.getConnection()); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Connection conn = list.removeFirst(); System.out.println("获取连接池"); //通过构造方法将connection类传入包装类中 ConnectionWrapper cw = new ConnectionWrapper(conn,list); //返回包装类 return cw; }
}
3.利用装饰者模式,创建Connection的包装类ConnectionWrapper
装饰者模式的具体介绍参照装饰者模式
装饰者模式中,装饰者和被装饰者需要继承同一个类或者实现同一个接口。由于Connection本身就是一个接口,所以很自然的让装饰者继承这个接口。
通过查看Connection接口的api发现,接口中的抽象方法实在是太多了。如果我们直接让装饰者类继承,需要重写太多方法。
以下为部分方法:
所以在这里,我们需要使用适配器模式,新增加一个类ConnectionAdapter,让它来实现Connection接口,让装饰者类继承ConnectionAdapter类,这样就可以只重写需要用的方法啦。
该类部分截图:
装饰者类:
public class ConnectionWrapper extends ConnectionAdapter { private Connection conn; private LinkedList<Connection> list; public ConnectionWrapper() { }
//通过构造方法将被装饰者类传入进来并赋值给内部类。将List集合也传进来,因为有归还连接的操作。 public ConnectionWrapper(Connection conn,LinkedList<Connection> list) { this.conn = conn; this.list = list; } @Override
//重写close方法,归还连接。 public void close() throws SQLException { System.out.println("前"+list.size()); list.addLast(this); System.out.println("后"+list.size()); }
//以下两个方法就调用以前的方法就行 @Override public Statement createStatement() throws SQLException { // TODO Auto-generated method stub return conn.createStatement(); } @Override public PreparedStatement prepareStatement(String sql) throws SQLException { // TODO Auto-generated method stub return conn.prepareStatement(sql); }
测试类:
public class Test { public static void main(String[] args) throws SQLException { Connection conn = MyDataSource.getConnection(); //注意:这里的conn已经不是单纯的Connection了,它是实际传过来的装饰类ConnectionWrapper String sql = "select * from student"; PreparedStatement st = conn.prepareStatement(sql); ResultSet rs = st.executeQuery(); while(rs.next()){ System.out.println( rs.getString("id")+" "+rs.getString("name") +" "+rs.getString("score")); } //这里调用的也不是Connection以前的close()方法了,这里是包装类的增强close()方法。不销毁连接,只归还连接。 conn.close(); } }
运行结果如下:
以上,就是一个简易的jdbc连接池。
然而现实有两个比较常用的,封装好的连接池,他们分别是DBCP和C3P0,以下介绍他们的用法:
一.DBCP连接池
apache组织出品。
1.导入jar包(commons-dbcp-1.4.jar和commons-pool-1.5.6.jar)
(emmmm请无视中间的dbutils的jar包......)
2.使用
a.硬编码(不推荐使用)
以下是硬编码方式的配置信息:
//创建连接池 BasicDataSource ds = new BasicDataSource(); //配置信息 ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql:///exercise"); ds.setUsername("root"); ds.setPassword("123456");
b.采用配置文件的方式读取配置信息。这里拿properties文件举例。(名称为dbcp.properties)
代码如下:
//存放配置文件 Properties prop = new Properties(); prop.load(new FileInputStrea("src/dbcp.properties")) //创建连接池 DataSource ds = new BasicDataSourceFactory().createDataSource(prop);
Connection conn=ds.getConnection();
二.C3P0连接池(比较常见)
hibernate和spring使用,有自动回收空闲连接的功能。
1.导入jar包(c3p0-0.9.1.2.jar)
2.使用
a.硬编码:
ComboPooledDataSource ds = new ComboPooledDataSource(); //设置基本参数 ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql:///exercise"); ds.setUser("root"); ds.setPassword("123456"); Connection conn=ds.getConnection();
b.读取配置文件
注意:配置文件的名称:c3p0.properties 或者 c3p0-config.xml时,可以直接使用new ComboPooledDataSource()来获取配置文件(默认配置)。
new ComboPooledDataSource(String configName)//使用命名的配置 若配置的名字找不到,使用默认的配置
以下用properties文件举例:
代码如下:
//由于配置文件写的默认名字,这里使用无参构造 ComboPooledDataSource ds =new ComboPooledDataSource(); Connection conn=ds.getConnection();