此博客仅为本人学习JDBC时所做记录。
10-数据库连接池
10.1 传统连接的问题
这种模式开发,存在的问题:
- 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。**数据库的连接资源并没有得到很好的重复利用。**若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
- **对于每一次数据库连接,使用完后都得断开。**否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。(回忆:何为Java的内存泄漏?)
- 这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。
10.2 如何解决传统开发中的数据库连接问题
使用数据库连接池。
10.3 使用数据库连接池的好处
1. 资源重用
由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。
2. 更快的系统反应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间
3. 新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源
4. 统一的连接管理,避免数据库连接泄漏
在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露
简单总结:
- 提高程序的响应速度(减少创建连接相应的时间)
- 降低资源的消耗(可以重复使用)
- 便于连接的管理
10.4 实现的方式
- DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。
- C3P0 是一个开源组织提供的一个数据库连接池,**速度相对较慢,稳定性还可以。**hibernate官方推荐使用
- Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
- BoneCP 是一个开源组织提供的数据库连接池,速度快
- Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是速度不确定是否有BoneCP快
10.5 C3P0
-
导入jar包
-
测试连接的代码
/** * 使用C3P0的数据库连接池技术 */ //连接池只需要提供一个 private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0"); public static Connection getConnection1() throws SQLException { Connection conn = cpds.getConnection(); return conn; }
-
配置文件
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <named-config name="helloc3p0"> <!--提供获取连接的4个基本信息--> <property name="driverClass">com.mysql.cj.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true</property> <property name="user">root</property> <property name="password">*****</property> <!--提供数据库连接池管理的4个基本信息--> <!-- 当数据库连接池中的连接数不够时,c3p0一次性向数据库服务器申请的连接数 --> <property name="acquireIncrement">5</property> <!-- c3p0数据库连接池中初始化时的连接数 --> <property name="initialPoolSize">10</property> <!-- c3p0数据库连接池中维护的最少的连接数 --> <property name="minPoolSize">10</property> <!-- c3p0数据库连接池中维护的最多的连接数 --> <property name="maxPoolSize">100</property> <!-- c3p0数据库连接池中维护的最多的statement的个数 --> <property name="maxStatements">50</property> <!-- 每个连接中可以最多使用的Statement的个数 --> <property name="maxStatementsPerConnection">2</property> </named-config> </c3p0-config>
10.6 DBCP
-
导入jar包
-
测试连接的代码
/** * 使用DBCP数据库连接池技术获取数据库连接 * @return * @throws Exception */ //创建一个DBCP数据库连接池 private static DataSource source; static { try { Properties pros = new Properties(); // InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties"); FileInputStream is = new FileInputStream(new File("src/dbcp.properties")); pros.load(is); //创建DBCP数据库连接池 source = BasicDataSourceFactory.createDataSource(pros); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection2() throws Exception { Connection conn = source.getConnection(); System.out.println(conn); return conn; }
-
配置文件
driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true username=root password=**** initialSize=10
10.7 Druid(最常用)
-
导入jar包
-
测试连接的代码
/** * 使用Druid数据库连接池技术 */ private static DataSource source1; static { Properties pros = new Properties(); InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties"); try { pros.load(is); //获取Druid数据库连接池 source1 = DruidDataSourceFactory.createDataSource(pros); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection3() throws Exception { Connection conn = source1.getConnection(); return conn; }
-
配置文件
#等号最好不要有空格,否则会产生歧义,比如说密码是否包括空格呢? url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true username=root password=**** driverClassName=com.mysql.cj.jdbc.Driver initialSize=10 maxActive=10
11-DBUtils提供的jar包实现CRUD操作
11.1 导入jar包
11.2 使用现有的jar中的QueryRunner测试增、删、改的操作
//测试插入
@Test
public void testInsert() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "insert into customers(name,email,birth) values(?,?,?)";
int insertCount = runner.update(conn, sql, "蔡徐坤", "caixukun@126.com", "1999-09-09");
System.out.println("添加了"+insertCount+"条记录");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
11.3 使用现有的jar中的QueryRunner测试查询的操作
//测试查询
/**
* BeanHandler:是ResultSetHandler接口的实现类,用于封装表中的一条记录
* @throws Exception
*/
@Test
public void testQuery1() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ? ";
BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
Customer customer = runner.query(conn, sql, handler, 25);
System.out.println(customer);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
/**
* BeanListHandler:是ResultSetHandler接口的实现类,用于封装表中的多条记录构成的集合
* @throws Exception
*/
@Test
public void testQuery2() throws Exception {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id < ? ";
BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);
List<Customer> customerList = runner.query(conn, sql, handler, 25);
customerList.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
/**
* MapHandler:是ResultSetHandler接口的实现类,对应表中的一条记录,将字段及相应字段的值作为map中的键和值
* @throws Exception
*/
@Test
public void testQuery3() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ? ";
MapHandler handler = new MapHandler();
Map<String,Object> map = runner.query(conn, sql, handler, 25);
System.out.println(map);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
/**
* MapListHandler:是ResultSetHandler接口的实现类,对应表中的多条记录,将字段及相应字段的值作为map中的键和值,将这些map添加到List中
*/
@Test
public void testQuery4() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ? ";
MapListHandler handler = new MapListHandler();
List<Map<String, Object>> mapList = runner.query(conn, sql, handler, 25);
mapList.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
/**
* ScalarHandler:是ResultSetHandler接口的实现类,用于查询特殊值
*/
@Test
public void testQuery5() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select count(*) from customers";
ScalarHandler handler = new ScalarHandler();
Long count = (Long) runner.query(conn, sql, handler);
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
@Test
public void testQuery6() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select max(birth) from customers";
ScalarHandler handler = new ScalarHandler();
Date max = (Date) runner.query(conn, sql, handler);
System.out.println(max);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
/**
* 自定义ResultSetHandler实现类
*/
@Test
public void testQuery7() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>() {
@Override
public Customer handle(ResultSet resultSet) throws SQLException {
if(resultSet.next()){
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String email = resultSet.getString(3);
Date birth = resultSet.getDate(4);
Customer customer = new Customer(id,name,email,birth);
return customer;
}
return null;
}
};
Customer customer = runner.query(conn, sql, handler, 12);
System.out.println(customer);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
11.4 使用dbutils.jar包中的dbutils工具类实现连接等资源的关闭
public static void closeResource1(Connection conn, Statement ps, ResultSet rs){
// try {
// DbUtils.close(conn);
// } catch (SQLException e) {
// e.printStackTrace();
// }
// try {
// DbUtils.close(ps);
// } catch (SQLException e) {
// e.printStackTrace();
// }
// try {
// DbUtils.close(rs);
// } catch (SQLException e) {
// e.printStackTrace();
// }
DbUtils.closeQuietly(conn,ps,rs);
}