JDBC 的连接池
1. 连接池
a. 概述
- 连接池其实就是一个容器(集合),存放数据库连接的容器。
- 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
- 原理可见:JDBC 连接池原理。
b. 优点
- 节约资源,减轻服务器压力;
- 提高连接复用性,用户访问高效。
c. 常见连接池
- DBCP:Apache 提供的数据库连接池技术。
- C3P0:数据库连接池技术,目前使用它的开源项目有 Hibernate、Spring 等。
- HikariCP:日本开发的连接池技术,性能之王,目前使用它的开源项目有 SpringBoot 等。
- Druid([ˈdruːɪd],也称德鲁伊):阿里巴巴提供的数据库连接池技术,是目前最好的数据库连接池。600+ 项目中使用,支持 SQL 日志监控。
d. 实现 DataSource
- Java 为数据库连接池提供了公共的接口 DataSource ,各个连接池厂商去实现这套接口,提供 Jar 包。
- DataSource 功能:
- 获取连接:
Connection getConnection()
- 归还连接:
connection.close()
- 如果连接对象是通过连接池获取的,那么执行
connection.close()
方法时,是归还到连接池,而不是销毁对象。 - 底层通过动态代理技术对
close()
方法进行了增强。
- 如果连接对象是通过连接池获取的,那么执行
- 获取连接:
2. Druid 连接池
a. 快速入门
i. 导入 Druid 相关 Jar 包
- 相关 Jar 包已上传 CSDN:druid-1.0.9 .jar
ii. 测试代码
package com.regino.util;
import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
public class DruidDemo {
public static void main(String[] args) throws Exception {
// 1.创建Druid连接池对象
DruidDataSource dataSource = new DruidDataSource();
// 2.与MySQL建立连接(初始化一些连接个数),设置数据库基本四项
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/regino");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 3.从连接池中获取连接
Connection connection = dataSource.getConnection();
// 4.处理业务
System.out.println(connection);
// 5.归还连接
connection.close();// 注意:此方法执行完毕,不是销毁对象,而是归还到连接池容器中
}
}
iii. API 介绍
- initialSize:初始化时建立物理链接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时。例如,容器创建完后,先初始化 10 个对象。
- maxActive:最大连接池数量。例如,白天设为 100 个。
- minIdle:最小连接池数量。例如,凌晨设为 20 个。
- maxWait:获取链接时最大等待时间,单位毫秒。即等待上一个用户归还链接。
b. 配置文件版本
i. 定义配置文件 druid.properties
# 数据库基本四项
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/regino
username=root
password=root
# 初始化个数
initialSize=5
# 最大连接数
maxActive=10
# 等待时间,单位毫秒
maxWait=3000
ii. 测试代码
package com.regino.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
public class DruidDemo {
public static void main(String[] args) throws Exception {
// 通过类加载器读取src目录下配置文件,获取io流
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
// 创建Properties对象
Properties properties = new Properties();
properties.load(is);
// Druid连接池工厂对象,初始化连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// 获取连接
// 测试最大连接数
for (int i = 1; i <= 11; i++) {
Connection connection = dataSource.getConnection();
System.out.println(connection);
if (i == 10) {
connection.close();
}
}
// 归还连接
// connection.close();
}
}
c. 连接池工具类 JdbcUtils
- 每次操作数据库都需要创建连接池,获取连接,关闭资源,都是重复的代码。可以将创建连接池和获取连接池的代码放到一个工具类中。
- 初始化连接池对象(Druid),一个项目只有一个
static{}
- 提供获取连接池对象静态方法
- 提供连接对象的静态方法
- 提供释放资源的静态方法(Connection 是归还)
package com.regino.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils {
private static DataSource dataSource = null;
// 1.初始化连接池对象(Druid),一个项目只有一个 static{}
static {
try {
// 通过类加载器读取src目录下配置文件,获取io流
InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
// 创建Properties对象
Properties properties = new Properties();
properties.load(is);
// Druid连接池工厂对象,初始化连接池
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 2.提供获取连接池对象静态方法
public static DataSource getDataSource() {
return dataSource;
}
// 3.提供连接对象的静态方法
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 4.提供释放资源的静态方法(Connection是归还)
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 重载关闭方法
public static void close(Statement statement, Connection connection) {
close(null, statement, connection);
}
}
3. 综合案例:用户登录
a. 主要需求
- 使用三层架构 + JDBC 连接池技术(面向接口编程),实现用户登录案例。
b. 需求分析
c. 环境搭建
- 具体可见:JDBC 简述与基础语法 >> 综合案例:用户登录。
- 导入 Jar 包:
d. 编写 User 实体类
- 一个 Java 实体(class 类) 对应一张表;
- 一个 Java 对象对应一条记录。
package com.regino.domain;
public class User {
private Integer id;
private String username;
private String password;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
e. 代码实现
i. UserDao 接口
package com.regino.dao;
import com.regino.domain.User;
public interface UserDao {
// 我来定义规范(根据用户名和密码查询User对象)
public User login(String username, String password);
}
ii. UserDaoImpl 实现类
package com.regino.dao;
import com.regino.domain.User;
import com.regino.util.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDaoImpl implements UserDao {
@Override
public User login(String username, String password) {
Connection connection =null;
PreparedStatement preparedStatement = null;
ResultSet resultSet =null;
try {
// 1.获取连接【从连接池】
connection = JdbcUtils.getConnection();
// 2.编写sql
String sql = "select * from user where username = ? and password = ?";
// 3.获取sql预编译执行对象
preparedStatement = connection.prepareStatement(sql);
// 4.设置实际参数
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
// 5.执行sql并返回结果
resultSet = preparedStatement.executeQuery();
// 6.处理结果
User user = null;
if (resultSet.next()) {
// 获取 id 用户名、密码
int id = resultSet.getInt("id");
username = resultSet.getString("username");
password = resultSet.getString("password");
user = new User();
user.setId(id);
user.setUsername(username);
user.setPassword(password);
}
return user;
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7.释放资源
JdbcUtils.close(resultSet, preparedStatement, connection);
}
return null;
}
}
iii. UserService 接口
package com.regino.service;
import com.regino.domain.User;
public interface UserService {
// 我来定义规范(根据用户名和密码查询User对象)
public User login(String username, String password);
}
iv. UserServiceImpl 实现类
package com.regino.service;
import com.regino.dao.UserDao;
import com.regino.dao.UserDaoImpl;
import com.regino.domain.User;
public class UserServiceImpl implements UserService {
@Override
public User login(String username, String password) {
UserDao userDao = new UserDaoImpl();
return userDao.login(username, password);
}
}
v. LoginServlet
package com.regino.web;
import com.regino.domain.User;
import com.regino.service.UserService;
import com.regino.service.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.接收请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
// 2.调用service
UserService userService = new UserServiceImpl();
User user = userService.login(username, password);
// 3.判断
if (user == null) {
// 登录失败,友情提示
request.setAttribute("error", "用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request, response);
} else {
// 登录成功
request.getSession().setAttribute("user", user);
response.sendRedirect(request.getContextPath() + "/list.jsp");
}
}
}