1 连接池概述
- 连接对象的使用问题:
我们原先连接数据库都是用一次创建连接一次,不用的话就直接关闭了,然后在使用在创建,这样就造成了占用资源过多加重服务器负担,耗时。 - 解决:
解决这些问题,我们要考虑如何提高连接速度,还有就是如何提高使用率。
下面会详细介绍一种最实用的数据库连接池的详细介绍及其使用------德鲁伊(druid)
经过上面的介绍,我们需要创建一个连接池,以供我们连接数据库操作,在没用连接池之前,用户访问数据库的时候都是自己创建连接对象的。
我们使用了连接池之后:从系统开始启动的时候就会创建一个工厂对象,里面有一定数量的数据库连接对象,用户使用的时候直接会从池子里拿连接对象,不需要自己在创建了。
连接池解决现状问题的原理
Connection连接对象 | 操作特点 |
---|---|
创建时 | 连接对象不再由自己创建,而是系统启动的时候已经创建一定数量的连接, 并且放在连接池中 |
使用时 | 直接从连接池中去获取一个已经创建好的连接对象即可 |
关闭时 | 不是真的关闭连接对象,而是将连接对象再放回到连接池中,供下一个用户使用 |
- 数据源接口:javax.sql.DataSource接口
数据源接口中的方法:
DataSource接口中的方法 | 描述 |
---|---|
Connection getConnection() | 从连接池中获取连接对象 |
每个连接池都会有很多的参数,每个参数都有不同的含义,几乎所有的参数都是由默认值的,参数名在不同的连接池中代表的意思也有所差异!
常用参数 | 描述 |
---|---|
初始连接数 | 服务器启动的时候创建的连接对象数量 |
最大连接数 | 连接池中最多可以允许放多少个连接对象 |
最长等待时间 | 如果连接池中没有连接对象,设置用户等待的最长时间是多久,单位是毫秒。 如果超过这个时间就抛出异常 |
最长空闲回收时间 | 如果一个连接对象长时间没有人使用,设置多久回收这个对象,默认是不回收。 |
2 常用的连接池(我们首选druid)
常用连接池的介绍
DataSource本身是Oracle公司提供的一个接口,本身没有具体的实现,它的实现由各大连接池的数据库厂商去实现,我们只需要学习如何使用就ok了。
常用的连接池组件:
- 阿里巴巴-德鲁伊druid连接池:Druid是阿里巴巴开源平台上的一个项目(主要)。
- DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。
- C3P0是一个开源的JDBC连接池,目前使用它的开源项目有Hibernate,Spring等。C3P0有自动回收空闲连接功能。
使用Druid连接池
DRUID简介
Druid是阿里巴巴开发的号称为监控而生的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。如:一年一度的双十一活动,每年春运的抢火车票。
Druid的下载地址:https://github.com/alibaba/druid
DRUID连接池使用的jar包:druid-1.0.9.jar
常用的配置参数
参数 | 说明 |
---|---|
url | 连接字符串 |
username | 用户名 |
password | 密码 |
driverClassName | 驱动类名,会自动根据URL识别,这一项可以不配置 |
initialSize | 初始连接数 |
maxActive | 最大连接数 |
maxWait | 最长等待时间 |
Druid连接池API介绍
- 得到配置文件的输入流
Class类中的方法 | 说明 |
---|---|
InputStream getResourceAsStream(String path) | 加载类路径下配置文件,转成一个输入流对象 |
- Properties类的方法,读取属性文件中的键和值,并且加载到集合中
- 通过Druid工厂的静态方法创建连接池,提供属性集合作为参数
DruidDataSourceFactory的方法 | 方法 |
---|---|
public static DataSource createDataSource(Properties properties) | 通过属性集合中属性,创建一个连接池 |
3 使用Druid连接池
案例演示:获取连接对象
导包:
步骤
- 在src目录下创建一个properties文件,文件名随意,设置上面的参数
- Java代码
- 加载properties文件的内容到Properties对象中
- 使用工厂类,创建DRUID连接池,使用配置文件中的参数
- 从DRUID连接池中取出10个连接输出
.properties配置文件:
url=jdbc:mysql://localhost:3306/test
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=2000
java代码:
public class Demo2Druid {
public static void main(String[] args) throws Exception {
//1.从类路径下加载配置文件,获取一个输入流。如果不指定路径,默认是读取同一个包下资源文件
InputStream inputStream = Demo2Druid.class.getResourceAsStream("/druid.properties");
//2.使用Properties对象的方法将配置文件中属性加载到Properties对象中
Properties properties = new Properties();
//加载了配置文件中所有的属性
properties.load(inputStream);
//3.通过druid的工厂类创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//获取10个连接对象
for (int i = 1; i <= 11; i++) {
Connection connection = dataSource.getConnection();
System.out.println("第" + i + "个连接对象:" + connection);
//第3个连接关闭
if (i==3) {
connection.close();
}
}
}
}
但是如果超过了数据库最大连接数量:
但是我们让第三个关闭了连接,相当于还给连接池一个连接对象,所以会打印是以个结果:(有两个地址值是相同的!)
4 数据工具类再增强
分析
使用Druid连接池来获取连接对象,达到提升访问数据库速度目的
- 去掉类中与数据库连接有关的代码
- 得到数据库的连接,从连接池中获取
- 新加一个方法,获取连接池对象
- 在类一开始加载就能够获取连接池(数据源)对象,在静态代码块中创建连接池
代码
package com.aoshen.Test;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class JDBCUtils {
//声明连接池对象
private static DataSource dataSource;
//使用静态,是类加载的时候就创建连接池
static{
try {
//读取配置文件
InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("druid");
//获取Properties对象,加载到该对象中
Properties properties = new Properties();
//获取配置文件
properties.load(inputStream);
//创建druid工厂
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取数据库连接
public static Connection getConn() throws SQLException {
return dataSource.getConnection();
}
/**
* 关闭连接
* 查询调用这个方法
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭连接
* 增删改没有结果集
*/
public static void close(Connection connection, Statement statement) {
//直接调用上面的方法
close(connection, statement, null);
}
/**
* 通用的增删改方法
*/
public static int update(String sql,Object...args){
Connection conn = null;
PreparedStatement ps = null;
//返回影响的行数
int row = 0;
try{
//获取连接
conn = getConn();
//获取预编译对象
ps = conn.prepareStatement(sql);
//获取元数据,得到有多少占位符
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount();
//循环获取赋值
for (int i = 0; i < count; i++) {
ps.setObject(i+1,args[i]);
}
//执行SQL语句
row = ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
close(conn,ps);
}
return row;
}
/**
* 通用的查询方法
*/
public static <T> List<T> equery(String sql,Class<T>c,Object...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
//创建集合用于接收数据库中查的值
List<T>list = new ArrayList<>();
try{
//获取连接
conn = getConn();
//获取预编译对象
ps = conn.prepareStatement(sql);
//通过获取元数据给占位符赋值
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount();
for (int i = 0; i < count; i++) {
ps.setObject(i+1,args[i]);
}
//执行sql
rs = ps.executeQuery();
//遍历集合,封装到集合中吗,一行数据封装一个对象
while (rs.next()){
//每条记录封装成一个对象
T t = c.newInstance();
//得到实体类中有哪些列名
Field[] fields = c.getDeclaredFields();
//遍历赋值
for (Field field : fields) {
//获取列名
String name = field.getName();
//获取内容
Object value = rs.getObject(name);
//因为是私有的,要暴力反射
field.setAccessible(true);
//把最后得到的值赋值给创建的对象中
field.set(t,value);
}
//把最后含每一行值的对象添加到集合中
list.add(t);
}
}catch (Exception e){
e.printStackTrace();
}finally {
close(conn,ps,rs);
}
return list;
}
}
使用工具类
/**
* 使用工具类
*/
public class Demo3UseUtils {
public static void main(String[] args) {
//使用工具类添加1条记录
int row = JdbcUtils.update("insert into student values(null,?,?,?)", "嫦娥", 0, "1997-07-07");
System.out.println("添加了" + row + "条");
//使用工具类查询所有的数据
List<Student> students = JdbcUtils.query("select * from student", Student.class);
//打印
students.forEach(System.out::println);
}
}
小结
修改了连接的获取方式
- 在静态代码块中创建连接池对象
- 添加了获取数据源的方法
- 修改了获取连接的方法,从连接池中获取连接对象