12. JDBC事务的处理 以及 转账案例

2022-01-17 14:55:27 浏览数 (1)

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{
 ...释放资源
}

下面我们用 转账案例 来完整演示一下 事务的操作。

案例-转账案例

  • 案例的准备工作
代码语言:javascript复制
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.小结

  1. 涉及到两个写的操作,我们一般通过手动事务去控制
  2. JDBC操作事务API
代码语言:javascript复制
connection.setAutoCommit(fasle);  //开启事务
connection.commit();              //提交事务
connection.rollback();            //回滚事务

0 人点赞