目录
数据库连接池
传统获取Connection问题分析
- 传统的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再验证IP地址,用户名和密码(0.05s~1s时间).需要数据库连接 的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃.
- 每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终导致重启数据库.
- 传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MySQL崩溃. 解决传统开发中数据库连接问题,可以采用数据库连接池技术
数据库连接池基本介绍
- 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从 "缓冲池" 中取出一个,使用完毕之后再放回去
- 数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是建立一个
- 当应用程序向连接池请求的连接数量超过最大连接数量时,这些请求将被加入到等待队列中
数据库连接池种类
- JDBC数据库连接池使用javax.sql.DataSource 来表示,DataSource只是一个接口,该接口通常由第三方提供实现
- C3P0数据库连接池,速度相对较慢,稳定性不错 (hibernate,spring)
- DBCP 数据库连接池,速度相对c3p0较快,但不稳定
- Proxool数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
- BoneCP 数据库连接池,速度快
- Druid(德鲁伊) 是阿里提供的数据库连接池,集DBCP,C3P0,Proxool优点于一身的数据库连接池
C3P0连接池使用
先要下载好相关jar包,或maven依赖
C3P0连接数据库方式1:
相关参数在程序中指定(user ,password,url,driver)
//方式1:相关参数在程序中指定(user ,password,url,driver)
public static void testC3p0_01() throws IOException, PropertyVetoException, SQLException {
//获取数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//读取配置信息
Properties properties = new Properties();
properties.load(new FileReader("src/mysql.properties"));
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
//设置配置信息
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//设置连接池初始连接数量
comboPooledDataSource.setInitialPoolSize(10);
//设置最大连接数量
comboPooledDataSource.setMaxPoolSize(50);
//获取连接
long start=System.currentTimeMillis();
for (int i=0;i<5000;i++){
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end-start) +" ms");//耗时: 702 ms
}
输出:
耗时: 702 ms
C3P0连接数据库方式2:
通过配置文件获取: 配置文件: c3p0-config.xml 文件名一定要是这个,要不然程序找不到,可以放在项目的src目录下,通过配置文件可以设置很多值,可以从官方文档中获取
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc</property>
<property name="user">root</property>
<property name="password"></property>
</default-config>
<!-- This app is massive! -->
<named-config name="ThinkNet">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true</property>
<property name="user">root</property>
<property name="password"></property>
<!-- 初始连接数-->
<property name="initialPoolSize">10</property>
<!-- 最小连接数-->
<property name="minPoolSize">5</property>
<!-- 最大连接数-->
<property name="maxPoolSize">50</property>
</named-config>
</c3p0-config>
测试程序:
//方式2,直接读取配置文件连接
public static void testC3p0_02() throws SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("ThinkNet");
long start = System.currentTimeMillis();
for (int i=0;i<500000;i++){
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("耗时: "+ (end-start) +" ms");//耗时: 1366 ms
}
输出:
耗时: 1366 ms
德鲁伊(Druid)连接池使用
先要下载好相关jar包,或maven依赖
Druid连接数据库
首先要有配置文件*.properties,文件名可以自定义,路径自定义,加载到程序中就可以
driverClassName = com.mysql.jdbc.Driver
#url,?后面是批处理和解决idea警告的语句,可以不写
url =jdbc:mysql://localhost:3306/jdbc?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&useSSL=false
#用户名
username = root
#数据库的密码
password =
initialSize = 10
#闲置最小连接数
minIdle = 5
#最大连接数
maxActive = 50
#最大等待时间
maxWait = 5000
#timeBetweenEvictionRunsMillis = 60
测试代码:
public static void druidTest() throws Exception {
//加载配置文件
Properties properties = new Properties();
properties.load(new FileReader("src\\druid.properties"));
//创建一个指定参数的数据库连接池(druid连接池)
DataSource dataSource= DruidDataSourceFactory.createDataSource(properties);
//获得连接
long start = System.currentTimeMillis();
for (int i=0;i<500000;i++){
Connection connection = dataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("耗时: "+ (end-start) +" ms");//耗时: 548 ms
}
输出:
耗时: 548 ms
从上面的测试果可以看出:用配置文件连接的C3P0连接池,和Druid连接池在500000次连接数据库中,Druid性能要优于C3P0
将Druid连接池封装成工具类
工具类:
public class JdbcUtilsByDruid {
static DataSource ds;
static {
Properties properties = new Properties();
try {
properties.load(new FileReader("src\\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
//转成运行时异常
throw new RuntimeException(e);
}
}
//获取连接
public static Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException e) {
//转成运行时异常
throw new RuntimeException(e);
}
}
//关闭连接(不是真正意义上的关闭)
public static void close(Connection connection, Statement statement, ResultSet resultSet){
try {
if(connection!=null){
connection.close();
}
if(statement!=null){
statement.close();
}
if (resultSet!=null){
resultSet.close();
}
} catch (SQLException e) {
//转成运行时异常
throw new RuntimeException(e);
}
}
}
测试案例
public static void main(String[] args) {
String sql="select * from actor";
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
connection=JdbcUtilsByDruid.getConnection();
//看看它的运行类型是什么
System.out.println(connection.getClass()); //class com.alibaba.druid.pool.DruidPooledConnection
preparedStatement=connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String sex = resultSet.getString("sex");
Date borndate= resultSet.getDate("borndate");
String phone = resultSet.getString("phone");
System.out.println("\t"+id+"\t"+name+"\t"+sex+"\t"+borndate+"\t"+phone+"\t");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtilsByDruid.close(connection,preparedStatement,resultSet);
}
}
输出:
class com.alibaba.druid.pool.DruidPooledConnection
2 jack 男 2002-02-25 119
3 king 男 2021-10-10 120
4 tom 男 2020-02-10 110
commons-dbutils
ResultSet弊端
- 当Connection连接关闭后,ResultSet将不可用,这样就不能对ResultSet里的数据达到复用;
- ResultSet 不利于对数据的管理
- 返回数据也不方便
Apache的commons-dbutils基本介绍:
- commons-dbutils是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化jdbc编码的工作量
dbutils中的类
- QueryRunner类: 该类封装了sql的执行,是线程安全的.可以实现增,删,改,查,批处理
- ResultSetHandler接口:该接口用于处理java.sal,ResultSet,将数据转换为另一种形式
- 常用类
类 | 说明 |
---|---|
ArrayHandler | 把结果集中的第一行数据转成对象数组 |
ArrayListHandler | 把结果集中的没一行数据都转成一个数组,放到List中 |
BeanHandler | 将结果集中的第一行数据封装到一个对应的javaBean实例中 |
BeanListHandler | 将结果集中的每一行数据都封装到一个对应的javaBean实例中,存放在List里 |
ColumnListHandler | 将结果集中某一列的数据存放到List中 |
KeyedHandler(name) | 将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的key |
MapHandler | 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值 |
MapListHandler | 将结果集中的每一行数据都封装到一个Map里,然后再存放到List |
使用案例
返回表中所有行
//返回表中所有行,返回List
public static void query() throws Exception {
String sql="select * from actor where id>?";
//获得配置文件
Properties properties = new Properties();
properties.load(new FileReader("src//druid.properties"));
//获取数据库连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//得到连接
Connection connection = dataSource.getConnection();
//参数1:将得到的连接扔进去;
//参数2:sql语句扔进去;
//参数3:创建一个BeanListHeader对象,将根据表抽象出来的对象的类对象丢进去,底层利用反射机制获取表的各个字段信息
//参数4:sql语句中的?的值,参数4是可变参数,还可以往下追加
/**
* 调用new QueryRunner().query(connection, sql, new BeanListHandler<>(Actor.class), 2)方法后,该方法调用以下方法,closeConn参数为假
* query()方法源码解读:
* private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
* if (conn == null) { //一系列判空处理
* throw new SQLException("Null connection");
* } else if (sql == null) {
* if (closeConn) {
* this.close(conn);
* }
*
* throw new SQLException("Null SQL statement");
* } else if (rsh == null) {
* if (closeConn) {
* this.close(conn);
* }
*
* throw new SQLException("Null ResultSetHandler");
* } else {
* PreparedStatement stmt = null; //给出PreparedStatement对象
* ResultSet rs = null; //给出ResultSet对象
* Object result = null; //最终这个变量将返回一个ArrayList对象
*
* try {
* stmt = this.prepareStatement(conn, sql);//将sql语句放入PreparedStatement对象中
* this.fillStatement(stmt, params); //将sql语句中的问号填满
* rs = this.wrap(stmt.executeQuery()); //得到结果集处理后给rs
* result = rsh.handle(rs); //底层利用反射机制获取Actor信息,并返回装有Actor对象的ArrayList集合
* } catch (SQLException var33) {
* this.rethrow(var33, sql, params);
* } finally {
* try {
* this.close(rs); 关闭ResultSet对象rs
* } finally {
* this.close(stmt); //关闭PreparedStatement对象stmt
* if (closeConn) { //参数为假,不走下面的语句,所以在要外面关闭
* this.close(conn);
* }
*
* }
* }
*
* return result; //返回ArrayList集合
* }
* }
*/
List<Actor> list = new QueryRunner().query(connection, sql, new BeanListHandler<>(Actor.class), 0);
for (Actor actor : list) {
System.out.println(actor);
}
//关闭连接(ResultSet和PreparedStatement底层已经关闭)
connection.close();
}
输出:
2 jack 男 2002-02-25 00:00:00.0 119
3 king 男 2021-10-10 00:00:00.0 120
4 tom 男 2020-02-10 00:00:00.0 110
返回表中的单行
//获取一行数据,返回Actor对象
public static void beanHandlerTest() throws Exception {
String sql="select * from actor where id=?";
//获得配置文件
Properties properties = new Properties();
properties.load(new FileReader("src//druid.properties"));
//获取数据库连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//得到连接
Connection connection = dataSource.getConnection();
Actor query = new QueryRunner().query(connection, sql, new BeanHandler<>(Actor.class), 2);
System.out.println(query);
//关闭连接
JdbcUtilsByDruid.close(connection,null,null);
}
输出:
2 jack 男 2002-02-25 00:00:00.0 119
获取表中的单行单列数据
//获取单行单列数据,返回Object对象
public static void ScalarHandlerTest() throws Exception {
String sql="select `name` from actor where id=?";
//获得配置文件
Properties properties = new Properties();
properties.load(new FileReader("src//druid.properties"));
//获取数据库连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//得到连接
Connection connection = dataSource.getConnection();
Object o = new QueryRunner().query(connection, sql, new ScalarHandler<>(), 2);
System.out.println(o);
JdbcUtilsByDruid.close(connection,null,null);
}
输出:
jack
对表进行dml操作
public static void updateTest() throws Exception {
//增
//String sql="insert into actor values(?,?,?,?,?)";
//删
// String sql="delete from actor where id=? ";
//改
String sql="update actor set name =? where id=? ";
//获得配置文件
Properties properties = new Properties();
properties.load(new FileReader("src//druid.properties"));
//获取数据库连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//得到连接
Connection connection = dataSource.getConnection();
//执行对表的增删改操作(dml语句)
int rows = new QueryRunner().update(connection, sql,"king" ,3 );
System.out.println(rows>0?"执行成功":"没有受影响的行数");
JdbcUtilsByDruid.close(connection,null,null);
}
输出:表已改变,不给出了.