JDBC和数据库连接池
今日语录:一日很长,十年很短
一、JDBC概述
-
简介
image-20220509215845074 -
JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题 -
Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作 -
JDBC的基本原理 -
模拟JDBC -
JDBC带来的好处
-
如果Java直接访问数据库
image-20220509215942103 -
JDBC带来的好处
image-20220509220022717 -
说明:JDBC是Java提供一套用于数据库操作的接口API,Java程序员只需要面向这套接口编程即可。不同的数据库厂商,提供不同实现。
-
JDBC API
JDBC API 是一系列的接口,它统一和规范了应用程序与数据库的连接、执行mysql语句,并得到返回结果等各类操作,相关类和接口在java.sql与javax.sql包中。
image-20220509220324167 -
JDBC程序编写步骤
-
注册驱动 – 加载Driver类 -
获取链接 – 得到Connection -
执行增删改查 – 发送SQL 给mysql执行 -
释放资源 – 关闭相关连接 -
JDBC第一个程序
通过JDBC对表actor进行添加,删除和修改操作
package JDBC_;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @author chenx
* @date 2022年05月10日
*/
public class JDBC01_ {
public static void main(String[] args) throws SQLException {
// 前置工作,在项目下创建一个文件夹比如:lib
// 将驱动程序jar包复制到这个文件夹中,选中jar右击,add as Library
// 1、注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver();
// 2、得到连接
// (1)127.0.0.1:本地ip
// (2)3306:表示mysql坚挺的端口
// (3)mysql_learn:指定连接的数据库
// (4)mysql的连接本质是socket连接
String url = "jdbc:mysql://127.0.0.1:3306/mysql_learn?characterEncoding=utf-8&serverTimezone=UTC";
Properties properties = new Properties();
properties.setProperty("user","root");
properties.setProperty("password","123456");
Connection connect = driver.connect(url, properties);
// 3、执行sql
String sql = "INSERT INTO actor values(2,'MM','女','2022-05-10 14:50:59','15371009565')";
// statement 用于执行静态SQL语句并返回其生成的结果的对象
Statement statement = connect.createStatement();
// 如果是dml语句,返回的就是影响行数
int rows = statement.executeUpdate(sql);
System.out.println("新增了:"+rows+" 条数据");
}
}image-20220510150034951
二、获取数据库连接5种方式
-
方式一
// 1、注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver();
// 2、得到连接
// (1)127.0.0.1:本地ip
// (2)3306:表示mysql坚挺的端口
// (3)mysql_learn:指定连接的数据库
// (4)mysql的连接本质是socket连接
String url = "jdbc:mysql://127.0.0.1:3306/mysql_learn?characterEncoding=utf-8&serverTimezone=UTC";
Properties properties = new Properties();
properties.setProperty("user","root");
properties.setProperty("password","123456");
Connection connect = driver.connect(url, properties); -
方式二
Class<?> clazz = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)clazz.newInstance();
String url = "jdbc:mysql://127.0.0.1:3306/mysql_learn?characterEncoding=utf-8&serverTimezone=UTC";
Properties properties = new Properties();
properties.setProperty("user","root");
properties.setProperty("password","123456");
Connection connect = driver.connect(url, properties); -
方式三
Class<?> clazz = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)clazz.newInstance();
String url = "jdbc:mysql://127.0.0.1:3306/mysql_learn?characterEncoding=utf-8&serverTimezone=UTC";
String user = "root";
String password = "123456";
// 注册Driver驱动
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(url, user, password); -
方式四
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://127.0.0.1:3306/mysql_learn?characterEncoding=utf-8&serverTimezone=UTC";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password); -
方式五:配置文件更灵活
package JDBC_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @author chenx
* @date 2022年05月10日
*/
public class JDBC05_ {
public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {
Properties properties = new Properties();
properties.load(new FileInputStream("src\application.properties"));
// 获取相关值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
// 3、执行sql
String sql = "INSERT INTO actor values(2,'MM','女','2022-05-10 14:50:59','15371009565')";
sql = "UPDATE actor set sex='5' where id = 1";
// statement 用于执行静态SQL语句并返回其生成的结果的对象
Statement statement = connection.createStatement();
// 如果是dml语句,返回的就是影响行数
int rows = statement.executeUpdate(sql);
//System.out.println("新增了:"+rows+" 条数据");
System.out.println("更新了:"+rows+" 条数据");
// 4、关闭资源
statement.close();
connection.close();
}
}
三、Statement
-
简介
SQL注入示例
image-20220510214145482 -- SQL注入成功
-- 用 1' or 替换正确的id值
-- 用 or '1' = '1 替换正确的name (or '1' = '1在sql注入中称为万能钥匙)
select * from actor where id = '1' or' and name = 'or '1' = '1';image-20220510215022609 -
Statement -
PreparedStatement -
CallableStatement -
Statement对象用于执行静态SQL语句并返回其生成的结果的对象 -
在连接建立后,需要对数据库进行访问,执行命名或是SQL语句,可以通过 -
Statement对象执行SQL语句,存在SQL注入风险 -
SQL注入是利用某些系统没有设置对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句或命令,恶意攻击数据库 -
要防范SQL注入,只要使用PreparedStatement(从Statement扩展而来)取代Statement就可以了
四、PreparedStatement
-
简介
-
PreparedStatement执行的SQL语句中的参数用问号(?)来表示,调用PreparedStatement对象的setXxx()方法来设置这些参数.setXxx()方法有两个参数,第一个参数是要设置的SQL语句中的参数的索引(从1开始),第二个是设置的SQL语句中的参数的值 -
调用executeQuery(),返回ResultSet对象 -
调用executeUpdate();执行更新,包括增、删、修改 -
使用实例
package JDBC_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @author chenx
* @date 2022年05月10日
*/
public class PreparedStatement_ {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Properties properties = new Properties();
properties.load(new FileInputStream("src\application.properties"));
// 获取相关值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
// 3、执行sql
String sql = "INSERT INTO actor values(2,'MM','女','2022-05-10 14:50:59','15371009565')";
sql = "UPDATE actor set sex='6' where id = 1";
sql = "SELECT * FROM actor";
sql = "select * from actor where id = ? and name = ? ";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1,"1' or");
statement.setString(2,"or '1' = '1");
// 如果是dml语句,返回的就是影响行数
//int rows = statement.executeUpdate(sql);
ResultSet r = statement.executeQuery();
//System.out.println("新增了:"+rows+" 条数据");
System.out.println("更新了:"+r.next()+" 条数据");
}
}image-20220510220733278
预处理DML
-
新增记录
package JDBC_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @author chenx
* @date 2022年05月10日
*/
public class PreparedStatement_ {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Properties properties = new Properties();
properties.load(new FileInputStream("src\application.properties"));
// 获取相关值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
// 3、执行sql
String sql = "";
// 添加记录
sql = "INSERT INTO actor values(?,?,?,?,?)";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1,"3");
statement.setString(2,"CC");
statement.setString(3,"女");
statement.setString(4,"2022-05-10 21:50:59");
statement.setString(5,"13048678192");
// 如果是dml语句,返回的就是影响行数
int r = statement.executeUpdate();
System.out.println("新增了:"+r+" 条数据");
}
}image-20220510221544475 -
修改记录
package JDBC_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @author chenx
* @date 2022年05月10日
*/
public class PreparedStatement_ {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Properties properties = new Properties();
properties.load(new FileInputStream("src\application.properties"));
// 获取相关值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
// 3、执行sql
String sql = "";
// 修改文件
sql = "UPDATE actor set sex = ? where id = ?";
// 删除记录
//sql = "DELETE FROM actor where id = 1";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1,"0");
statement.setString(2,"1");
// 如果是dml语句,返回的就是影响行数
int r = statement.executeUpdate();
System.out.println("修改了:"+r+" 条数据");
}
}image-20220510221734062 -
删除记录
package JDBC_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @author chenx
* @date 2022年05月10日
*/
public class PreparedStatement_ {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Properties properties = new Properties();
properties.load(new FileInputStream("src\application.properties"));
// 获取相关值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
// 3、执行sql
String sql = "";
// 删除记录
sql = "DELETE FROM actor where id = ?";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1,"3");
// 如果是dml语句,返回的就是影响行数
int r = statement.executeUpdate();
System.out.println("删除了:"+r+" 条数据");
}
}image-20220510221909966
五、ResultSet(结果集)
-
简介 -
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成 -
ResultSet对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前 -
next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false,因此可以在while循环中使用循环来遍历结果集。
六、JDBC API


