目录
使用数据库连接池优化数据库性能
之前我们对数据库的操作,其实是有很大问题的;
因为我们是每次操作数据库之前,都会问数据库要一个连接,用完之后,就把这个链接还给了数据库;
其实数据库连接是重量级的东西,数据库每次创建一个连接出来,都要花老大力气了;
因此,我们应该固定的保存着一些连接,这些连接用完以后,就放在那里,等下次再用,避免每次都问数据库要 ;
连接池
程序一启动,就问数据库要一定数量的连接,维护在一个地方(连接池);
这样每次,需要连接,就从池中获取,用完之后也不返回给数据库,而是继续保持在池中;起到复用连接的作用;
这种变化是很惊人的;以前10000次访问,就需要创建一千次连接。现在无论多少次访问,都只需要创建连接池中的连接数 ;
编写自定义数据库连接池
编写连接池需要实现 javax.sql.DataSource
接口。DateSource
接口中定义了 2
个重载 getConnection
的方法 :
Connection getConnection() ;
Connection getConnection(String username,String password) ;
实现
DateSource
接口,并实现连接池功能的步骤:a、在DateSource构造器中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中 ; b、实现getConnection方法,让getConnection方法每次调用时,从Linkedlist中取出一个连接返回给用户 ; c、当用户使用完Connection以后,调用Connection.close()方法时,Collection对象保证将自己返回到LinkedList中,而不是把conn返给数据库 ; 其中Collection保证将自己返回给LinkedList中是此处编程的难点!!
在实现 Connection getConnection()
的时候,有个问题,就是我们不能简单的从LinkedList
里面获取 Connection
返回,而要是确保 linkedList
里面将按个Connection
对象移除了,否则会造成连接冲突;
还有一个问题,就是用户在用完 Connection
的时候,会习惯调用 close()
方法,这样就导致了连接放给了数据库,而不是返给了我们连接池;
close()
方法加强
现在就有一个问题,就是如何保证调用 Connection的close()
方法,是将连接还给连接池而不是数据库 ?
这就涉及到,我们对一个类功能进行加强的问题;
在java里面,通常有三种方法;
a、写一个子类,覆写父类的方法
这个做法有个致命的缺点:就是必须获得父类的所有信息,向我们这里,继承是基本不可能的!
因为,真正返回给我们的连接是,mysql的驱动类生成的连接,这个类里面,封装了太多的信息:比如:连接的数据库地址、数据库名字、用户名、连接密码。。这些信息 ;
我们要真的想继承它,我们就要在子类中得到这些信息,可惜人家都说private字段,子类也拿不着;子类拿不着,那么子类就连不上数据库。。
除非你去看去mysql的驱动源码,自己把信息拿出来,写在你创建的子类里面,但这跟重写mysql驱动没哈区别了,工程量吓人!
因此,这里放弃这种做法 ;
b、用包装模式
写法:
第一步:定义一个类实现与被增强类相同的接口
第二步:在类中定义一个变量,记住被增强对象
第三步:定义一个构造器,参数接受一个被增强对象的类
第四步:覆盖我们向增强的方法 ;
第五步:对于不想增强的方法,直接调用被增强对象的方法 ;
这样就完美的增强了我们想要的方法;但是也有缺点,就是接口中的每个方法,都要我们去调用被增强对象的方法 ;
接口中的方法少还行,一旦很多,这种代码,我们就要写很多次,,,,,很恶心!
因此,应该考虑动态代理 ;
c、动态代理(aop编程,面向切面编程)
主要是利用拦截;(暂时,我也不会,以后会跟,最多20天以内更新。写于2018年6月12日22:07:53)
代码:
自定义连接池代码。。
public class MyDataSource implements javax.sql.DataSource {
private static LinkedList<Connection> connections = new LinkedList<>();
static {
try {
// 初始化10个连接出来
for (int i = 0; i < 10; i++) {
connections.add(new MyConnection(JdbcUtils.getConnection()));
}
} catch (Exception e) {
e.printStackTrace();
throw new ExceptionInInitializerError(e);
}
}
private static MyDataSource myDataSource = new MyDataSource();
public static MyDataSource getDataSource() {
return myDataSource;
}
private MyDataSource() {
}
@Override
public Connection getConnection() throws SQLException {
if (connections.size() < 1) {
throw new SQLException("数据库连接正忙,请稍后再试...");
}
return connections.removeFirst();
}
....
....
....
// 包装器类 ,对MyDataSource类 进行增强
static class MyConnection implements Connection {
private Connection connection;
public MyConnection(Connection connection) {
this.connection = connection;
}
// 其他方法,直接调用 connection的方法。。
@Override
public Statement createStatement() throws SQLException {
return connection.createStatement();
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
}
....
// 重点关注 close方法,对其进行增强,不将连接返回给数据库
@Override
public void close() throws SQLException {
System.out.println("连接池中的剩余连接量:"+connections.size());
connections.add(this) ;
System.out.println("成功拦截关闭!将它们返给连接池。。");
System.out.println("连接池中的剩余连接量:"+connections.size());
}
....
// 还有许多其他方法,不一一列举了,太多了,这也是包装模式的缺点 。。。