Druid的properties文件:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=UTF8&useSSL=false
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
Druid的jar:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
</dependencies>
Druid的Utils:
package com.kirn.datasource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* @author FaMing
* @date 2022/3/18
*/
public class DruidPoolUtils {
private static DataSource ds=null;
/**
* 创建连接池:
* 1.使用当前线程类加载器加载磁盘配置文件
* 2.创建Properties对象,载入配置文件
* 3.创建Druid连接池对象,将Properties注入druid连接池
*/
static{
try(
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("druid.properties")
) {
if(null==in){
throw new RuntimeException("加载配置文件失败");
}
Properties properties = new Properties();
properties.load(in);
//使用工厂模式创建连接池,向我们隐藏了连接池的细节,只为我们暴漏对象
ds = DruidDataSourceFactory.createDataSource(properties);
}catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("连接池配置文件初始化失败");
}
}
/**
* 将创建好的druid连接池给外界使用
* @return
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 从连接池对象中获取Connection
*/
public static Connection getConnection() throws Exception {
return ds.getConnection();
}
}
Druid的场景验证:
package com.kirn.datasource;
import org.junit.Test;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* @author FaMing
* @date 2022/3/18
*/
public class DruidPoolTest {
@Test
public void DataSourceTest1(){
/**
* 编译期是DataSource接口类型,运行期是Druid连接池对象
*
* {
* CreateTime:"2022-03-18 23:13:47",
* ActiveCount:0, 活动连接总数
* PoolingCount:0, 连接池容器中的连接总数
* CreateCount:0, 创建连接池的总数
* DestroyCount:0, 销毁的总数
* CloseCount:0, 归还得总数
* ConnectCount:0, 连接总数
* Connections:[ 连接池里面的容器,里面存储了Connection连接对象
* ]
* }
* 特点:
* 创建连接池对象不会立马创建初始连接池大小,此时连接池对象Connection此时是0个
* 此时只创建了连接池对象并没有获取连接池对象里面的连接connection
*/
DataSource dataSource = DruidPoolUtils.getDataSource();
System.out.println(dataSource);
}
/**
* 获取连接池连接对象
*/
@Test
public void getconnection(){
try {
/**
* 通过代码调试:第一次调用该方法获取连接会阻塞,因为池子里面没有连接对象Connection
*
*/
Connection connection = DruidPoolUtils.getConnection();
System.out.println(connection);
//此时看看获取连接对象后,连接池里面有多少个
System.out.println("===============================");
/**
* 第一次获取连接池的Connection对象成功之后的连接池打印结果
* {
* CreateTime:"2022-03-18 23:26:15",
* ActiveCount:1, 活动连接数
* PoolingCount:4,连接池里面没有使用的连接总数
* CreateCount:5, 连接池创建总数
* DestroyCount:0, 销毁总数
* CloseCount:0, 归还总数
* ConnectCount:1, 连接数量
* Connections:[ 此时连接池里面总共还有多少个没使用
* {ID:690521419, ConnectTime:"2022-03-18 23:26:18", UseCount:0, LastActiveTime:"2022-03-18 23:26:18"},
* {ID:665726928, ConnectTime:"2022-03-18 23:26:18", UseCount:0, LastActiveTime:"2022-03-18 23:26:18"},
* {ID:689401025, ConnectTime:"2022-03-18 23:26:18", UseCount:0, LastActiveTime:"2022-03-18 23:26:18"},
* {ID:790067787, ConnectTime:"2022-03-18 23:26:18", UseCount:0, LastActiveTime:"2022-03-18 23:26:18"}
* ]
* }
*
* 为什么会在第一次加载的时候没有创建呢?
* Druid用到了“延迟加载”或者“缓释初始化”机制,懒加载
* 理解:第一次调用DataSource里面的getConnection()方法时,Druid连接池会判断有没有连接对象
* 如果没有那么就会根据配置文件的配置信息创建初始化连接大小的连接对象Connection
*
*/
DataSource dataSource = DruidPoolUtils.getDataSource();
System.out.println(dataSource);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接池对象,获取多个,观察第二次调用getConnection()方法是否会阻塞
*
* 结果认证:第二次调用不会阻塞,因为池子里面已经存在连接对象了
*/
@Test
public void getConnectionList() throws Exception {
//获取连接池,总共配置了5个
Connection connection1 = DruidPoolUtils.getConnection();
Connection connection2 = DruidPoolUtils.getConnection();
Connection connection3 = DruidPoolUtils.getConnection();
Connection connection4 = DruidPoolUtils.getConnection();
System.out.println(connection1);
System.out.println(connection2);
System.out.println(connection3);
System.out.println(connection4);
//因为druid里面初始化大小为5个,现在获取了4个,那么池子里面只有1了
/**
* 因为我调用了池子里面的连接对象后,没有归还,所有只会存在一个了
* 归还方式:
* 1.显示调用close()方法
* 2.try with resource
* {
* CreateTime:"2022-03-18 23:39:08",
* ActiveCount:4,
* PoolingCount:1,
* CreateCount:5,
* DestroyCount:0,
* CloseCount:0,
* ConnectCount:4,
* Connections:[
* {ID:790067787, ConnectTime:"2022-03-18 23:39:10", UseCount:0, LastActiveTime:"2022-03-18 23:39:10"}
* ]
* }
*/
DataSource dataSource = DruidPoolUtils.getDataSource();
System.out.println(dataSource);
}
/**
* 获取连接,然后归还连接,观察连接池中的大小
*/
@Test
public void getConnection3(){
Connection connection = null;
try {
connection = DruidPoolUtils.getConnection();
/**
* 没归还
* {
* CreateTime:"2022-03-18 23:44:16",
* ActiveCount:1,
* PoolingCount:4,
* CreateCount:5,
* DestroyCount:0,
* CloseCount:0,
* ConnectCount:1,
* Connections:[
* {ID:2045766957, ConnectTime:"2022-03-18 23:44:19", UseCount:0, LastActiveTime:"2022-03-18 23:44:19"},
* {ID:690521419, ConnectTime:"2022-03-18 23:44:19", UseCount:0, LastActiveTime:"2022-03-18 23:44:19"},
* {ID:665726928, ConnectTime:"2022-03-18 23:44:19", UseCount:0, LastActiveTime:"2022-03-18 23:44:19"},
* {ID:689401025, ConnectTime:"2022-03-18 23:44:19", UseCount:0, LastActiveTime:"2022-03-18 23:44:19"}
* ]
* }
*/
System.out.println(connection);
DataSource dataSource1 = DruidPoolUtils.getDataSource();
System.out.println(dataSource1);
connection.close();
System.out.println("================================归还后");
/**
* 很明显,归还回去了
* {
* CreateTime:"2022-03-18 23:46:19",
* ActiveCount:0,
* PoolingCount:5,
* CreateCount:5,
* DestroyCount:0,
* CloseCount:1,
* ConnectCount:1,
* Connections:[
* {ID:2045766957, ConnectTime:"2022-03-18 23:46:22", UseCount:0, LastActiveTime:"2022-03-18 23:46:22"},
* {ID:690521419, ConnectTime:"2022-03-18 23:46:22", UseCount:0, LastActiveTime:"2022-03-18 23:46:22"},
* {ID:665726928, ConnectTime:"2022-03-18 23:46:22", UseCount:0, LastActiveTime:"2022-03-18 23:46:22"},
* {ID:689401025, ConnectTime:"2022-03-18 23:46:22", UseCount:0, LastActiveTime:"2022-03-18 23:46:22"},
* {ID:817348612, ConnectTime:"2022-03-18 23:46:22", UseCount:1, LastActiveTime:"2022-03-18 23:46:22"}
* ]
* }
*/
System.out.println(connection);
System.out.println("==================================");
DataSource dataSource = DruidPoolUtils.getDataSource();
System.out.println(dataSource);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 目前线程池初始化大小数量只有5个,当全部获取出来之后,继续获取会怎样?
*/
@Test
public void getConnection4() throws Exception {
//获取连接池,总共配置了5个
Connection connection1 = DruidPoolUtils.getConnection();
Connection connection2 = DruidPoolUtils.getConnection();
Connection connection3 = DruidPoolUtils.getConnection();
Connection connection4 = DruidPoolUtils.getConnection();
Connection connection5 = DruidPoolUtils.getConnection();
System.out.println(connection1);
System.out.println(connection2);
System.out.println(connection3);
System.out.println(connection4);
System.out.println(connection5);
System.out.println("==============获取完线程池内连接对象不归还====================");
DataSource dataSource = DruidPoolUtils.getDataSource();
System.out.println(dataSource);
System.out.println("=======继续获取连接池连接对象,观察会发生什么========");
Connection connection6 = DruidPoolUtils.getConnection();
System.out.println(connection6);
DataSource dataSource1 = DruidPoolUtils.getDataSource();
System.out.println(dataSource1);
}
/**
* 连接池中究竟应该配置多大?
* 连接数=(cpu核心数*2)+有效磁盘数
* 为什么呢?
* CPU ,磁盘, 网络, 内存
* 首先如果我们是8核CPU的情况,如果我们设置了8个连接,那么这时候的效率是最高的
* 但是那么考虑到磁盘,那么就会涉及到IO的读取,那么在读取的过程中,我们的CPU是处在空闲的状态
* 所以我们让连接数乘以2,网络同理,内存同理;
*
*
* 配置连接池中的参数:
* 1. maxActive 连接池支持的最大连接数,这里取值为20,表示同时最多有20个数据库连接。一般把maxActive设置成可能的并发量就行了设 0 为没有限制。
*
* 2. maxIdle 连接池中最多可空闲maxIdle个连接 ,这里取值为20,表示即使没有数据库连接时依然可以保持20空闲的连接,而不被清除,随时处于待命状态。设 0 为没有限制。
*
* 3. minIdle 连接池中最小空闲连接数,当连接数少于此值时,连接池会创建连接来补充到该值的数量
*
* 4. initialSize 初始化连接数目
*
* 5. maxWait 连接池中连接用完时,新的请求等待时间,毫秒,这里取值-1,表示无限等待,直到超时为止,也可取值9000,表示9秒后超时。超过时间会出错误信息
*
* 6. removeAbandoned 是否清除已经超过“removeAbandonedTimout”设置的无效连接。如果值为“true”则超过“removeAbandonedTimout”设置的无效连接将会被清除。设置此属性可以从那些没有合适关闭连接的程序中恢复数据库的连接。
*
* 7. removeAbandonedTimeout 活动连接的最大空闲时间,单位为秒 超过此时间的连接会被释放到连接池中,针对未被close的活动连接
*
* 8. minEvictableIdleTimeMillis 连接池中连接可空闲的时间,单位为毫秒 针对连接池中的连接对象
*
* 9. timeBetweenEvictionRunsMillis / minEvictableIdleTimeMillis 每timeBetweenEvictionRunsMillis毫秒秒检查一次连接池中空闲的连接,把空闲时间超过minEvictableIdleTimeMillis毫秒的连接断开,直到连接池中的连接数到minIdle为止.
*/
}
C3p0的解释:
package com.kirn.datasource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.lang.reflect.Field;
import java.sql.Connection;
/**
* 池化思想
* 字符串常量池 线程池 数据连接池
* 表示就是需要就去创建,相当于一个容器,先初始化一些连接,当需要的访问的时候就去池里面拿,如果用完了
* 就还回去,这样子就防止了资源的浪费
*
* c3p0数据库连接中,首先获取一个connection,它实则是用的一个代理类去代理连接的:NewProxyConnection
* 从这个类进去它是实现了connection这个接口的,这个接口又实现了DataSource的,在NewProxyConnection代理对象中
* 实际代理的真实对象就是Connection inner
*
* 比如连接池数量设置了1个,最大连接数量也设置1个,那么每一次访问获取的Connection inner都是相等的,但是它只是获取的代理对象不一样;
* @author FaMing
* @date 2022/3/15
*/
public class C3p0DataSource1 {
public static void main(String[] args) throws Exception {
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/myuser");
//连接池数量
dataSource.setInitialPoolSize(1);
//最大连接数量
dataSource.setMaxPoolSize(1);
Connection connection = dataSource.getConnection();
Object inner = getInner(connection);
connection.close();
Connection connection1 = dataSource.getConnection();
//这里获取的是代理对象,所以false
System.out.println(connection1==connection);
//实际底层获取的连接对象就一个,若要对比就要反射获取inner
Object inner1 = getInner(connection1);
System.out.println(inner1.getClass().getName());
System.out.println(inner.getClass().getName());
System.out.println(inner1==inner);
}
public static Object getInner(Object connection) {
Object result=null;
Field field=null;
try {
//获取NewProxyConnection代理对象里面的真实连接对象Connection inner
field=connection.getClass().getDeclaredField("inner");
field.setAccessible(true);
result = field.get(connection);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}