七、封装JDBCUtils
package JDBC_;
import IO.Properties_;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @author chenx
* @date 2022年05月10日
* 这是一个工具类,完成mysql的连接和关闭资源
*/
public class JDBCUtils {
//定义相关的属性(4个),因为只需要一份,因此,我们做出static
private static String user; // 用户名
private static String password; // 密码
private static String url; // 链接
private static String driver; // 驱动类
// 在静态代码块中去初始化
static{
try {
Properties properties = new Properties();
properties.load(new FileInputStream("src\application.properties"));
// 获取相关值
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
driver = properties.getProperty("driver");
} catch (IOException e) {
// 在实际开发中,这样处理
// 1、将编译异常转为运行异常
// 2、这里是调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便
throw new RuntimeException(e);
}
}
// 链接数据库,返回Connection
public static Connection getConnection(){
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
// 1、将编译异常转为运行异常
// 2、这里是调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便
throw new RuntimeException(e);
}
}
/**
* 关闭相关资源
* @param resultSet 结果集
* @param statement 或者 PreparedStatement
* @param connection
*/
public static void close(ResultSet resultSet, Statement statement, Connection connection){
// 判断是否为null
try{
if(resultSet != null){
resultSet.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
}catch(SQLException e){
throw new RuntimeException(e);
}
}
}
-
修改
package JDBC_;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author chenx
* @date 2022年05月10日
*/
public class JDBCUtils_use {
public static void main(String[] args) {
Connection connection = null;
String sql = "UPDATE actor set sex = ? where id = ?";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = null;
try {
connection = JDBCUtils.getConnection();
statement = connection.prepareStatement(sql);
statement.setString(1,"8");
statement.setString(2,"1");
// 如果是dml语句,返回的就是影响行数
int resultSet = statement.executeUpdate();
System.out.println("更新:"+resultSet);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtils.close(null,statement, connection);
}
}
} -
查询
package JDBC_;
import java.sql.*;
/**
* @author chenx
* @date 2022年05月10日
*/
public class JDBCUtils_use {
public static void main(String[] args) {
Connection connection = null;
String sql = "SELECT * FROM actor";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection();
statement = connection.prepareStatement(sql);
// 如果是dml语句,返回的就是影响行数
resultSet = statement.executeQuery();
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println("查询结果:"+ id + " | " + name);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtils.close(resultSet,statement, connection);
}
}
}image-20220510230051687
八、事务
-
简介
-
JDBC程序中当一个Connection对象创建时,默认情况下是自动提交事务:每次执行一个sql语句时,如果执行成功,就会向数据库自动提交,而不能回滚。 -
JDBC程序中为了让多个SQL语句作为一个整体执行,需要使用事务 -
调用Connection的setAutoCommit(false)可以取消自动提交事务 -
在所有的SQL语句都成功执行后,调用Connection的commit();方法提交事务 -
在其中某个操作失败或出现异常时,调用Connection的rollback();方法回滚事务 -
应用实例
模拟转账业务
package JDBC_;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author chenx
* @date 2022年05月11日
*/
public class Transaction_ {
public static void main(String[] args) {
Connection connection = null;
// 马云 -> 马化腾 转账 1000 元
String sql = "UPDATE account_02 SET balance = balance - 1000 WHERE id = 1";
String sql2 = "UPDATE account_02 SET balance = balance + 1000 WHERE id = 2";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = null;
ResultSet resultSet = null;
int rows = 0;
try {
connection = JDBCUtils.getConnection();
statement = connection.prepareStatement(sql);
// 如果是dml语句,返回的就是影响行数
rows = statement.executeUpdate();
System.out.println("sql更新结果:"+ rows );
int i = 1 / 0;
statement = connection.prepareStatement(sql2);
// 如果是dml语句,返回的就是影响行数
rows = statement.executeUpdate();
System.out.println("sql2更新结果:"+ rows );
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtils.close(resultSet,statement, connection);
}
}
}image-20220511221932485 -
使用传统方式(无事务),发生异常导致数据不一致

