目录
一、事务
事务是由一步或几步数据库操作序列组成的逻辑执行单元,这些序列要么都执行,要么都不执行
事务的四个特性:
- 原子性:事务是最小执行单位,具有不可再分的特性,事务中的操作要么都执行,要么都不执行
- 一致性:事务执行前和执行后,数据库都处于一致性状态
- 隔离性:各事务的执行互不干扰,任意一个事物的内部操作对其它事务都是隔离的
- 持久性:事务一旦提交,对数据库所做的任何操作都永久地记录到存储器中
事务处理过程的三个概念:
- 事务提交:指成功执行事务,事务提交又分两种方式:显式提交(commit)和自动提交(正常执行完毕)
- 事务中止:指未能成功完成事务,从而执行中断
- 事务回滚:指对事务中止所造成的的变更需要进行撤销处理,数据库返回事务执行前的状态,事务回滚又分两种方式:显式方式(rollback)和自动回滚(系统错误或强行退出)
事务操作的具体步骤:
- 开启事务
- 执行任意条DML语句
- 执行成功,则提交事务
- 执行失败,则回滚事务
注意:
Connection接口在默认情况下会自动提交,即事务是关闭的,即一条SQL语句执行成功后,系统会立即调用commit()方法提交到数据库,而无法对其进行rollback回滚操作
通过调用Connection对象的setAutoCommit(boolean bool)方法可以开启或者关闭自动提交模式
代码:
// 开启事务(关闭自动提交)
con.setAutoCommit(false);
// 提交事务
con.commit();
// 回滚事务
con.rollback();
实例演示:
import java.sql.*;
public class EventManagementDemo {
// JDBC控制事务
// 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
private static String driver = "com.mysql.cj.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/de?serverTimezone=UTC&useSSL=false";
private static String user = "root";
private static String password = "root";
public static void main(String[] args) {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
int count = 0;
try {
connection = DriverManager.getConnection(url,user,password); // 获取数据库连接对象
String sql = "UPDATE tb_student SET studentName=? WHERE studentNo=?";
pstmt = connection.prepareStatement(sql);
pstmt.setString(1, "张三");
pstmt.setInt(2,2013110101);
// ------开启事务------- // 同时成功/同时失败
connection.setAutoCommit(false);// 在执行sql语句之前开启事务
count = pstmt.executeUpdate();
System.out.println("更新" + count + "条记录");
String sql_select = "SELECT * FROM tb_student";
pstmt = connection.prepareStatement(sql_select);
rs = pstmt.executeQuery();
while(rs.next()){
int no = rs.getInt(1);
String name = rs.getString(2);
System.out.println(no+"\t"+name);
}
int exception = 5/0; // 手动制造异常
/*
通过实验可以得知:
在程序出错的情况下:
如果不开启事务管理,程序在执行到错误到错误代码片段时,
执行sqi语句的对象依然可以对数据库的数据进行修改,
反之,如果开启事务管理,
执行到错误代码片段时,执行sql语句的对象无法对数据库里面的数据进行操作
*/
// ------提交事务-------
connection.commit(); // 当所有sql语句都执行完提交事务
} catch (SQLException e) {
// ------回滚事务-------
try {
connection.rollback(); // 在catch中回滚事务
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null){
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
根据结果集返回的数据可知,JDBC似乎成功修改了数据库中数据表的信息,但由于开启了事务管理,所以出现事务中断时,JDBC对数据库的修改并未起作用,而是恢复到了执行前的状态
执行SQL语句后的数据表:
二、保存点
JDBC通过保存点,可以更好地控制事物回滚
原数据表:
import java.sql.*;
public class SavePointDemo {
// JDBC还支持保存点操作,通过保存点,可以更好的控制事务回滚
// 保存点是事务中的逻辑回滚点
// 设置保存点时,只要在保存点之后发生错误,就可以使用该rollback()方法撤消在保存点之前所做的事件。
private static String driver = "com.mysql.cj.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/de?serverTimezone=UTC&useSSL=false";
private static String user = "root";
private static String password = "root";
public static void main(String[] args) {
Connection connection = null;
Savepoint savepoint = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
Class.forName(driver);
connection = DriverManager.getConnection(url,user,password); // 获取数据库连接对象
// -------开启事务-------
connection.setAutoCommit(false);
String sql1 = "INSERT INTO tb_student VALUES(2014310103,'张三','男','2022-7-5','北京','汉',null)";
pstmt = connection.prepareStatement(sql1);
pstmt.executeUpdate();
// 设置保存点
savepoint = connection.setSavepoint();
String sql2 = "INSERT INTO tb_student VALUES(2014310104,'李四','女','2022-7-6','上海','满',null )";
pstmt = connection.prepareStatement(sql2);
pstmt.executeUpdate();
// 回滚保存点
connection.rollback(savepoint);
// ------提交事务-------
connection.commit(); // 当所有sql语句都执行完提交事务
// -----恢复原有事务提交状态------
connection.setAutoCommit(true);
} catch (SQLException | ClassNotFoundException e) {
// ------回滚事务-------
if(connection != null){
try {
connection.rollback(); // 在catch中回滚事务
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null){
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
上述代码首先声明了一个Savepoint对象,然后在执行第一条数据插入操作后调用setSavepoint()方法设置保存点,执行完第二条插入操作后,事物回滚到保存点
进行插入操作后的数据表显示,在保存点之前执行的插入操作成功,而保存点之后执行操作失效
三、批量更新
批量更新:即多条SQL语句将作为一批操作被同时收集,并同时提交
批量更新需得到底层数据库的支持,通过调用DatabaseMetaData接口的supports()方法来查看底层数据库是否支持批量更新
批量更新的步骤:
- 创建一个Statement对象
- 调用Statement对象的addBatch()方法收集多条SQL语句
- 调用Statement对象的execteBatch()或executeLargeBatch()方法同时执行所有的SQL语句
原数据表:
import java.sql.*;
public class DatabaseMetaDataDemo{
// JDBC提供了批量更新功能,多条SQL语句将被作为一批操作被同时收集、提交
// 通过调用DatabaseMetaData的supports()来查看底层数据库是否支持批量更新
private static String driver = "com.mysql.cj.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/de?serverTimezone=UTC&useSSL=false";
private static String user = "root";
private static String password = "root";
public static void main(String[] args) {
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
int count = 0;
try {
connection = DriverManager.getConnection(url, user, password); // 获取数据库连接对象
// 1.创建一个statement对象
stmt = connection.createStatement();
// 2.使用statement对象的addBatch方法收集多条sql语句
String sql1 = "UPDATE tb_student SET classNo='ABC123' WHERE studentName='狗蛋'";
String sql2 = "UPDATE tb_student SET classNo='ABC123' WHERE studentName='王一敏'";
String sql3 = "UPDATE tb_student SET classNo='ABC123' WHERE studentName='江山'";
String sql4 = "UPDATE tb_student SET classNo='ABC123' WHERE studentName='李明'";
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.addBatch(sql4);
// 3.调用statement对象的excuteBatch方法同时执行所有sql语句
stmt.executeBatch();
} catch (SQLException e) {
e.printStackTrace();
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}