JDBC
1 JDBC 的基本概念
JDBC 概念: Java DataBase Connectivity ,java 数据库连接,java 语言操作数据库
本质:
程序员(Coder),写 java 代码,去操作数据库,但是市场上面的关系型数据库很多,比如说(MySQL,Oracle,DB2)每种关系型数据库都有自己的一套规范,这时候,想用一套 java 代码操作所有的关系型数据库,是行不通的。
所以呢,就期望使用统一的一套,java 代码,去操作所有的关系型数据库。
官方,就是,Sun 公司的程序员写了一套 java 代码,起名叫 JDBC,定义了操作所有关系数据库的规则(接口)。 定义了一套接口。
这个时候,所有的数据库厂商都使用这一套接口,而具体的实现类,都是这些数据库厂商自己实现的。(而这每种关系型数据的实现类,有个名字,叫做 数据库驱动)
简述 JDBC 本质:
其实是官方(sun 公司)定义了一套操作所有关系型数据库的规则,即接口。各个数据库 厂商去实现这套接口(JDBC),提供数据库驱动 jar 包。我们可以使用这套接口编程,真正执行的代码是驱动 jar 包中的实现类,对应的实现方法。
2 快速入门
了解 java 语言如何操作数据库,流程,步骤
步骤:
-
导入驱动 jar 包
-
注册驱动 (让程序知道使用的是哪一个驱动包,哪个版本)
-
获取数库连接对象 Connection (这个对象就是本地的 java 代码和数据裤之间的桥梁对象)
-
定义 sql 语句(通过一些方式将 sql 语句发送给数据库,从而达到 java 代码操作数据库的要求)
-
获取执行 sql 语句的对象 Statement
-
执行 sql ,接收返回结果
-
处理结果
-
释放资源
为什么需要释放jdbc资源?
提问: Connection、Statement 和 ResulSet?这三个对象是在方法内部定义的,则这三个对象不是在方法执行完毕就消失了么,为什么还要单独去关闭它们呢? 解答: 这个连接是与数据库服务器的一个连接,虽然你的方法结束了,但是这个资源依然存在,数据库连接并没有释放。 提问: 为什么在 JDBC 对数据库访问结束后,要按先关闭 ResultSet,然后关闭 PreparedStatement ,最后关闭 Connection,直接关闭 Connection 不就行了吗? 解答: 1. 感觉上好象是只要把connection给关闭了,系统就能正常运行了。 那在查询或是其它操作中,如果只关闭Connection,不作 ResultSet 和 Statement 的关闭的话,对系统性能是否会有影响呢?或者是其它实方面的不良影响。 如果你不使用连接池,那么就没有什么问题,一旦 Connection 关闭,数据库物理连接就被释放,所有相关 Java 资源也可以被G C 回收了。 但是如果你使用连接池,那么请注意,Connection 关闭并不是物理关闭,只是归还连接池,所以 PreparedStatement 和 ResultSet 都被持有,并且实际占用相关的数据库的游标资源,在这种情况下,只要长期运行,往往就会报“游标超出数据库允许的最大值”的错误,导致程序无法正 常访问数据库 2. 因为你打开的时候有顺序, 打开时:Connection -> PreparedStatement -> ResultSet 关闭时:ResultSet-> PreparedStatement -> Connection 这个就像 栈,后进先出
package com.kang.jdbc; import java.sql.*; public class jdbcDemo04 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { // 0. 添加驱动 // 1. 注册驱动 Class.forName("com.mysql.cj.jdbc.Driver"); // 2. 获取 Connection 对象 conn = DriverManager.getConnection("jdbc:mysql:///mybatis?serverTimezone=Asia/Shanghai", "root", "root"); // 3. 定义 sql String sql = "delete from users where id = 1"; // 4. 获取执行 sql 的对象 stmt = conn.createStatement(); // 5. 执行 sql int count = stmt.executeUpdate(sql); // 6. 处理结果 System.out.println(count); if (count != 0) { System.out.println("Success!"); } else { System.out.println("Fail!"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { // 7. 释放资源 if (stmt != null) try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } if (conn != null) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
3 详解各个对象
1、 DriverManager:
(驱动管理对象)
功能:
-
注册驱动 (告诉程序该使用哪一个数据库驱动 jar )
static void registerDriver(Driver driver) :注册给定的驱动程序 DriverManager
写代码使用: Class.forName("com.mysql.cj.jdbc.Driver");
(加载Driver 类,肯定有代码在类被加载的时候被执行,一定是放在 静态代码块中的。Class.forName 方法的作用,就是初始化给定的类。而我们给定的 MySQL 的 Driver 类中,它在静态代码块中通过 JDBC 的 DriverManager 注册了一下驱动。我们也可以直接使用 JDBC 的驱动管理器注册 mysql 驱动,从而代替使用 Class.forName。
DriverManager.registerDriver(new Driver)
)通过查看源码发现:在 com.mysql.cj.jdbc.Driver 类中存在静态代码块,其实是 DriverManager 注册驱动,
Class.forName("com.mysql.cj.jdbc.Driver");
这样写比较简单

【注意】mysql 5 之后的驱动 jar 包可以省略注册驱动步骤,建议写上。
原因: services 里写上了

-
获取数据库连接
-
方法:
static Connection getConnection(String url, String user, String password)
-
url : 指定连接的路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
例子:jdbc:mysql://localhost:3306/mybatis
细节: 如果连接的是本机的 mysql 服务器,并且 mysql 服务器默认端口是 3306 ,则 url 可以简写为: jdbc: mysql 😕//数据库名称
jdbc:mysql:///mybatis?serverTimezone=Asia/Shanghai
-
user: 用户名
-
password:密码
-
2 、Connection:
数据库连接对象
功能:
-
获取执行 sql 的对象
Statement createStatement()
PrepareStatement prepareStatement(String sql)
-
管理事务:
- 开启事务:
void setAutoCommit(boolean autoCommit)
调用该方法设置参数为false
,即开启事务 - 提交事务:
commit()
- 回滚事务:
rollback()
- 开启事务:
3、 Statement:
执行 sql 的对象
-
boolean execute(String sql)
: 可以执行任意 sql (了解一下,用的不多) -
int executeUpdate(String sql)
: 执行 DML(insert ,update,delete)
DDL(create , alert ,drop )
语句。返回值(int):影响的行数, 可以通过这个影响的行数来判断 DML 语句是否执行成功,返回值 >0 的则执行成功,反之,则执行失败。
-
ResultSet executeQuery(String sql)
执行 DQL(Select语句)
用于执行静态 sql 语句并返回其生成的结果的对象。
4、 ResultSet:
结果集对象:就是来封装查询的结果

-
boolean next() : 游标向下移动一行, 判断当前行是否是最后一行末尾(是否有数据)如果是则返回 false, 如果不是 则返回 true
-
getXxx(参数): 获取数据
- ( Xxx 代表数据类型) 如 int getInt(),这个方法会返回一个 int 类型的值
- 参数:
- int:代表列的编号 ,从 1 开始 。 如 getString (1) 获取第一列的数据
- String :代表列的名称。 如: getInt(id)
ResultSet 初步使用:
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///mybatis?serverTimezone=Asia/Shanghai", "root", "root");
String sql = "select * from db1";
stmt = conn.createStatement();
// 结果集对象也是一个资源,不要忘记关闭
rs = stmt.executeQuery(sql);
// 处理结果
// 1. 让游标向下移动一行, (本来是指向字段名的,向下一行指向数据)
rs.next();
// 2. 获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
// 3. 打印数据
System.out.println(id + "---" + name);
// 只能打印一行数据 ,可以通过重复 复制处理结果代码,实现显示多条信息,且不知道有多少条数据,会报错, After end and resultSet 有很大的局限性
// 还可以改良,看看下面的 resultSet 真正使用方法
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
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();
}
}
}
}
ResultSet 真正使用:
- 游标向下移动一行
- 判断是否有数据
- 获取数据
public class JdbcDemo07 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///mybatis?serverTimezone=Asia/Shanghai", "root", "root");
String sql = "select * from db1";
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
/**
重要的在这里!
*/
// 处理结果 rs.next() 返回的是 boolean 值,如果没有
while (rs.next()) {
// 循环判断游是否是最后一行末尾
// 获取数据 ,打印数据
int id = rs.getInt(1);
String name = rs.getString("name");
System.out.println(id + "---" + name);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
练习:
定义一个方法,查询 db1 表中的数据,将其封装为对象,然后装在集合(专门装载对象的容器),返回,打印。
1. 定义` user `类
2. 定义方法 `public List<Emp> findAll(){}`
3. 实现方法 ` select * from user`
create table mybatis.db1
(
id int(100) auto_increment
primary key,
name varchar(20) null
);
我们来实现一下:
- 先定义一个 User 类,封装 db1 表数据的 JavaBean
public class User {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 方便打印看效果
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
- 然后写 jdbc
/*
定义一个方法,查询 db1 表的数据将其封装为对象,然后装在集合,返回
*/
public class JdbcDemo08 {
public List<User> findAll() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List<User> list = null;
try {
// 1. 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取连接对象
conn = DriverManager.getConnection("jdbc:mysql:///mybatis?serverTimezone=Asia/Shanghai", "root", "root");
// 3.定义 sql
String sql = "select * from db1";
// 4. 获取执行 sql 的对象
stmt = conn.createStatement();
// 5. 执行 sql
rs = stmt.executeQuery(sql);
// 6. 处理结果:遍历结果集,封装对象,装在集合
User user = null; // 复用 引用
list = new ArrayList<User>();
while (rs.next()) {
// 获取数据
int id = rs.getInt("id");
String name = rs.getString("name");
// 创建 user 对象 并赋值 这里实现 引用的复用
user = new User();
user.setId(id);
user.setName(name);
// 装载集合
list.add(user);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
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();
}
}
}
return list;
}
// 测试
public static void main(String[] args) {
List<User> list = new JdbcDemo08().findAll();
System.out.println(list);
}
}
结果:

