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

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.示例

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);

执行完毕后,查询一下数据,如下:

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.分析

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

3.实现

3.1 演示一下在转账的过程中,如果出现网络异常,导致转账失败的情况

12. JDBC事务的处理 以及 转账案例
image-20210124225021134
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(1100);
        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(1100);
        preparedStatement1.setObject(2"ww");
        // 4.3 执行 preparedStatement 的 SQL 操作
        preparedStatement1.executeUpdate();

        // 5. 关闭资源
        preparedStatement1.close();
        JdbcUtils.closeAll(preparedStatement, connection);
    }
}

执行完毕之后,我们来查询一下数据库,如下:

-- 执行之前的数据
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  使用事务控制转账的过程

12. JDBC事务的处理 以及 转账案例
image-20210124230453865
12. JDBC事务的处理 以及 转账案例
image-20210124231032534
    /**
     * 开启事务
     *
     */

    @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(1100);
            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(1100);
            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
connection.setAutoCommit(fasle);  //开启事务
connection.commit();              //提交事务
connection.rollback();            //回滚事务

原文始发于微信公众号(海洋的渔夫):12. JDBC事务的处理 以及 转账案例

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容