12. JDBC事务的处理 以及 转账案例
前言
上一章节,我已经写了一篇数据库事务的章节。篇幅比较长,基本让我们知道了数据库事务操作、隔离级别等等知识。那么本章节我们再简化一下内容,再快速过一下事务处理 和 转账案例,加深印象。
JDBC事务介绍
1.目标
- 掌握JDBC事务相关API
2.分析
之前我们是使用MySQL的命令来操作事务。接下来我们使用JDBC来操作事务. 先来学习下相关的API
3.JDBC事务处理方法
Connection中与事务有关的方法 | 说明 |
---|---|
setAutoCommit(boolean autoCommit) | 参数是true或false 如果设置为false,表示关闭自动提交,相当于开启事务; 类似sql里面的 start transaction; |
void commit() | 提交事务; 类似sql里面的 commit; |
void rollback() | 回滚事务; 类似sql里面的 rollback; |
4.示例
代码语言:javascript复制try{
connection.setAutoCommit(false); //开启事务
...操作数据库
connection.commit(); //提交事务
}catch(Exection e){
connection.rollback(); //回滚事务
}finally{
...释放资源
}
下面我们用 转账案例 来完整演示一下 事务的操作。
案例-转账案例
- 案例的准备工作
create table account(
id int primary key auto_increment,
name varchar(20),
money double
);
insert into account values (null,'zs',1000);
insert into account values (null,'ls',1000);
insert into account values (null,'ww',1000);
执行完毕后,查询一下数据,如下:
代码语言:javascript复制mysql> select * from account;
---- ------ -------
| id | name | money |
---- ------ -------
| 1 | zs | 1000 |
| 2 | ls | 1000 |
| 3 | ww | 1000 |
---- ------ -------
3 rows in set (0.00 sec)
1.需求
zs给ls转100, 使用事务进行控制
2.分析
3.实现
3.1 演示一下在转账的过程中,如果出现网络异常,导致转账失败的情况
代码语言:javascript复制public class TransferClient {
/**
* 演示由于异常,导致转账不正确的情况
* 1. 首先进行账号A 转账 账户B 100元
* 2. 编写一个异常,模拟网络出现问题
* 3. 在下面继续些 账户B 增加 100 元的 操作
* 4. 最后发现 账号A 减去了 100,但是 账号B 并没有增加 100
*/
@Test
public void test01() throws SQLException {
// 1. 获取数据库连接
Connection connection = JdbcUtils.getConnection();
// 2. 首先进行账号A 转账 账户B 100元
// 2.1 编写账号 A 减去 100 元的 SQL
String sql = "update account set money = money-? where name = ?";
// 2.2 获取 preparedStatement,并设置参数
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1, 100);
preparedStatement.setObject(2, "zs");
// 2.3 执行 preparedStatement 的 SQL 操作
preparedStatement.executeUpdate();
// 3. 编写一个异常,模拟网络出现问题
int i = 100/0;
// 4. 账户B 增加 100 元的 操作
// 4.1 编写 账户B 增加 100元 的 SQL
String sql1 = "update account set money = money ? where name = ?";
// 4.2 获取 preparedStatement,并设置参数
PreparedStatement preparedStatement1 = connection.prepareStatement(sql1);
preparedStatement1.setObject(1, 100);
preparedStatement1.setObject(2, "ww");
// 4.3 执行 preparedStatement 的 SQL 操作
preparedStatement1.executeUpdate();
// 5. 关闭资源
preparedStatement1.close();
JdbcUtils.closeAll(preparedStatement, connection);
}
}
执行完毕之后,我们来查询一下数据库,如下:
代码语言:javascript复制-- 执行之前的数据
mysql> select * from account;
---- ------ -------
| id | name | money |
---- ------ -------
| 1 | zs | 1000 |
| 2 | ls | 1000 |
| 3 | ww | 1000 |
---- ------ -------
3 rows in set (0.00 sec)
-- 执行之后的数据
mysql> select * from account;
---- ------ -------
| id | name | money |
---- ------ -------
| 1 | zs | 900 | -- 可以看到账号A 减去了 100,但是 账户B ww 并没有增加 100
| 2 | ls | 1000 |
| 3 | ww | 1000 |
---- ------ -------
3 rows in set (0.00 sec)
mysql>
由于只执行了异常之前的代码,导致只减去了 账户A 的 100元,而账户B 没有增加,这就导致了 金额转账错误了。
考虑这种情况,这时候就要用数据库的事务,将账号A 的 SQL 执行 以及 账户 B 的 SQL 执行 作为一个 原子性操作。只有两者都执行成功,才允许数据库进行更改。
那么下面我们来使用事务来操作。
3.2 使用事务控制转账的过程
代码语言:javascript复制 /**
* 开启事务
*
*/
@Test
public void test02() {
Connection connection = null;
PreparedStatement preparedStatement = null;
PreparedStatement preparedStatement1 = null;
try {
// 1. 获取数据库连接
connection = JdbcUtils.getConnection();
// ************** 开启事务 **************
connection.setAutoCommit(false);
// 2. 首先进行账号A 转账 账户B 100元
// 2.1 编写账号 A 减去 100 元的 SQL
String sql = "update account set money = money-? where name = ?";
// 2.2 获取 preparedStatement,并设置参数
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1, 100);
preparedStatement.setObject(2, "zs");
// 2.3 执行 preparedStatement 的 SQL 操作
preparedStatement.executeUpdate();
// 3. 编写一个异常,模拟网络出现问题
// int i = 100/0;
// 4. 账户B 增加 100 元的 操作
// 4.1 编写 账户B 增加 100元 的 SQL
String sql1 = "update account set money = money ? where name = ?";
// 4.2 获取 preparedStatement,并设置参数
preparedStatement1 = connection.prepareStatement(sql1);
preparedStatement1.setObject(1, 100);
preparedStatement1.setObject(2, "ww");
// 4.3 执行 preparedStatement 的 SQL 操作
preparedStatement1.executeUpdate();
//*******提交事务*********
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
//*******回滚事务*********
try {
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
try {
// 5. 重设自动提交commit
connection.setAutoCommit(true);
// 6. 关闭资源
preparedStatement1.close();
JdbcUtils.closeAll(preparedStatement, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
4.小结
- 涉及到两个写的操作,我们一般通过手动事务去控制
- JDBC操作事务API
connection.setAutoCommit(fasle); //开启事务
connection.commit(); //提交事务
connection.rollback(); //回滚事务