问题来了!这样写代码的重复度特别高,每一次都要写,显然,这样写不太聪明,我们可以写一个工具类来简化一下代码。
接下来:
抽取 jdbc 的工具类: 目的 :简化代码!!
-
分析:
-
注册驱动
-
抽取一个方法获取对象
-
需求:不想传递参数(麻烦),还得保证工具类的通用性。
-
解决:配置文件。 前人想好的。。。
-
jdbc.properties
url=
user=
password=
-
-
-
抽取一个方法释放资源
-
JDBCUtils 工具类
/*
JDBC 工具类
*/
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
/**
* 获取连接
* @return 连接对象
* 在这里为了方便调用都写成,static
*/
// 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块来完成这个事情
static {
// 读取资源文件,获取值
try {
// 1. 创建 Properties 集合类
Properties pro = new Properties();
// 获取 src 路径下的文件的方式 ----> ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader(); // 获取 classLoader 对象
// 传一个文件名,就能获取一个resource 资源 URL :统一资源定位符,可以定位一个文件的绝对路径
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();
System.out.println(path);
// 2. 加载文件
//pro.load(new FileReader("src/jdbc.properties"));
pro.load(new FileReader(path));
// 3. 获取数据,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
// 4. 注册驱动
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
// executeUpdate 和 executeQuery 两个方法返回的东西不一样,后者还要返回 resultSet 多了一个释放的资源,一共要释放3个资源
// 所以这里用重载写一下
/**
* 释放资源
*
* @param stmt
* @param conn
*/
public static void close(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(ResultSet rs, Statement stmt, Connection conn) {
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();
}
}
}
}
还是上面的例子,演示 JDBCUtils
/*
测试 JDBC 工具类
定义一个方法,查询 db1 表的数据将其封装为对象,然后装在集合,返回
*/
public class JdbcDemo09 {
public List<User> findAll2() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List<User> list = null;
try {
conn = JDBCUtils.getConnection();
// 3.定义 sql
String sql = "select * from db1";
// 4. 获取执行 sql 的对象
stmt = conn.createStatement();
// 5. 执行 sql
rs = stmt.executeQuery(sql);
// 6. 处理结果:遍历结果集,封装对象,装在集合
User user = null; // 复用 引用
list = new ArrayList<User>();
while (rs.next()) {
// 获取数据
int id = rs.getInt("id");
String name = rs.getString("name");
// 创建 user 对象 并赋值 这里实现 引用的复用
user = new User();
user.setId(id);
user.setName(name);
// 装载集合
list.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(rs,stmt,conn);
}
return list;
}
// 测试
public static void main(String[] args) {
List<User> list = new JdbcDemo09().findAll2();
System.out.println(list);
}
}
同样也是可以的!
登录案例:
需求:
1、通过键盘录入用户名和密码
2、判断用户是否登录成功
select * from user1 where username = "" and password ="";
如果这个 sql 有查询结果,登录成功,反之则失败
步骤:
1、创建一个数据库表 user 表
create table user1(
id int primary key auto_increment,
username varchar(32),
password varchar(32)
);
实现:
/*需求:
1、通过键盘录入用户名和密码
2、判断用户是否登录成功*/
public class JdbcDemo10 {
public static void main(String[] args) {
// 1. 键盘录入,接受用户名和密码
Scanner input = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = input.nextLine();
System.out.println("请输入密码:");
String password = input.nextLine();
// 2. 调用方法
boolean flag = new JdbcDemo10().login(username, password);
if (flag){
// 登录成功
System.out.println("登录成功!");
}else{
System.out.println("用户名或密码错误!");
}
}
/**
* 登录方法
*/
public boolean login(String username, String password) {
if (username == null || password == null) {
return false;
}
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
// 连接数据库来判断是否登录成功
try {
// 1.获取连接
conn = JDBCUtils.getConnection();
// 2. 定义 sql
String sql = "select * from user1 where username='" + username + "' and password = '" + password + "'";
// 3. 获取执行 sql 的对象
stmt = conn.createStatement();
// 4. 执行 sql
rs = stmt.executeQuery(sql);
// 5. 判断
/* if(rs.next()){ // 如果有下一行,则返回 true
return true;
}else{
return false;
} // 这样写代码太垃圾了,本来就是 个布尔值 下面这样写*/
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(rs,stmt,conn);
}
// 这里不知道具体情况,先返回个 false ,防止报错
return false;
}
}
5、 prepareStatement:
执行 sql 对象 继承 Statement,功能比 Statement 更强大
上面的登录,写的其实是有问题的,会有 sql 注入问题。

