欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

事务处理

程序员文章站 2022-07-12 17:03:12
...

事务是保证底层数据完整的重要手段,对于任何数据库都是非常重要的。事务是由一步或几步数据库操作序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。

事务具有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后端技术栈)。
事务处理