2.使用事务
//用事务来解决
@Test
public void useTransaction() {
Connection connection = null;
// 马云 -> 马化腾 转账 1000 元
String sql = "UPDATE account_02 SET balance = balance - 1000 WHERE id = 1";
String sql2 = "UPDATE account_02 SET balance = balance + 1000 WHERE id = 2";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = null;
ResultSet resultSet = null;
int rows = 0;
try {
// 在默认情况下,connection是默认自动提交
connection = JDBCUtils.getConnection();
// 关闭自动提交
connection.setAutoCommit(false);
statement = connection.prepareStatement(sql);
// 如果是dml语句,返回的就是影响行数
rows = statement.executeUpdate();
System.out.println("sql更新结果:"+ rows );
int i = 1 / 0;
statement = connection.prepareStatement(sql2);
// 如果是dml语句,返回的就是影响行数
rows = statement.executeUpdate();
System.out.println("sql2更新结果:"+ rows );
// 提交事务
connection.commit();
} catch (SQLException e) {
// 这里我们可以设置自动回滚
System.out.println("sql执行异常,回滚!");
try {
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,statement, connection);
}
}


九、批处理
-
简介
-
当需要成批插入或者更新记录时,可以采用java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。
-
JDBC的批量处理语句包括下面的方法:
addBatch():添加需要批量处理的SQL语句或参数
executeBatch():执行批量处理语句
clearBatch():清空批处理包的语句 -
JDBC连接MySQL时,如果要使用批处理功能,请在url中加参数 :
?rewriteBatchedStatements=true
-
批处理往往和PreparedStatement一起搭配使用,既可以减少编译次数,又可以减少运行次数,效率大大提高
-
使用实例
-
传统方式
@Test
public void batchDemo(){
Connection connection = null;
// 马云 -> 马化腾 转账 1000 元
String sql = "INSERT INTO admin2 values(null,?,?)";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = null;
ResultSet resultSet = null;
int rows = 0;
try {
// 在默认情况下,connection是默认自动提交
connection = JDBCUtils.getConnection();
// 关闭自动提交
connection.setAutoCommit(false);
statement = connection.prepareStatement(sql);
System.out.println("开始执行插入操作");
long startTime = System.currentTimeMillis();
for(int i=0;i<10000;i++){
statement.setString(1,"chenx"+1);
statement.setString(2,"888");
statement.executeUpdate();
}
// 提交事务
connection.commit();
long endTime = System.currentTimeMillis();
System.out.println("总耗时"+(endTime-startTime));
} catch (SQLException e) {
// 这里我们可以设置自动回滚
System.out.println("sql执行异常,回滚!");
try {
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,statement, connection);
}
}image-20220511230452808 -
批处理方式
# 将url连接路径加上参数:?rewriteBatchedStatements=true
# 批处理方式
@Test
public void useBatch(){
Connection connection = null;
// 马云 -> 马化腾 转账 1000 元
String sql = "INSERT INTO admin2 values(null,?,?)";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = null;
ResultSet resultSet = null;
int rows = 0;
try {
// 在默认情况下,connection是默认自动提交
connection = JDBCUtils.getConnection();
// 关闭自动提交
connection.setAutoCommit(false);
statement = connection.prepareStatement(sql);
System.out.println("开始执行插入操作");
long startTime = System.currentTimeMillis();
for(int i=0;i<10000;i++){
statement.setString(1,"chenx"+1);
statement.setString(2,"888");
// 把sql语句加入到批处理包中
statement.addBatch();
// 当有1000条数据就批量执行
if((i+1)%1000 == 0){
statement.executeBatch();
// 清空一次
statement.clearBatch();
}
}
// 提交事务
connection.commit();
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
} catch (SQLException e) {
// 这里我们可以设置自动回滚
System.out.println("sql执行异常,回滚!");
try {
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(resultSet,statement, connection);
}
}image-20220511230657193
十、源码解读(JDK 1.8.0)

十一、数据库连接池
传统获取Connection问题分析
-
传统的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再验证IP地址,用户名和密码(0.05~1s时间)。需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃
-
每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄露,最终将导致重启数据库
-
传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MYSQL崩溃
-
解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool)
image-20220513191739569
数据库连接池基本介绍
-
预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去
-
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
-
当应用程序向连接池请求的连接数量超过最大连接数量时,这些请求将被加入到等待队列中
image-20220513191537524
数据库连接池种类
-
JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现 -
C3P0数据库连接池,速度相对较慢,稳定性不错(hibernate,spring) -
DBCP数据库连接池,速度相对C3P0较快,但不稳定 -
Proxool数据库连接池,有监控连接池状态的功能,稳定性较C3P0差一点 -
BoneCP数据库连接池,速度快 -
Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3P0、Proxool优点于一身的数据库连接池
C3P0
2.使用示例
-
方式一
@Test
public void testC3p001() throws IOException, ClassNotFoundException, SQLException, PropertyVetoException {
//1、创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2、通过配置文件读取连接信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\application.properties"));
// 获取相关值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
// 3、给数据源comboPooledDataSource设置相关参数
// 注意:连接管理是由comboPooledDataSource来管理
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
// 4、设置初始化连接数
comboPooledDataSource.setInitialPoolSize(10);
// 5、设置最大连接数
comboPooledDataSource.setMaxPoolSize(50);
// 测试频繁连接连接池的效率,测试对mysl50000次操作
long start = System.currentTimeMillis();
for(int i=0;i < 50000; i++){
Connection connection = comboPooledDataSource.getConnection();
//System.out.println("数据库连接成功!");
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("C3P0 50000次连接mysql 耗时="+(end-start)/1000+" 秒");
}

-
方式二
// 使用配置文件模板来完成
// 1、将c3p0提供的c3p0.config.xml拷贝到src目录下
// 2、该文件制定了连接数据库和相关参数
@Test
public void testC3P0002() throws SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("phoenix");
// 测试频繁连接连接池的效率,测试对mysl50000次操作
long start = System.currentTimeMillis();
for(int i=0;i < 50000; i++){
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("C3P0 50000次连接mysql 耗时="+(end-start)+" 毫秒");
}<c3p0-config>
<!-- 数据源名称代表连接池-->
<named-config name="phoenix">
<!-- 驱动类 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<!-- url-->
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/mysql_learn?serverTimezone=UTC</property>
<!-- 用户名 -->
<property name="user">root</property>
<!-- 密码 -->
<property name="password">123456</property>
<!-- 每次增长的连接数-->
<property name="acquireIncrement">5</property>
<!-- 初始的连接数 -->
<property name="initialPoolSize">10</property>
<!-- 最小连接数 -->
<property name="minPoolSize">5</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">10</property>
<!-- 可连接的最多的命令对象数 -->
<property name="maxStatements">5</property>
<!-- 每个连接对象可连接的最多的命令对象数 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
Druid(德鲁伊)
-
使用实例
@Test
public void druidTest001() throws Exception {
Properties properties = new Properties();
properties.load(new FileInputStream("src\druid.properties"));
// 创建一个指定参数的数据库连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
long start = System.currentTimeMillis();
for(int i=0;i < 500000000; i++){
Connection connection = dataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
// 1254ms
System.out.println("Druid 5000000次连接mysql 耗时="+(end-start)+" 毫秒");
} -
DruidUtils
package Druid_;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @author chenx
* @date 2022年05月15日
* 基于druid数据库连接池的工具类
*/
public class DruidUtils_ {
private static DataSource ds;
// 在静态代码块完成ds初始化
static{
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 编写getConnection方法
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
// 关闭连接
public static void close(ResultSet resultSet, Statement statement, Connection connection){
try {
if(resultSet!=null){
resultSet.close();
}
if(statement!=null){
statement.close();
}
if(connection!=null){
connection.close();
}
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}
}
}工具类使用实例
package Druid_;
import JDBC_.JDBCUtils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.jupiter.api.Test;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
* @author chenx
* @date 2022年05月15日
*/
public class DruidUtilsDemo_ {
@Test
public void druidTest001() throws Exception {
String sql = "SELECT * FROM actor";
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
PreparedStatement statement = null;
ResultSet resultSet = null;
Connection connection = null;
try {
connection = DruidUtils_.getConnection();
statement = connection.prepareStatement(sql);
// 如果是dml语句,返回的就是影响行数
resultSet = statement.executeQuery();
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println("查询结果:"+ id + " | " + name);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtils_.close(resultSet,statement, connection);
}
}
}image-20220515223143509
Apache-DBUtils
背景
传统方式,connection关闭之后,无法操作resultSet
image-20220515223912450

简介
-
common-dbutils是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化jdbc编码的工作量
DBUtils类
-
QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增删改查、批处理
-
使用QueryRunner类实现查询
-
ResultSetHandler接口:该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式
image-20220515225450472
DBUtils使用实例(apache-DBUtils工具类 + Druid)
package Druid_;
import java.util.Date;
/**
* @author chenx
* @date 2022年05月15日
* Actor 对象和 actor表的记录对应
*/
public class Actor { //Javabean, POJO, Domain对象
private Integer id;
private String name;
private String sex;
private Date borndate;
private String phone;
public Actor() { //一定要给一个无参构造器[反射需要]
}
public Actor(Integer id, String name, String sex, Date borndate, String phone) {
this.id = id;
this.name = name;
this.sex = sex;
this.borndate = borndate;
this.phone = phone;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBorndate() {
return borndate;
}
public void setBorndate(Date borndate) {
this.borndate = borndate;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "nActor{" +
"id=" + id +
", name='" + name + ''' +
", sex='" + sex + ''' +
", borndate=" + borndate +
", phone='" + phone + ''' +
'}';
}
}
查询
-
查询多行数据返回
// 使用apache-DBUtils工具类 + Druid 完成对表的crud操作
@Test
public void druidTest001() throws Exception {
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
Connection connection = null;
try {
// 得到connection
connection = DruidUtils_.getConnection();
// 使用DBUtils类和接口,先引入DBUtils相关的jar,加入本项目的依赖
// 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 执行相关的方法,返回ArrayList结果集
String sql = "SELECT * FROM actor where id >= ?";
// (1)query方法就是执行sql语句,得到resultSet --->封装到 ---> ArrayList集合中
// (2)返回集合
// (3)connection:连接
// (4)sql:执行的sql语句
// (5)new BeanListHandler<>(Actor.class):在将resultset -> Actor对象 -> 封装到ArrayList
// 底层使用反射机制 去获取Actor类的属性,然后封装
// (6)1:就是给sql语句中的 ? 赋值,可以有多个值,因为是可变参数(Object... params)
List<Actor> list =
queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class),1);
for(Actor actor : list){
System.out.println(actor);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtils_.close(null,null, connection);
}
}

-
查询单行数据返回
// 演示 apache-dbutils + druid 完成 返回的结果是单行记录(单个对象)
@Test
public void testQuerySingle(){
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
Connection connection = null;
try {
// 得到connection
connection = DruidUtils_.getConnection();
// 使用DBUtils类和接口,先引入DBUtils相关的jar,加入本项目的依赖
// 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 执行相关的方法,返回ArrayList结果集
String sql = "SELECT * FROM actor where id = ?";
// 因为我们返回的单行记录 --> 单个对象,使用的Handler是BeanHandler
Actor query = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 1);
System.out.println(query);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtils_.close(null,null, connection);
}
}image-20220516174600345 -
查询单行单列返回结果
// 演示 apache-dbutils + druid 完成 返回的结果是单行单列记录(单个对象)
@Test
public void testQuerySingleColunm(){
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
Connection connection = null;
try {
// 得到connection
connection = DruidUtils_.getConnection();
// 使用DBUtils类和接口,先引入DBUtils相关的jar,加入本项目的依赖
// 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 执行相关的方法,返回ArrayList结果集
String sql = "SELECT name FROM actor where id = ?";
// 因为我们返回的单行记录 --> 单个对象,使用的Handler是BeanHandler
Object query = queryRunner.query(connection, sql, new ScalarHandler(), 1);
System.out.println(query);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtils_.close(null,null, connection);
}
}image-20220516175024960
新增
@Test
public void insertByDBUtils(){
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
Connection connection = null;
try {
// 得到connection
connection = DruidUtils_.getConnection();
// 使用DBUtils类和接口,先引入DBUtils相关的jar,加入本项目的依赖
// 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 执行相关的方法,返回ArrayList结果集
String sql = "insert into actor values (?,?,?,?,?)";
// (1)执行dml操作是queryRunner.update()
// (2返回的值是受影响的行数(affected:影响的行数)
int query = queryRunner.update(connection,sql,55,"PP","女","2022-05-10 14:50:59","15371009565");
System.out.println(query);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtils_.close(null,null, connection);
}
}

修改
@Test
public void updateByDBUtils(){
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
Connection connection = null;
try {
// 得到connection
connection = DruidUtils_.getConnection();
// 使用DBUtils类和接口,先引入DBUtils相关的jar,加入本项目的依赖
// 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 执行相关的方法,返回ArrayList结果集
String sql = "update actor set id=? where id=?";
// (1)执行dml操作是queryRunner.update()
// (2返回的值是受影响的行数(affected:影响的行数)
int query = queryRunner.update(connection,sql,99,1);
System.out.println(query);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtils_.close(null,null, connection);
}
}

删除
@Test
public void deleteByDBUtils(){
// PreparedStatement 用于执行静态SQL语句并返回其生成的结果的对象(可防止SQL注入)
Connection connection = null;
try {
// 得到connection
connection = DruidUtils_.getConnection();
// 使用DBUtils类和接口,先引入DBUtils相关的jar,加入本项目的依赖
// 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 执行相关的方法,返回ArrayList结果集
String sql = "delete from actor where id = ?";
// (1)执行dml操作是queryRunner.update()
// (2返回的值是受影响的行数(affected:影响的行数)
int query = queryRunner.update(connection,sql, 100);
System.out.println(query);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DruidUtils_.close(null,null, connection);
}
}

BasicDAO
-
引出使用BasicDAO的背景
image-20220516181815136 -
简介
-
DAO:data access object数据访问对象 -
这样的通用类,称为BasicDao,是专门和数据库交互的,即完成对数据库(表)的CRUD操作。 -
在BasicDao的基础上,实现一张表对应一个Dao,更好的完成功能,比如Customer表->Customer类(javabean)->CustomerDao.java -
使用实例
package dao_.dao;
import Druid_.Actor;
import Druid_.DruidUtils_;
import com.mysql.cj.QueryResult;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @author chenx
* @date 2022年05月16日
*/
public class BasicDAO<T> { // 泛型指定具体类型
QueryRunner queryRunner = new QueryRunner();
// 开发通用dml方法,针对任意表
public int update(String sql, Object... parameters){
Connection connection = null;
try {
connection = DruidUtils_.getConnection();
int update = queryRunner.update(connection, sql, parameters);
return update;
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}finally {
DruidUtils_.close(null,null,connection);
}
}
// 返回多个参数(即查询的结果是多行),针对任意表
/**
*
* @param sql sql语句,可以有 ?
* @param clazz 传入一个类的Class对象
* @param parameter 传入可变长参数
* @return
*/
public List<T> queryMulti(String sql, Class<T> clazz, Object... parameter){
Connection connection = null;
try {
connection = DruidUtils_.getConnection();
return queryRunner.query(connection, sql, new BeanListHandler<T>(clazz),parameter );
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}finally {
DruidUtils_.close(null,null,connection);
}
}
/**
* 查询单条结果
* @param sql
* @param clazz
* @param parameter
* @return
*/
public T querySingle(String sql, Class<T> clazz, Object... parameter){
Connection connection = null;
try {
connection = DruidUtils_.getConnection();
return queryRunner.query(connection, sql, new BeanHandler<>(clazz),parameter);
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}finally {
DruidUtils_.close(null,null,connection);
}
}
/**
* 查单列结果
* @param sql
* @param parameter
* @return
*/
public Object queryScalar(String sql, Object... parameter){
Connection connection = null;
try {
connection = DruidUtils_.getConnection();
return queryRunner.query(connection, sql, new ScalarHandler(),parameter);
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}finally {
DruidUtils_.close(null,null,connection);
}
}
}package dao_.dao;
import dao_.domain.Actor;
import org.junit.jupiter.api.Test;
import java.util.List;
/**
* @author chenx
* @date 2022年05月16日
*/
public class ActorDAO extends BasicDAO<Actor>{
/**
* 查多条
*/
@Test
public void testUpdate(){
ActorDAO actorDAO = new ActorDAO();
List<Actor> actors = actorDAO.queryMulti("select * from actor where id > ?", Actor.class, 1);
for(Actor actor : actors){
System.out.println("结果:"+actor);
}
}
/**
* 查单条
*/
@Test
public void test002(){
ActorDAO actorDAO = new ActorDAO();
Actor actors = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 99);
System.out.println("结果:"+actors);
}
/**
* 查单列
*/
@Test
public void test003(){
ActorDAO actorDAO = new ActorDAO();
Object obj = actorDAO.queryScalar("select name from actor where id = ?", 99);
System.out.println("结果:"+obj);
}
/**
* 更新
*/
@Test
public void test004(){
ActorDAO actorDAO = new ActorDAO();
int obj = actorDAO.update("update actor set name=? where id=?", "DHH",99);
System.out.println("结果:"+obj);
}
/**
* 新增
*/
@Test
public void test005(){
ActorDAO actorDAO = new ActorDAO();
int obj = actorDAO.update("insert into actor values(?,?,?,?,?)", 666,"HDH","男","2022-05-16 21:35:56","123321123321");
System.out.println("结果:"+obj);
}
/**
* 删除
*/
@Test
public void test006(){
ActorDAO actorDAO = new ActorDAO();
int obj = actorDAO.update("delete from actor where id = ?", 99);
System.out.println("结果:"+obj);
}
}
原文始发于微信公众号(Whisper Coding):Java入门-JDBC和数据库连接池
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/255636.html