JDBC处理事务
在数据库操作中,一项事务是由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单元。只有当事务中的所有操作都正常完成,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务会被撤销。例如,在银行的转账业务中,假定zhangsan从自己的账号上把200块钱转到lisi的账号里,相关的SQL语句如下:
UPDATE ACCOUNT set MONEY=MONEY-200 WHERE NAME='zhangsan';
UPDATE ACCOUNT set MONEY=MONEY+200 WHERE NAME='lisi';
在上述SQL语句中,它们只有全部执行成功,才能提交整个事务。否则,如果zhangsan账号的钱少了200,而lisi账号的钱没有变化,势必会造成银行转账业务的混乱。
针对JDBC处理事务的操作,在Connection接口中,提供了三个相关的方法,具体如下:
● setAutoCommit(boolean autoCommit):设置是否自动提交事务。
● commit():提交事务。
● rollback():撤销事务。
在上述三个方法中,默认情况下,事务是自动进行提交的。也就是说,如果每一条操作数据库的SQL语句执行成功,系统会自动调用commit()方法来提交事务,否则就自动调用rollback()撤销事务。
为了大家更好地学习JDBC如何处理事务,接下来,通过一个案例来模拟银行之间的转账业务,具体步骤如下:
(1)首先创建一个chapter02的数据库,并在该数据库中创建名称为account的表,向表中插入若干条数据,具体的SQL语句如下所示:
CREATE DATABASE chapter02;
USE chapter02;
CREATE TABLE account(
id int primary key auto_increment,
name varchar(40),
money float
)character set utf8 collate utf8_general_ci;
INSERT INTO account(name,money) VALUES('aaa',1000);
INSERT INTO account(name,money) VALUES ('bbb',1000);
INSERT INTO account(name,money) VALUES ('ccc',1000);
上述SQL语句执行成功后,使用SELECT语句查询account表中的数据,SQL语句的执行结果如下:
mysql> SELECT * FROM account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | aaa | 1000 |
| 2 | bbb | 1000 |
| 3 | ccc | 1000 |
+----+------+-------+
3 rows in set (0.00 sec)
从上述结果可以看出,account表存在三条数据,并且这三条数据的money值都为1000。
(2)新建工程chapter02,在工程中,新建一个类Example01,该类用于模拟两个账号之间的转账业务,Example01的具体实现代码如例1所示。
例1 Example01.java
1 package cn.itcast.jdbc.example;
2 import java.sql.Connection;
3 import java.sql.PreparedStatement ;
4 import java.sql.SQLException;
5 import cn.itcast.utils.JDBCUtils;
6 public class Example01{
7 public static void main(String[] args) {
8 String outAccount = "aaa";
9 String inAccount = "bbb";
10 double amount = 200;
11 Connection conn = null;
12 PreparedStatement pstmt1 = null;
13 PreparedStatement pstmt2 = null;
14 try {
15 conn = JDBCUtils.getConnection();
16 // 控制事务,关闭事务的自动提交
17 conn.setAutoCommit(false);
18 // 账号转出200
19 String sql = "UPDATE account SET money = money-? WHERE "
20 + "name=? AND money>=200";
21 pstmt1 = conn.prepareStatement(sql);
22 // 设置参数
23 pstmt1.setDouble(1, amount);
24 pstmt1.setString(2, outAccount);
25 pstmt1.executeUpdate();
26 // 账号转入200
27 String sql2 = "update account set money=money+? where name=?";
28 pstmt2 = conn.prepareStatement(sql2);
29 pstmt2.setDouble(1, amount);
30 pstmt2.setString(2, inAccount);
31 pstmt2.executeUpdate();
32 // 提交事务
33 conn.commit();
34 System.out.println("转账成功");
35 } catch (Exception e) {
36 // 回滚事务
37 try {
38 conn.rollback();
39 System.out.println("转账失败");
40 } catch (SQLException e1) {
41 e1.printStackTrace();
42 }
43 } finally {
44 if (pstmt1 != null) {
45 try {
46 pstmt1.close();
47 } catch (SQLException e) {
48 e.printStackTrace();
49 }
50 pstmt1 = null;
51 }
52 if (pstmt2 != null) {
53 try {
54 pstmt2.close();
55 } catch (SQLException e) {
56 e.printStackTrace();
57 }
58 pstmt2 = null;
59 }
60 if (conn != null) {
61 try {
62 conn.close();
63 } catch (SQLException e) {
64 e.printStackTrace();
65 }
66 conn = null;
67 }
68 }
69 }
70 }
程序运行后,会在控制台打印出“转账成功”的结果,这时,进入MySQL,使用SELECT语句查看当前的account表,SQL语句的执行结果如下:
mysql> SELECT * FROM account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | aaa | 800 |
| 2 | bbb | 1200 |
| 3 | ccc | 1000 |
+----+------+-------+
3 rows in set (0.00 sec)
从上述执行结果可以看出,第一条数据的money值为800,第二条数据的money值为1200。由此可见,JDBC程序成功实现了转账功能。
需要注意的是,将setAutoCommit()方法的参数设置为false后,事务必须使用conn.commit()方法提交,而事务的回滚不一定显式执行conn.rollback()。如果程序最后没有执行conn.commit(),事务也会回滚,一般是直接抛出异常,终止程序的正常执行。因此,通常情况下,我们会将conn.rollback()语句放在catch语句块中执行。