事务处理
事务是保证底层数据完整的重要手段,对于任何数据库都是非常重要的。事务是由一步或几步数据库操作序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。
事务具有ACID四个特性:
● 原子性(Atomicity):事务是应用中的最小执行单位,就如原子是自然界的最小颗粒一样,具有不可再分的特性。事务中的全部操作要么全部完成,要么都不执行。
● 一致性(Consistency):事务执行之前和执行之后,数据库都必须处于一致性状态,即从执行前的一个一致状态变为另一个一致性的状态。
● 隔离性(Isolation):各个事务的执行互不干扰,任意一个事务的内部操作对其他并发事务都是隔离的,即并发执行的事务之间不能看到对方的中间状态,并发事务之间是互不影响的。
● 持久性(Durability):事务一旦提交,对数据库所做的任何改变都永久地记录到存储器中,即保存到物理数据库中,不被丢失。
事务处理过程中会涉及到事务的提交、中止和回滚三个概念。“事务提交”是指成功执行完毕事务,事务提交又分显示提交和自动提交两种;“事务中止”是指未能成功完成事务,执行中断;“事务回滚”对于中止事务所造成的变更需要进行撤销处理,即事务所做的修改全部失效,数据库返回到事务执行前的状态,事务回滚也有显示回滚和自动回滚两种。
JDBC对事务操作提供了支持,其事务支持由Connection提供。JDBC的事务操作步骤如下:
① 开启事务;
② 执行任意多条DML语句;
③ 执行成功,则提交事务;
④ 执行失败,则回滚事务;
Connection在默认情况下会自动提交,即事务是关闭的。此种情况下,一条SQL语句更新成功后,系统会立即调用commit()方法提交到数据库,而无法对其进行rollback回滚操作。
使用Connection对象的setAutoCommit()方法可开启或者关闭自动提交模式,其参数是一个布尔类型,如果参数为false,表示关闭自动提交;如果参数为true(默认),则表示打开自动提交。
因此,在JDBC中,开启事务时需要调用Connection对象的setAutoCommit(false)来关闭自动提交,示例代码如下:
conn.setAutoCommit(false);
需要了解的是:使用Connection对象的getAutoCommit()方法能够获取该连接的自动提交状态,可以使用该方法来检查自动提交方式是否打开。
当所有的SQL语句都执行成功后,调用Connection的commit()方法来提交事务,代码如下所示:
conn.commit();
如果任意一条SQL语句执行失败,则调用Connection的rollback()方法来回滚事务,代码如下所示:
conn.rollback();
需要注意的是:实际上,当程序遇到一个未处理的SQLException异常时,系统会非正常退出,事务也会自动回滚;但如果程序捕获该异常,则需要在异常处理块中显式地调用Connection的rollback()方法进行事务回滚。
下述案例示例了JDBC的事务处理过程,代码如下所示。
【代码14.6】
TransactionExample.java
package com;
import java.sql.*;
public class TransactionExample {
static Connection conn ;
public static void main(String args[]) {
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://127.0.0.1:3306/student";
String user = "root";
String pass = "root";
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, pass);
// 使用Connection来创建一个Statement对象
Statement stmt = conn.createStatement();
boolean autoCommit = conn.getAutoCommit();
System.out.println("事务自动提交状态:" + autoCommit);
if (autoCommit) {
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
}
// 多条DML批处理语句
stmt.executeUpdate("INSERT INTO t_user(id,name,password,sex) "
+ "VALUES(10,'张四','123456','男')");
stmt.executeUpdate("INSERT INTO t_user(id,name,password,sex) "
+ "VALUES(11,'刘牛','123456','女')");
// 由于主键约束,下述语句将抛出异常
stmt.executeUpdate("INSERT INTO t_user(id,name,password,sex)"
+ " VALUES(11,'杨八','123456','男')");
// 如果顺利执行则在此提交
conn.commit();
// 恢复原有事务提交状态
conn.setAutoCommit(autoCommit);
} catch (Exception e) {
// 出现异常
if (conn != null) {
try {
// 回滚
conn.rollback();
} catch (SQLException se) {
se.printStackTrace();
}
}
e.printStackTrace();
}
}
}
上述代码在执行多条DML批处理语句时,由于主键限制,将会在插入第三个用户时抛出主键约束异常,从而使程序转到catch语句中,通过调用rollback()方法回滚事务,撤销前面所有的操作。如果将插入第三个用户的语句注释掉,则程序会正常执行,将两条数据插入到表中。
我是田先生,一名热爱技术、热爱生活的Java程序员。专注分享java基础、dubbo源码、zookeeper, rabbitmq、mybatis源码、微服务springboot、集群、分布式、多线程等相关知识与实战经验。欢迎大家积极交流,共同探讨。欢迎关注我的公众号:t-j20120622(Java后端技术栈)。
上一篇: Hibernate中的事务处理流程详解
下一篇: Lab1