居然登录成功了? 这个就是 sql 注入问题了。
-
sql 注入问题:在拼接 sql 时,有一些 sql 的特殊关键字参与字符串的拼接。会造成安全性问题。
- 输入用户随便,输入密码:
a' or 'a' = 'a
- sql :
select * from user1 where username = 'sdf' and password = a' or 'a' = 'a'
这句 sql 最后结果是 true。 所以会查询出来所有信息。
- 输入用户随便,输入密码:
-
解决 sql 注入问题:使用
PreparedStatement
对象来解决 -
预编译 SQL: 参数使用
?
作为占位符 -
步骤:
-
导入驱动 jar 包
-
**注册驱动 **
-
**获取数库连接对象 **
-
定义 sql 语句
注意:sql 的参数所使用
?
作为占位符,如select * from user1 where username = ? and password = ? ;
-
获取执行 sql 语句的对象 preparedStatement (
Connection.prepareStatement(String sql)
) -
给
?
去赋值方法:setXxx( 参数1,参数2)
参数1:? 的位置从 1 开始
参数2: ? 的值
-
执行 sql ,接收返回结果,不需要传递 sql
-
处理结果
-
释放资源
-
-
注意:后期都会使用
PrepareStatement
来完成增删改查的所有操作1、可以防止 sql 注入
2、效率更高
我们把上面的登录案例改进一下
/*需求:
1、通过键盘录入用户名和密码
2、判断用户是否登录成功*/
public class JdbcDemo11 {
public static void main(String[] args) {
// 1. 键盘录入,接受用户名和密码
Scanner input = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = input.nextLine();
System.out.println("请输入密码:");
String password = input.nextLine();
// 2. 调用方法
boolean flag = new JdbcDemo11().login(username, password);
if (flag){
// 登录成功
System.out.println("登录成功!");
}else{
System.out.println("用户名或密码错误!");
}
}
/**
* 登录方法 使用 prepareStatement 实现
*/
public boolean login(String username, String password) {
if (username == null || password == null) {
return false;
}
// 连接数据库来判断是否登录成功
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 1.获取连接
conn = JDBCUtils.getConnection();
// 2. 定义 sql
String sql = "select * from user1 where username= ? and password = ? ";
// 3. 获取执行 sql 的对象
pstmt = conn.prepareStatement(sql);
// 给 ? 赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
// 4. 执行 sql 的时候,不需要传参
rs = pstmt.executeQuery();
// 5. 判断下一行有没有下一行 sql 查出东西没有,
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(rs,pstmt,conn);
}
return false;
}
}
4 JDBC 控制事务
-
事务:一个包含多个步骤的业务操作,如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
-
操作:
- 开启事务
- 提交事务
- 回滚事务
-
使用的是
Connection
对象来管理事务-
开启事务:
void setAutoCommit(boolean autoCommit)
调用该方法设置参数为false
,即开启事务在执行 sql 之前开启事务 事务开始的地方
-
提交事务:
commit()
当所有 sql 执行完之后提交事务
-
回滚事务:
rollback()
回滚到事务开始的地方,一般在财务转账的时候用的较多.防止扣钱成功,却加钱失败的现象.在 catch 中回滚事务
-
事务的管理
要知道 什么时候开事务,操作都执行完了,提交事务,一旦操作异常,回滚事务
事务回滚和提交只会执行一个,提交就是正常执行,回滚就是不正常执行.
实现:
/**
* 事务操作
*/
public class JdbcDemo12 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
// 1. 获取连接
conn = JDBCUtils.getConnection();
// 开启事务 事务手动提交
conn.setAutoCommit(false);
// 2. 定义 sql
// 2.1 张三 -500
String sql1 = "update account set balance = balance - ? where id = ?";
// 2.2 李四 +500
String sql2 = "update account set balance = balance + ? where id = ?";
// 3. 获取执行 SQL 的对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
// 4. 设置参数
pstmt1.setDouble(1,500);
pstmt1.setInt(2,1);
pstmt2.setDouble(1,500);
pstmt2.setInt(2,2);
// 5.执行 sql
int count = pstmt1.executeUpdate();
// 手动设置异常
int i= 3/0 ;
int count1 = pstmt2.executeUpdate();
// 提交事务
conn.commit();
} catch (Exception e) {
// 事务回滚
try {
if (conn != null)
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}finally{
JDBCUtils.close(pstmt2,null);
JDBCUtils.close(pstmt1,conn);
}
}
}
如何优化和简化 JDBC 的东西呢 ?
5 数据库连接池
简述
为什么要使用数据库连接池呢?

