JDBC - 数据库连接池
目录
数据库连接池的概念
为什么需要连接池?
连接池的优势
连接池的工作流程
连接池参数
连接池的使用
数据库连接池的概念
数据连接池是一个用于管理和复用(核心价值)数据库连接的“容器”。
也就是说,当程序启动或第一期请求连接时,创建一定数量的数据库连接,将这些连接保存在一个“池子”中。当程序需要于数据库进行连接时,直接从“池子”中获取一个现成的、空闲的数据库连接。当时用完毕,将连接归还给“池子”,而不是直接关闭连接,以便后续的使用。
为什么需要连接池?
当没有连接池时,会有那些问题?
- 连接开销巨大:每次执行一次sql的查询,都需要完整的数据库连接(建立TCP连接,数据库权限验证,资源分配等)。这非常的耗时,可能比sql查询时间还长。
- 并发能力差:当大量的用户同时访问,数据库服务器需要给每个用户都进行创建连接。数据库能同时维护的数量有限,而大量请求会造成数据库崩溃。
- 资源耗尽:频繁的创建和关闭连接,会大量消耗客户端红和服务端CPU和内存资源。
连接池的优势
- 性能大幅提升:避免了频繁的创建和关闭连接而带来的巨大资源消耗,响应速度更快。
- 资源高效复用:复用现有的连接,大大减少了客户端和服务端的CPU、内存资源消耗。
- 资源控制:通过限制最大连接数,防止数据库被过多的连接而崩溃。
- 连接管理:内置的连接通过超时管理、健康检等机制,提高了应用的健壮性。
连接池的工作流程
- 初始连接数:程序启动时,连接池会预先创建一定数量的数据库的连接,放入“池子”中。
- 获取连接:当程序需要进行数据库连接时,连接池会从“池子”中分配一个现有的、空闲的连接给程序。
- 使用连接:程序使用这个连接执行数据库的操作。
- 归还连接:程序使用连接完毕,将其归还给连接池,连接池会将这个连接回收,标记为空闲状态,放回“池子”中,等待下一次的使用。连接本身并不会被物理关闭。
- 管理连接:连接池在后台持续工作,负责:
- 健康检测:定期检测池中的连接是否依然有效
- 动态扩容:当所有连接都被使用时,若新的连接请求,连接池可以创建新的连接(直到达到设置的最大连接数),以避免请求等待。
- 连接回收:若池中空闲连接过多,超过了最小空闲连接数,连接池会关闭一部分连接,已释放资源。
- 超时控制:可以设置连接的最大使用时间、获取连接的最大等待时间等,防止资源被无限期的占用。
连接池参数
池参数如果不知道,有默认值。
- 初始大小:10个
- 最小空闲连接数:3个
- 增量:一次创建的最小单位5个
- 最大空闲连接数:12个
- 最大连接数:20个
- 最大等待时间:1000毫秒
任何开源的连接池,都有四大参数需要自己设置:
- 驱动名称:com.mysql.jdbc.Driver
- 连接:jdbc:mysql:///数据库名
- 用户名:root
- 密码:root
连接池的使用
1. 导入开发的jar包druid-1.1.10.jar
2. 写测试程序
- 创建连接池对象,从连接池中获取连接对象
- 设置必要的4个参数:驱动类、地址、用户名、密码
- 设置初始化连接个数,默认是0
- 设置最大连接数和最大等待时间,单位是毫秒
- 定义连接对象
- 获取连接对象
- 编写sql语句
- 预编译sql语句
- 设置值
- 执行sql
- 释放资源
package cn.qcby.demo1;import com.alibaba.druid.pool.DruidDataSource;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;/*** 测试连接池对象*/
public class JdbcTest6 {public static void main(String[] args) {// 创建连接池对象,从连接池中获取到连接对象DruidDataSource dataSource = new DruidDataSource();// 设置4个参数 驱动类 地址 用户名 密码dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///jdbcdemo");dataSource.setUsername("root");dataSource.setPassword("root");// 设置初始化连接个数,默认是0dataSource.setInitialSize(5);// 设置最大连接数dataSource.setMaxActive(10);// 最大等待时间,单位是毫秒 2秒dataSource.setMaxWait(2000);// 定义连接对象Connection conn = null;PreparedStatement stmt = null;try {// 获取到连接对象conn = dataSource.getConnection();// 编写SQL语句String sql = "insert into t_user values (null,?,?,?)";// 预编译SQL语句stmt = conn.prepareStatement(sql);// 设置值stmt.setString(1,"eee");stmt.setString(2,"eee");stmt.setString(3,"eee");// 执行sqlstmt.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally {// 释放资源if(stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {// 把conn关闭了,其实连接池的底层已经对close方法进行增强。原来是销毁连接,现在是归还连接。conn.close();} catch (SQLException e) {e.printStackTrace();}}}}}
3. 配置文件druid.properties
后续数据库的连接等,都可以在配置文件中修改
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///jdbcdemo
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
maxIdle=6
minIdle=3
4. 写工具类
package cn.qcby.utils;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;/*** JDBC的工具类 1.0版本* JDBC的工具类 2.0版本(智能一些),编写properties属性文件,程序就可以读取属性文件* JDBC的工具类 3.0版本,加入连接池对象*/
public class JdbcUtils2 {// 连接池对象private static DataSource DATA_SOURCE;static{// 加载属性文件Properties pro = new Properties();InputStream inputStream = JdbcUtils2.class.getResourceAsStream("/druid.properties");try {// 加载属性文件pro.load(inputStream);// 创建连接池对象DATA_SOURCE = DruidDataSourceFactory.createDataSource(pro);} catch (Exception e) {e.printStackTrace();}}/*** 从连接池中获取连接,返回。* @return*/public static Connection getConnection(){Connection conn = null;try {conn = DATA_SOURCE.getConnection();} catch (SQLException e) {e.printStackTrace();}return conn;}/*** 关闭资源* @param conn* @param stmt* @param rs*/public static void close(Connection conn, Statement stmt, ResultSet rs){if(rs != null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if(stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 关闭资源* @param conn* @param stmt*/public static void close(Connection conn, Statement stmt){if(stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
