JDBC(六)——JDBC事务处理

事务处理:所有的操作要么一起成功,要么一起失败,事务本身具有原子性(atomicity)、一致性(consistency)、隔离性或独立性(isolation)、持久性(durability)四个特性,这四个特性被称为ACID特征。
原子性:原子性是事务的最小单元,是不可再分隔的单元,相当于一个小小的数据库操作,这些操作必须同时成功,如果一个失败了,则一切的操作将全部失败。
一致性:在数据库操作的前后是完全一样的,保证数据的有效性。如果事务正常操作则系统会维持有效性,如果事务出现了错误,则返回到最原始状态,也要维持其有效性。这样保证事务开始时和结束时系统处于一致状态。
隔离性:多个事务可以同时进行,且彼此之间无法访问,只有当事务完成最终操作时,才可以看到结果。
持久性:事务完成之后,它对于系统的影响是永久性的,该修改即使出现致命的系统故障也将一直保持。

Mysql对事务的支持

JDBC事务处理

注意:由于切换数据库,数据库连接里的url地址需要修改。
若转账过程中出现事故:转出成功500,但转入失败500,则张三500李四依然是1000。此时就需要天剑事务处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//将连接的自动提交设为false
con.setAutoCommit(false);
//遇到异常回滚
try {
con.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//最终提交事务
con.commit();

/*
* 获取数据库连接
*/
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class DbUtil {
// 数据库地址
private static String dbUrl = "jdbc:mysql://localhost:3306/db_bank";
// 驱动名称
private static String jdbcName = "com.mysql.fabric.jdbc.FabricMySQLDriver";
private static String dbUserName = "root"; // 数据库用户名
private static String dbPassword = "123456"; // 数据库密码

/**
*获取数据库连接的方法
* @return
* @throws Exception
*/
public Connection getCon() throws Exception {
Class.forName(jdbcName); //加载数库驱动
System.out.println("加载数据库驱动成功");
Connection con = DriverManager
.getConnection(dbUrl, dbUserName, dbPassword);//获取数据库连接
System.out.println("获取数据库连接成功");
return con;
}
/**
* 关闭连接
* @param con
* @throws Exception
*/
public void close(Statement stat, Connection con) throws Exception {
if (stat != null) {
stat.close();
if (con != null) {
con.close();
}
}

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.java.jdbc.chap09.sec01;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import com.java.jdbc.util.DbUtil;

/**
* 张三向李四转账500,
*若不成功,则回滚,不影响总数
* @author think
*
*/
public class Demo1 {
private static DbUtil dbUtil = new DbUtil();
/**
* 转出
* @param con
* @param accountName 账户名称
* @param accountBalance 转账金额
* @throws Exception
*/
private static void outAccount(Connection con, String accountName, int accountBalance) throws Exception {
//Connection con = dbUtil.getCon();
String sql = "UPDATE t_account SET accountBalance = accountBalance - ? WHERE accountName = ?";
PreparedStatement pstat = con.prepareStatement(sql);
pstat.setString(2, accountName);
pstat.setInt(1, accountBalance);
pstat.executeUpdate();
}
/**
* 转入
* @param con
* @param accountName
* @param accountBalance
* @throws Exception
*/
private static void inAccount(Connection con, String accountName, int accountBalance) throws Exception {
String sql = "UPDATE t_account SET accountBalance = accountBalance + ? WHERE accountName = ?"; //account转入报错,但因为进行了事务处理,转账和入账需要同时进行才成功
PreparedStatement pstat = con.prepareStatement(sql);
pstat.setString(2, accountName);
pstat.setInt(1, accountBalance);
pstat.executeUpdate();
}
public static void main (String[] args) {
Connection con = null;
try {
con = dbUtil.getCon();
//将自动提交设为false
con.setAutoCommit(false);
//开始转账
System.out.println("张三开始向李四转账:");
int account = 500;
outAccount(con, "张三", account);
inAccount(con, "李四", account);
System.out.println("转账成功!");
} catch (Exception e) {
//遇到异常回滚
try {
con.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
//最终提交事务
con.commit();
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}

事务保存点

张三转账后设保存点,后面回滚只回滚到张三后面的保存点:张三只管转出,不管后面,这个例子不恰当,一般不舍保存点。

1
2
3
4
5
6
7
8
9
10
Savepoint sp = null;
try{

int account = 500;
outAccount(con, “张三”, account);
//设保存点
sp = con.setSavepoin();
inAccount(con, “李四”, account);

}