获取连接的速度快了,我们知道 向系统底层申请连接是很耗时的,优化的用户体验
- 数据库连接池概念:其实就是一个容器(集合),存放数据的连接对象的容器。
当系统初始化好之后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
- 好处:
- 节约了系统的资源
- 用户访问高效
-
实现
1、标准接口:
DataSource
javax.sql
包下的- 方法
- 获取连接对象:
getConnection
- 归还连接:如果连接对象
Connection
是从连接池中获取的,那么调用Connection.close()
方法,则不会再关闭连接,而是归还连接
- 获取连接对象:
- 一般我们不去实现它,由数据库厂商来实现
- C3P0:数据库连接池技术
- Druid: 数据库连接池技术实现技术 ,由阿里巴巴提供的 ,号称全世界最好的连接池结束之一
- 方法
C3P0
数据库连接池技术
步骤:
-
导入 jar 包 (三个包,c3p0 jar 包,它的依赖包,还有数据库驱动 jar 包) (
c3p0-0.9.5.5.jar 、 mchange-commons-java-0.2.19.jar
) -
两种使用方式,一种是自己定义,另一种是使用配置文件的方式,我们一般使用配置文件的形式。 如下:
定义配置文件:
- 名称:必须是
C3P0.properties or C3P0-config.xml
- 路径:直接将文件放在
src
目录下即可。
- 名称:必须是
-
创建核心对象 数据库连接池对象
CombopooledDateSource
-
获取连接 :
getConnection
简单实现:
public class C3P0Demo02 {
public static void main(String[] args) throws SQLException {
// 1. 获取数据库连接池对象 什么都没传,使用默认配置,
// DataSource ds = new ComboPooledDataSource();
// 1.1 获取 DataSource ,使用指定的名称配置
DataSource ds = new ComboPooledDataSource("otherc3p0");
// 2. 获取连接
for (int i = 0; i <= 7; i++) {
Connection conn = ds.getConnection();
System.out.println(i+":"+conn);
// if(i == 5){
// conn.close(); // 归还连接到连接池中
//
// }
}
}
}
C3P0-config.xml
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<!--初始化申请的连接数量-->
<property name="initialPoolSize">5</property>
<!--最大的连接数量-->
<property name="maxPoolSize">10</property>
<!--超时时间,3 秒之后才会报错 当连接池用完时客户端调用 getConnection() 后等待获取新连接的时间,超时后将抛出 -->
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">8</property>
<property name="checkoutTimeout">3000</property>
</named-config>
</c3p0-config>
Druid
数据库连接池实现技术 由阿里巴巴提供的,号称全世界最好的连接池结束之一
步骤:
-
导入 jar 包
-
定义配置文件:
- 是
properties
形式的 - 可以是任意名称的,可以放在任意的目录下
- 是
-
加载配置文件 Properties
-
获取数据库连接池对象:通过工厂来获取
DruidDataSourceFactory
-
获取连接
getConnection()
代码演示:
public class DruidDemo01 {
public static void main(String[] args) throws Exception {
// 1. 导入 jar 包
// 2. 定义配置文件
// 3. 加载配置文件
// 属性集对象
Properties pro = new Properties();
// 读取配置文件
InputStream is = DruidDemo01.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
// 4. 获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
// 5. 获取连接
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
那我们实际怎么使用呢?
定义工具类
- 定义一个类
JDBCUtils
- 提供静态代码块加载配置文件,初始化连接对象
- 提供方法
- 获取连接的方法:通过数据库连接池获取连接
- 释放资源
- 获取连接池的方法
上代码:
DruidUtils
/**
* Druid 连接池的工具类
*/
public class JDBCUtils {
// 1. 定义一个成员变量 DataSource
private static DataSource ds;
static {
try {
// 1. 加载配置文件
Properties pro = new Properties();
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
// 2. 获取 DataSource
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接对象
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
// 释放资源
public static void close(Statement stmt, Connection conn) {
/* if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close(); // 归还连接
} catch (SQLException e) {
e.printStackTrace();
}
}*/
close(null,stmt,conn);
}
public static void close(ResultSet rs, Statement stmt, Connection conn) {
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();
}
}
}
/*
获取连接池的方法
* */
public static DataSource getDataSource(){
return ds;
}
}
这是一个工具类,接下类, 实际用一下吧!!
/**
需求
* 完成添加操作,给 account 表添加一条记录
* */
/*
演示druid 工具类
*/
public class DruidDemo02 {
public static void main(String[] args) {
/**
* 完成添加操作,给 account 表添加一条记录
* */
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 1. 获取连接对象
conn = JDBCUtils.getConnection();
// 2. 定义 sql
String sql = "insert into account values(null,?,?)";
// 3. 获取 pstmt 对象
pstmt = conn.prepareStatement(sql);
// 4. 给 ? 赋值
pstmt.setString(1, "王伟");
pstmt.setDouble(2, 3000);
// 5. 执行 sql
int count = pstmt.executeUpdate();
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6. 释放资源
JDBCUtils.close(pstmt, conn);
}
}
}
结果:

添加成功
6 Spring JDBC : JDBC Template
序:
在使用了连接池之后,我们的连接对象复用性更高了!程序整体运行性能也更高了,但是在做具体的 jdbc
操作的时候,还是比较麻烦,要定义 sql, 要执行 sql,要设置参数,要处理结果,尤其是 resultSet,要遍历结果集,封装对象,装在集合,非常麻烦! 这些体力活我们不想做!!
为了简化我们的开发步骤!
Spring JDBC
Spring,JavaEE 的灵魂框架!
Spring
框架对 jdbc
的简单封装,提供了一个JdbcTemplate
对象简化 JDBC 的开发
本文来自博客园,作者:走马!,转载请注明原文链接:https://www.cnblogs.com/zou-ma/p/16052493.html
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术