连接池
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接。释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
传统的连接的缺点:多个用户并发访问servlet,从dao层获取链接,数据库创建连接要耗费大量资源。
使用连接池的优点:用户可以往连接池获取连接,用完之后把连接放回连接池,用linkedlist保存连接。
2、编写标准的数据源
自定义数据库连接池要实现javax.sql.DataSource接口,一般都叫数据源。
public class MyDataSource implements DataSource{
//创建一个存放连接的池子
private static LinkedList<Connection> pool=(LinkedList<Connection>) Collections.synchronizedList(new LinkedList<Connection>());
static{
for(int i=0;i<10;i++){
Connection conn;
try {
conn = DBUtil.getConnection();
pool.add(conn);
} catch (Exception e) {
throw new ExceptionInInitializerError("初始化数据库连接失败,请检查配置文件是否正确");
}
}
}
public Connection getConnection() throws SQLException {
Connection conn=null;
if(pool.size()>0){
conn= pool.removeFirst();//从池中取出一个连接
Connection myConn=new MyConnection(conn, pool);//得到一个包装后的MyConnection对象
return myConn;
}else{
//等待
//新创建一个连接
throw new RuntimeException("服务器忙");
}
}
创建一个LinkedList集合作为存放连接的池子,在静态代码块中把连接放入连接池,重写getConnection方法,用remoceFirst()方法从连接池中取出连接。
3、编写数据源时遇到的问题及解决办法
public class Test {
public static void main(String[] args) {
DataSource ds=new MyDataSource();
Connection conn=null;
PreparedStatement ps=null;
try {
conn=ds.getConnection();//Connection属于com.mysql.jdbc.Connection这个类,现在用装饰设计模式包装这个类
ps=conn.prepareStatement("");
//。。。
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
conn.close();//这里要是调用close方法就把连接关闭了
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
```在JDBC中从连接池获取连接,运行到释放资源的那一步时,调用close方法会把当前链接释放,而不是放回连接池,这有问题,所以用装饰设计模式来解决.
装饰设计模式
原理:
具体类和装饰类实现相同的接口,在装饰类里面创建具体类的引用,对已有对象的功能进行增强。
实现装饰设计模式有五步:
1、创建一个装饰类实现和被装饰类相同的接口
2、在装饰类创建一个被装饰类对象
3、定义一个构造方法,把这个对象注入进去
4、对于不需要重写的方法,直接调用原有的方法
5、对需要修改的方法进行重写
这里需要对com.mysql.jdbc.Connection类的close()方法进行重写
<div class="se-preview-section-delimiter"></div>
public class MyConnection implements Connection{//1、实现与被包装类相同的接口
private Connection oldConnection;//2、定义一个被包装类型的变量
private LinkedList pool;//连接池对象
public MyConnection(Connection oldConnection,LinkedList pool){//3、定义构造方法,把被包装的对象注入,给被包装类变量赋值
this.oldConnection=oldConnection;
this.pool=pool;//得到连接池对象
}
//5、对于需要改写的方法,写自己的代码
public void close() throws SQLException {
pool.add(oldConnection);
}
//4、对于不需要改写的方法,调用原来的方法
public PreparedStatement prepareStatement(String sql) throws SQLException {
// TODO Auto-generated method stub
return oldConnection.prepareStatement(sql);
}
然后在MyDataSource的getConnection()方法中使用装饰设计模式,原本返回的是被装饰类对象,现在创建一个装饰类对象,把被装饰类对象和连接池对象传入,返回装饰类对象。
public Connection getConnection() throws SQLException {
Connection conn=null;
if(pool.size()>0){
conn= pool.removeFirst();//从池中取出一个连接
Connection myConn=new MyConnection(conn, pool);//得到一个包装后的MyConnection对象
return myConn;
}else{
//等待
//新创建一个连接
throw new RuntimeException("服务器忙");
}
}
<div class="se-preview-section-delimiter"></div>
“`这样在调用getConnection()方法时返回的就是一个装饰类对象,调用close()方法把连接放回连接池.
“`
“`这样在调用getConnection()方法时返回的就是一个装饰类对象,调用close()方法把连接放回连接池.
还有另一种解决方案,适配器模式:
适配器模式是装饰设计模式的一种变体
/*
* 1、创建一个装饰类实现和被装饰类相同的接口
2、在装饰类创建一个被装饰类对象
3、定义一个构造方法,把这个对象注入进去
4、对于不需要重写的方法,直接调用原有的方法
*/
public class MyConnectionWraper implements Connection{
private Connection oldConn;
public MyConnectionWraper(Connection oldConn){
this.oldConn=oldConn;
}
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return oldConn.unwrap(iface);
}
然后再新建一个类继承这个适配器,传入被装饰类的引用,重写需要重写的方法,这样让代码更简洁,不用写那些不需要重写的方法
public class MyConnection extends MyConnectionWraper{
private Connection oldConn;
private LinkedList<Connection> pool;
public MyConnection(Connection oldConn,LinkedList<Connection> pool) {
super(oldConn);为什么一定要写这个,因为父类只有一个有参的构造方法,没有无参的,所以在子类中一定要通过super()调用父类的带参构造方法,否则构造不出父类,从而也构造不出他自己
this.oldConn=oldConn;
this.pool=pool;
}
@Override
public void close() throws SQLException {
pool.addFirst(oldConn);
//super.close();
}
}