1.自定义链接池
讲解
为什么要使用链接池
Conection对象在JDBC使用的时候就会去创建一个对象,使用结束以后就会将这个对象销毁(close)。每次创建和销毁对象都是耗时操作,需要使用连接池对其进行优化,程序初始化的时候,初始化多个连接,将多个链接放入到池(集合)中。每次获取的时候,都可以直接从连接池中进行获取,使用结束以后,将连接归还到池中
生活里面的连接归还到池中
老方式:
下了地铁需要汽车,跑去生产一个,然后骑完之后,直接把车销毁了连接池方式摩托单车骑之前,有一个公司生产了很多自行车,下了地铁需要骑车,直接扫码使用就好了,然后骑完还回去
连接池原理:
1.程序一开始就创建一定数量的连接,放在一个容器(集合)中,这个容器称为连接池
2.使用的时候直接从连接池中取一个已经创建好的连接对象使用完成之后归还池子
3.如果池子里面的连接使用完了,还有程序需要连接先等待一段时间(eg:3s)如果在这段时间之内有连接归还就拿去使用:如果还没有连接归还,新创建一个,但是新创建的这一个不会还了
4.集合选择LinkedList增删比较快LinkedList里面的removeFirst()和addLast()方法和连接池的原理唇合
小结:
使用连接池的目的:可以让连接得到复用,避免浪费自定义连接池-初级版本 目标根据连接的原理,是使用LinkedList自定义连接池
分析
1.创建一个类MyDataSource定义集合LinkedList
2.程序初始化时候,创建5个连接,存到LinkedList
3.定义getConnection()从LinkedList取出Connection返回
4.定义addBack()方法归还Connection到LinkedList
实现
/**
* 包名:com.sunlw.customer.datasource
*
* @author sunlw
* ⽇期2020-07-06 09:37
* ⾃定义连接池的第⼀个版本
* 1. 创建⼀个容器,存放连接
* 2. 默认往容器中存放5个连接
* 在构造函数中编写代码
* 3. 提供⼀个⽅法,让调⽤者获取连接
* 4. 提供⼀个⽅法,让调⽤者归还连接
*
* 当前第⼀个版本存在的问题:
* 1. 新创建的连接(原本没有在连接池中的连接)也会归还回
连接池
* 2. 连接池使⽤的耦合性太⾼了,不便于以后项⽬切换连接池
*/
public class MyDataSource {
private LinkedList<Connection>
connectionPool = new LinkedList<>();
public MyDataSource() {
//初始化往connectionPool中存放5个连接
for (int i = 0; i < 5; i++) {
try {
//创建⼀个连接
Connection conn =
DriverManager.getConnection("jdbc:mysql:///day2
0?characterEncoding=utf8", "root", "123");
//将连接添加到connectionPool中
connectionPool.add(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 获取连接的⽅法
* @return
*/
public Connection getConn() throws
SQLException {
//如果容器中有连接,则从容器中获取,如果容器中
没有连接,就直接新创建连接
Connection conn = null;
if (connectionPool.size() > 0){
//从容器中获取连接
conn =
connectionPool.removeFirst();
}else {
//则新创建连接
conn =
DriverManager.getConnection("jdbc:mysql:///day2
0?characterEncoding=utf8", "root", "123");
}
return conn;
}
public void addBack(Connection connection){
//归还连接:其实就是将要归还的那个连接,添加到容
器的尾部
connectionPool.addLast(connection);
}
}
小结//
1.创建一个类MyDataSource,定义一个集合LinkedLis
2..程序初级化(静态代码快)里面 创建5个连接存到LinkedLis
3.定义提供Conection的方法
4.定义归还Conection的方法
自定义连接池-进行版本
目标
实现datasource完成自定义连接池
分析
在初级版本中,我们定义的方法是getConnection()因为是自定义的,如果改用李四的自定义的连接池,李四定义的方法是getAbc(),那么我们的源码就需要修改,这样不方便维护,所有sun公司定义一个接口datasource,让自定义连接池有了范围
讲解
datasource接口概述
JAVA为数据库连接池提供了公共接口:java.sql.DataSource,各个厂商(用户)需要让自己的连接池实现这个接口,这样应用程序可以方便的切换不同厂商连接池
javax.sql.DataSource接口,中文翻译数据源,其实就是连接池。
从数据源中得到Connection连接对象。接口Sun公司没有具体的实现,
由各大数据库厂商去实现,很多第三方的公司也可以实现。
代码实现
/**
* 包名:com.sunlw.customer.datasource
*
* @author sunlw
* ⽇期2020-07-06 10:01
* ⾃定义连接池的第⼆版:
* 编写连接池,并且实现官⽅的DataSource接⼝
*
* 存在的问题:DataSource中并没有提供归还连接的⽅法
*/
public class MyDataSource2 implements
DataSource{
private LinkedList<Connection>
connectionPool = new LinkedList<>();
public MyDataSource2() {
//初始化往connectionPool中存放5个连接
for (int i = 0; i < 5; i++) {
try {
//创建⼀个连接
Connection conn =
DriverManager.getConnection("jdbc:mysql:///day2
0?characterEncoding=utf8", "root", "123");
//将连接添加到connectionPool中
connectionPool.add(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Override
public Connection getConnection() throws
SQLException {
//如果容器中有连接,则从容器中获取,如果容器中
没有连接,就直接新创建连接
Connection conn = null;
if (connectionPool.size() > 0){
//从容器中获取连接
conn =
connectionPool.removeFirst();
}else {
//则新创建连接
conn =
DriverManager.getConnection("jdbc:mysql:///day2
0?characterEncoding=utf8", "root", "123");
}
return conn;
}
@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;
}
}
小结
编写连接池遇到的问题
实现DataSource接口后,addBack()不能调用了 能不能引入新的api,直接调用之前的cnnection.clse()但是这个clise不是关闭是归还
解决方法:
续承
条件:可以控制父类,最起码知道父类的名字装饰者模式
作用:改写已存在的类的某个方法或某些方法
条件:增强和被增强类实现的是同一个接口增强类里要拿到增强类的引用动态代理
自定义连接池-终极版本
目标
使用装饰者模式改写connection的close()方法让connection归还
讲解
自定义连接 池终极版本分析
增强connection的close()方法,其它的方法逻辑不改
1. 创建WrapperConnection实现Connection
2.在WrapperConnection里面需要得到被增强的connection对象(通过构造方法进去)
3.改写close()的逻辑,变成归还
4.其它方法的逻辑,还是调用被增强Connection对象之前的逻辑
实现
WrapperConnection
/**
* 包名:com.sunlw.customer.connection
*
* @author sunlw
* ⽇期2020-07-06 10:16
* 依赖倒置原则:
* 尽量依赖抽象,不依赖具体
*/
public class WrapperConnection implements
Connection{
private Connection connection;
private LinkedList<Connection>
connectionPool;
public WrapperConnection(Connection
connection,LinkedList<Connection>
connectionPool) {
this.connection = connection;
this.connectionPool = connectionPool;
}
@Override
public void close() throws SQLException {
//将当前这个连接归还回原来那个连接池容器
connectionPool.addLast(this);
}
@Override
public Statement createStatement() throws
SQLException {
return connection.createStatement();
}
@Override
public PreparedStatement
prepareStatement(String sql) throws
SQLException {
return
connection.prepareStatement(sql);
}
@Override
public CallableStatement prepareCall(String
sql) throws SQLException {
return null;
}
@Override
public String nativeSQL(String sql) throws
SQLException {
return null;
}
@Override
public void setAutoCommit(boolean
autoCommit) throws SQLException {
connection.setAutoCommit(autoCommit);
}
@Override
public boolean getAutoCommit() throws
SQLException {
return false;
}
@Override
public void commit() throws SQLException {
connection.commit();
}
@Override
public void rollback() throws SQLException
{
connection.rollback();
}
//....其它⽅法省略
}
MyDataSource
/**
* 包名:com.sunlw.customer.datasource
*
* @author sunlw
* ⽇期2020-07-06 10:01
* ⾃定义连接池的第三版:
* 1. 可以归还连接
* 2. 如果是原本在连接池中的连接,就归还;如果是新创建的
连接⽤完后就销毁
*
* 如果是原本在连接池中的连接,调⽤close()⽅法不销毁,
⽽是将其归还回连接池
* 如果是新创建的连接,调⽤close()⽅法,就销毁(执⾏原本
的close)
*
* 要在不修改类的源码的基础之上,改变类的⽅法
* 1. 继承(在这⾥没法⽤)
* 2. 动态代理(代理模式)
* 3. 装饰者模式
* 1. 装饰者和被装饰者要实现相同的接⼝
* 2. 将被装饰者的对象传⼊装饰者中
* 3. 不需要修改的⽅法,直接调⽤被装饰者的⽅法;需要
修改的⽅法,由装饰者重写
*/
public class MyDataSource3 implements
DataSource{
private LinkedList<Connection>
connectionPool = new LinkedList<>();
public MyDataSource3() {
//初始化往connectionPool中存放5个连接
for (int i = 0; i < 5; i++) {
try {
//创建⼀个装饰后的连接
Connection conn = new
WrapperConnection(DriverManager.getConnection("
jdbc:mysql:///day20?
characterEncoding=utf8","root","123"),connectio
nPool);
//将连接添加到connectionPool中
connectionPool.add(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Override
public Connection getConnection() throws
SQLException {
//如果容器中有连接,则从容器中获取,如果容器中
没有连接,就直接新创建连接
Connection conn = null;
if (connectionPool.size() > 0){
//从容器中获取连接
conn =
connectionPool.removeFirst();
}else {
//则新创建连接
conn =
DriverManager.getConnection("jdbc:mysql:///day2
0?characterEncoding=utf8", "root", "123");
}
return conn;
}
@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;
}
}
小结
1.创建一个MyConnection实现Connection
2.创建MyConnection得到被增强的Connection对象
3.改写MyConnection里面的close()方法逻辑为归还
4.MyConnection里面的其它方法,调用被增强的connection
5.在MyConnection03的getConnection()方法里面返回了MyConnection
自定义连接池扩展版本-使用动态代理
使用动态代理创建Connection对象代理对象,增强Connection的close方法
/**
* 包名:com.sunlw.customer.datasource
*
* @author sunlw
* ⽇期2020-07-06 10:01
* ⾃定义连接池的第四版:
* 1. 可以归还连接
* 2. 如果是原本在连接池中的连接,就归还;如果是新创建的
连接⽤完后就销毁
*
* 使⽤动态代理技术,增强connection的close⽅法
*/
public class MyDataSource4 implements
DataSource{
private LinkedList<Connection>
connectionPool = new LinkedList<>();
public MyDataSource4() {
//初始化往connectionPool中存放5个连接
for (int i = 0; i < 5; i++) {
try {
Connection connection =
DriverManager.getConnection("jdbc:mysql:///day2
0?characterEncoding=utf8", "root", "123");
ClassLoader classLoader =
connection.getClass().getClassLoader();
//创建动态代理对象
Connection connectionProxy =
(Connection)
Proxy.newProxyInstance(classLoader, new Class[]
{Connection.class}, new InvocationHandler() {
@Override
public Object invoke(Object
proxy, Method method, Object[] args) throws
Throwable {
//判断执⾏⽅法是否是close⽅
法,如果是,则增强,如果不是则执⾏被代理者原本的⽅法
if
(method.getName().equals("close")) {
//增强close
//将当前这个连接对象,
添加到容器中
connectionPool.addLast((Connection) proxy);
return null;
}
//不需要增强的⽅法,就执⾏原
本的⽅法
return
method.invoke(connection,args);
}
});
//将连接添加到connectionPool中
connectionPool.add(connectionProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public Connection getConnection() throws
SQLException {
//如果容器中有连接,则从容器中获取,如果容器中
没有连接,就直接新创建连接
Connection conn = null;
if (connectionPool.size() > 0){
//从容器中获取连接
conn =
connectionPool.removeFirst();
}else {
//则新创建连接
conn =
DriverManager.getConnection("jdbc:mysql:///day2
0?characterEncoding=utf8", "root", "123");
}
return conn;
}
@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;
}
}
第三方连接池
常用连接池
1.目标
常用连接池
2.分析
通过前面的学习,我们已经能够使用所学的基本知识构建自定义的连接池了,其目是锻炼大家的基本功,帮助大家更好的理解来南京池的原理,但现实残酷的,我们所定义的连接池和第三方的连接池相比,还是显得渺小,工作里面都会用第三方连接池
3.讲解
常用的第三方连接池如下:
C3P0是一个开源的JDBC连接池,他实现了数据源和JND绑定,支持JDBC3规范和JDBC2的标准扩展,C3P0是异步骤作的,所有一些操作时间过长JDBC通过其它的辅助线程完成,目前使用它的开源目有Hibernate,Spring等,C3P0有自动回收空闲连接功能
阿里巴巴-德鲁伊druid连接池:
Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池,插件框架和SQL解析器组成,该项目主要为了扩展JDBC的一些限制可以程序员实现一些特殊的需求,
DBCP(DataBase Connection Pool)数据库连接池是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件,dbcp没有自动回收空闲连接的功能,
4.小结
我们工作里面的用的比较的是:
C3P0 druid 光连接池
C3P0使用
1.目标
掌握C3P0的使用
2.路径
1.c3p0介绍
2.c3p0 的使用(硬编码)
3.c3p0的使用(配置文件)
4.编写C3P0til工具类
3.讲解
c3p0介绍
C3P0开源免费的连接池!目前使用它的开源项目有:Spring,Hibernte等,使用第三方工具需要导入jar包 c3p0使用时还需要添加配置文件c3p0-config.xml使用C3P0需要添加c3p0-0.9.1.2..jar
c3p0使用
通过硬编码来编写
步骤
1.拷贝jar
2.创建C3P0连接池对象
3.从C3P0连接池对象里面获得connection
实现
ComboPooledDataSource cpds = new
ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/we
b10");
cpds.setUser("root");
cpds.setPassword("123456");
Connection connection = cpds.getConnection();
通过配置文件编写
步骤:
1.拷贝jar
2.拷贝配置文件(c3p0-config.xml)到src目录【名字不要改】
3.创建C3P0连接池对象【自动的读取】
4.从池子里面获得li连接
实现:编写配置文件c3p0-config.xml, 放在src目录下(注:文件名一定不要改)
<c3p0-config>
<default-config>
<property
name="driverClass">com.mysql.jdbc.Driver</prope
rty>
<property
name="jdbcUrl">jdbc:mysql://localhost:3306/web1
1</property>
<property name="user">root</property>
<property name="password">123</property>
<property
name="initialPoolSize">5</property>
</default-config>
</c3p0-config>
编写java代码(会自动读取resources目录下的c3p0-config.xml.所以不需要我们解析配置文)
DataSource ds = new ComboPooledDataSource();
使用c3p0改写工具类
编写C3P0til工具类,提供DataSource对象,保证整个项目只有一个DataSource对象
/**
* 包名:com.sunlw.utils
*
* @author sunlw
* ⽇期2020-07-06 11:43
* 这个⼯具类就负责,提供C3P0连接池对象
*/
public class C3P0Util {
private static DataSource dataSource;
static {
dataSource = new
ComboPooledDataSource();
}
/**
* 获取连接池
* @return
*/
public static DataSource getDataSource(){
return dataSource;
}
}
小结
1.C3P0配置文件方式使用拷贝配置文件到resources【配置文件的名字不要改】创建C3P0连接池对象
2.C3P0工具类 保证DataSource连接只有一个【static】
知识点-DRUID
1.目标
掌握DRUID连接池的使用
2.路径
1.DRUID的介绍
2.DRUID的使用(硬编码方式)
3.DRUID的使用(配置文件方式)
4.DRUID抽取成工具类
3.讲解
DRUID介绍
Druid是阿里巴巴开发的号称监控而生的数据库连接池 Druid是国内目前最好的数据库连接池,在功能,性能,扩展性方面,都超过其他数据库连接池,Druid已经在阿里巴巴部著了超过600给应用,经过很久生产环境大规模部著的严葛考验;如:一年一度双十活动。每年春运的抢火车票
Druid的下载地址
https://github.com/alibaba/druid
DRUID连接池使用的jar包:druid-1.0.9.jar
DRUID的使用
通过硬编码方式
步骤:
1.导入.DRUIDjar包
2.创建Druid连接池对象,配置4个基本参数
3.从Druid连接对象获得Connection
实现
通过配置文件方式
步骤:
1.导入DRUIDjar包
2.拷贝配置文件到src目录
3.根据配置文件创建Druid连接池对象
4.从Druid连接池对象获得Connection
实现
创建 druid.propertise放在src目录下
编写java代码
Druid工具类
小结:
Druid配置文件使用
拷贝jar
拷贝配置文件到src
读取配置文件或propeties对象
使用工厂根据prpertise创建DataSource
从DataSource获取