文章目录
- 一.数据库连接池的必要性
- (一).传统数据库连接模式的的步骤
- (二).传统数据库连接模式存在的问题
- 二.数据库连接池技术
- (一).数据连接池的思想:
- (二).数据库连接池的任务:
- (三).数据库连接池的规模:
- (四).工作原理:
- (五).数据库连接池的优点:
- 三.多种开源的数据库连接池
- 四.学习最主流的数据库连接池Druid
- (一).创建连接(配置文件方式)
- 简易增删改查
一.数据库连接池的必要性
(一).传统数据库连接模式的的步骤
- 在主程序中创建连接
- 进行sql操作
- 关闭数据库连接
(二).传统数据库连接模式存在的问题
-
浪费时间:每次连接时都要验证登录和将connection加载到内存,
-
不能大规模的访问数据库:当数据库访问人数过多时,占用大量系统资源,会导致服务器崩溃
-
存在内存泄漏问题:每次连接都需要断开连接,如果不断开,程序运行结束,会有创建的连接对象存在内存中一直无法关闭,就会导致java内存泄漏的问题。
内存泄漏:指创建的对象无法被回收
二.数据库连接池技术
(一).数据连接池的思想:
事先在内存中建立一个缓冲池,用来存放一定数量的连接对象,需要时在里面调用,结束时放回缓冲池。
(二).数据库连接池的任务:
管理和释放数据库连接,允许用户使用池内的连接对象,而不需要创建对象。
(三).数据库连接池的规模:
初始化时的数量:由数据库最小连接数来设定;
最大数量:由最大数据库连接数来确定。
当连接数超过了最大连接数,超过的连接就会停止等待连接对象的释放。
(四).工作原理:
(五).数据库连接池的优点:
-
资源重用:
连接池中的对象需要时取出,不需要被连接池回收 -
更快的反应速度:
事先在池中储备连接对象,初始化已经完成,直接调用。 -
数据库共享机制
多个用户访问同一数据库,通过在应用层的配置,可以避免资源独占。 -
避免内存泄漏:
连接对象统一管理,设置连接对象时间片,超时强制回收。
三.多种开源的数据库连接池
JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource是一个接口,该接口通常由服务器提供。
常见的开源数据库连接池:
DBCP:速度比C3P0快但有bug
c3p0:速度慢,但相对稳定
Proxool:开源连接池,有监控连接池的功能,但稳定性比C3P0差
BoneCP:速度快,开源
Druid:阿里提供的连接池,速度快(不及BoneCP),稳定性好,有监控连接池的功能。
四.学习最主流的数据库连接池Druid
Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 和Proxool 优点于一身的数据库连接池,它是目前国内用到最多的数据库连接池技术。
(一).创建连接(配置文件方式)
- 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<dependency>
<!--mysql版本对应-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
- 配置文件
创建配置文件druid.properties
,并输入配置信息
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=12345678
initialSize=10
maxActive=20
- 详细的配置参数
配置 | 缺省 | 说明 |
---|---|---|
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this) | |
url | 连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
username | 连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/使用ConfigFilter | |
driverClassName | 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下) | |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
maxIdle | 8 | 已经不再使用,配置了也没效果 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
maxOpenPreparedStatements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 | |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
timeBetweenEvictionRunsMillis | 有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明 | |
numTestsPerEvictionRun | 不再使用,一个DruidDataSource只支持一个EvictionRun | |
minEvictableIdleTimeMillis | ||
connectionInitSqls | 物理连接初始化的时候执行的sql | |
exceptionSorter | 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接 | |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall | |
proxyFilters | 类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系 |
- 封装连接工具类
public class DruidUtils {
private static DataSource source;
/**
* 静态代码块中加载配置文件,随着类的加载而执行
*/
static {
try {
//创建properties对象,用来封装从文件中获取的流数据
Properties pros = new Properties();
//采用类加载方式获取文件的内容,并封装成流
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("Druid.properties");
//将流传入到pros对象中
pros.load(is);
//利用工厂类创建数据库连接池
source = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接 直接使用数据库连接池对象条用getConnection()方法
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception {
return source.getConnection();
}
/**
* 关闭连接
* @param connection
* @param stm
* @param rs
*/
public static void close(Connection connection, Statement stm, ResultSet rs) {
try {
if (connection != null) connection.close();
} catch (Exception throwable) {
throwable.printStackTrace();
}
try {
if (stm != null) stm.close();
} catch (Exception throwable) {
throwable.printStackTrace();
}
try {
if (rs != null) rs.close();
} catch (Exception throwable) {
throwable.printStackTrace();
}
}
/**
* 重载close方法
*/
public static void close(Connection conn, Statement stm) {
close(conn, stm, null);
}
}
- 测试类
/**
* 测试连接是否成功
* @throws Exception
*/
@Test
public void getDruidConnection() throws Exception {
Connection conn = DruidUtils.getConnection();
System.out.println(conn);
}
返回值如下,连接成功
简易增删改查
- 实体类
public class ApplicationDO {
/**
* 主键ID
*/
private Long id;
/**
* 申请类型 0 未知,1 license,2 soultion,3 both
*/
private Integer applyType;
/**
* teamwork项目名称
*/
private String teamworkProjectName;
/**
* 项目名称
*/
private String projectName;
/**
* 客户名称
*/
private String customName;
/**
* 获取主键ID
*
* @return id - 主键ID
*/
public Long getId() {
return id;
}
/**
* 设置主键ID
*
* @param id 主键ID
*/
public void setId(Long id) {
this.id = id;
}
/**
* 获取申请类型 0 未知,1 license,2 soultion,3 both
*
* @return apply_type - 申请类型 0 未知,1 license,2 soultion,3 both
*/
public Integer getApplyType() {
return applyType;
}
/**
* 设置申请类型 0 未知,1 license,2 soultion,3 both
*
* @param applyType 申请类型 0 未知,1 license,2 soultion,3 both
*/
public void setApplyType(Integer applyType) {
this.applyType = applyType;
}
/**
* 获取teamwork项目名称
*
* @return teamwork_project_name - teamwork项目名称
*/
public String getTeamworkProjectName() {
return teamworkProjectName;
}
/**
* 设置teamwork项目名称
*
* @param teamworkProjectName teamwork项目名称
*/
public void setTeamworkProjectName(String teamworkProjectName) {
this.teamworkProjectName = teamworkProjectName;
}
/**
* 获取项目名称
*
* @return project_name - 项目名称
*/
public String getProjectName() {
return projectName;
}
/**
* 设置项目名称
*
* @param projectName 项目名称
*/
public void setProjectName(String projectName) {
this.projectName = projectName;
}
/**
* 获取客户名称
*
* @return custom_name - 客户名称
*/
public String getCustomName() {
return customName;
}
/**
* 设置客户名称
*
* @param customName 客户名称
*/
public void setCustomName(String customName) {
this.customName = customName;
}
@Override
public String toString() {
return "ApplicationDO{" +
"id=" + id +
", applyType=" + applyType +
", teamworkProjectName='" + teamworkProjectName + '\'' +
", projectName='" + projectName + '\'' +
", customName='" + customName + '\'' +
'}';
}
}
- 增删改查
/**
* 查询
* @throws Exception
*/
@Test
public void Select() throws Exception {
//因为获取连接创建的是静态方法 直接使用类名.方法名调取 获得连接即可
Connection conn= DruidUtils.getConnection();
String sql="SELECT * FROM licenx_application";
//获取执行者对象
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
ApplicationDO applicationDO;
List<ApplicationDO> applicationDOs=new ArrayList<>();
//遍历ResultSet结果集 存入List
while (rs.next()){
applicationDO=new ApplicationDO();
applicationDO.setId(rs.getLong("id"));
applicationDO.setCustomName(rs.getString("custom_name"));
applicationDO.setApplyType(rs.getInt("apply_type"));
applicationDO.setTeamworkProjectName(rs.getString("teamwork_project_name"));
applicationDO.setProjectName(rs.getString("project_name"));
applicationDOs.add(applicationDO);
}
//输出list查看
for (ApplicationDO applicationDO1 : applicationDOs) {
System.out.println(applicationDO1);
}
DruidUtils.close(conn,pstmt,rs);
}
/**
* 插入
* @throws Exception
*/
@Test
public void insert() throws Exception {
//获取数据库连接
Connection conn=DruidUtils.getConnection();
String sql="insert into licenx_application (custom_name, apply_type, teamwork_project_name, project_name) values (?, ?, ?, ?)";
//获取执行者对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置sql语句中的?值
pstmt.setObject(1,"测试");
pstmt.setObject(2,1);
pstmt.setObject(3,"测试");
pstmt.setObject(4,"测试");
int i = pstmt.executeUpdate();
if (i>0){
System.out.println("添加成功");
}
//释放资源
DruidUtils.close(conn,pstmt);
}
/**
* 删除
* @throws Exception
*/
@Test
public void delete() throws Exception {
//获取连接
Connection conn=DruidUtils.getConnection();
String sql="DELETE from licenx_application where id=?";
//获取执行者对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置sql中的?值
pstmt.setObject(1,251757598242504708l);
int i = pstmt.executeUpdate();
if (i>0) System.out.println("删除成功");
//关闭资源
DruidUtils.close(conn,pstmt);
}
/**
* 更新
* @throws Exception
*/
@Test
public void update() throws Exception {
//获取连接
Connection conn=DruidUtils.getConnection();
String sql="UPDATE licenx_application SET teamwork_project_name=? WHERE id=?";
//获取执行者对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置sql中?的值
pstmt.setObject(1,"99999");
pstmt.setObject(2,251757598242504706l);
int i = pstmt.executeUpdate();
if (i>0) System.out.println("修改成功");
//释放资源
DruidUtils.close(conn,pstmt);
}