JDBC(四)事务管理

1.银行转账案例

案例:银行转账:从张无忌账户上给赵敏转1000块钱.
准备:account(账户表):

id            name(账号,唯一)           balance(余额)
1             张无忌                    20000
2             赵敏                      0

转账操作步骤:
①:查询张无忌的账户余额是否大于等于1000.

SELECT * FROM account WHERE  name = '王健林' AND balance >= 100;

余额小于1000 : 温馨提示:亲,你的余额不足.
余额大于等于1000: GOTO 2;
②:从王健林的账户余额中减少100.

UPDATE account SET balance = balance - 1000 WHERE name = '王健林';

③:在马云的账户余额中增加100.

UPDATE account SET balance = balance + 1000 WHERE name = '马云';
public class TransactionTest extends TestCase {

    // 案例:银行转账:从王健林账户上转100个亿给马云
    public void testTranscation() throws Exception {

        // 1.查询王健林账户的余额是否大约100亿
        String sql = "SELECT * FROM t_account WHERE name =? AND balance >= ?";
        Connection conn = JdbcUtil.getConn();
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, "王健林");
        ps.setInt(2, 2000);
        ResultSet rs = ps.executeQuery();
        int wBalance = 0;
        if (rs.next()) {
            wBalance = rs.getInt("balance");
            System.out.println(rs.getString("name") + "还有" + wBalance + "的余额");
        } else {
            throw new RuntimeException("余额不足");
        }

        // 2.从王健林的账户中余额减少100
        sql = "UPDATE t_account SET balance= ? WHERE name= ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, wBalance - 100);
        ps.setString(2, "王健林");
        ps.executeUpdate();

        // 3.查询马云的余额
        int mBalance = 0;
        sql = "SELECT * FROM t_account WHERE name =?";
        ps = conn.prepareStatement(sql);
        ps.setString(1, "马云");
        rs = ps.executeQuery();
        if (rs.next()) {
            mBalance = rs.getInt("balance");
            System.out.println(rs.getString("name") + "还有" + mBalance + "的余额");
        } else {
            throw new RuntimeException("没有马云账户");
        }

        // 4.给马云的账户余额中增加100
        sql = "UPDATE t_account SET balance= ? WHERE name= ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, mBalance + 100);
        ps.setString(2, "马云");
        ps.executeUpdate();

        // 关闭资源
        JdbcUtil.close(conn, ps, rs);
    }

}

悲剧的事情来了:当程序执行到第②步和第③步之间,突然停电了.
使用异常来模拟停电:System.out.println(1/0);


2.JDBC的事务操作

事务(Transaction,简写为tx):

  • 在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。

  • 为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:
    (1)当每个逻辑操作单元全部完成时,数据的一致性可以保持;
    (2)而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。

  • 事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(commit),这些修改就永久地保存下来,如果回退(rollback),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。

  • 事务:指构成单个逻辑工作单元的操作集合。

  • 事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最初状态。

简单讲:事务其实就是多个操作,把多个操作看成是一个不可分割的整体,整体中的多个要成功都成功,要失败都失败。
在红楼梦中: 一损俱损,就是这个思想.


事务的ACID属性:

1. 原子性(Atomicity):原子在化学中,是最小单位,不可以再分割了。
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
2. 一致性(Consistency):包装数据的完整性。
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)
3. 隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
4. 持久性(Durability):
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。


事务的操作:

事务成功: 提交事务(commit),如果事务不提交,在数据库中数据永远都不会改变;
事务失败: 出现异常的时候,事务失败.事务回滚:rollback(取消之前所有的操作,回到事务最初的状态),释放锁资源。


操作事务的模板:

try{
    取消事务的自动提交机制,设置为手动提交. connection对象.setAutoCommit(false);
    操作1
    操作2
    异常
    操作3
    ....
   手动提交事务                          connection对象.commit();
}catch(Exception e){
   //处理异常
   回滚事务                              connection对象.rollback();
}

上面案例的事务处理

public void testTranscation() {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConn();
            conn.setAutoCommit(false);// 取消事务的自动提交机制
            String sql = "SELECT * FROM t_account WHERE name =? AND balance >= ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, "王健林");
            ps.setInt(2, 2000);
            rs = ps.executeQuery();
            int wBalance = 0;
            if (rs.next()) {
                wBalance = rs.getInt("balance");
                System.out.println(rs.getString("name") + "还有" + wBalance + "的余额");
            } else {
                throw new RuntimeException("余额不足");
            }
            // 2.从王健林的账户中余额减少100
            sql = "UPDATE t_account SET balance= ? WHERE name= ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, wBalance - 100);
            ps.setString(2, "王健林");
            ps.executeUpdate();

            // 3.查询马云的余额
            int mBalance = 0;
            sql = "SELECT * FROM t_account WHERE name =?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, "马云");
            rs = ps.executeQuery();
            if (rs.next()) {
                mBalance = rs.getInt("balance");
                System.out.println(rs.getString("name") + "还有" + mBalance + "的余额");
            } else {
                throw new RuntimeException("没有马云账户");
            }
            System.out.println(1 / 0); // 模拟停电
            // 4.给马云的账户余额中增加100
            sql = "UPDATE t_account SET balance= ? WHERE name= ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, mBalance + 100);
            ps.setString(2, "马云");
            ps.executeUpdate();
            conn.commit();//提交事务
            System.out.println("转账成功");
        } catch (Exception e) {
            try {
                conn.rollback();   //事务回滚
                System.out.println("转账失败");
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        } finally {
            // 关闭资源
            JdbcUtil.close(conn, ps, rs);
        }

    }

事务相关的:

  • 1.默认情况下,事务在执行完DML操作就自动提交.
  • 2.查询操作,其实是不需要事务的.但是,一般的,我们在开发中都把查询放入事务中.
  • 3.开发中,代码完全正确,没有异常,但是就是数据库中数据不变.
    意识:没有提交事务.
  • 4.在MySQL中,只有InnoDB存储引擎支持事务,支持外键,MyISAM不支持事务.
  • 5.以后事务我们不应该在DAO层处理,应该在service层控制.
  • 6.事务在讲解Hibernate,MyBatis,Spring,项目的时候都会再讲.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,695评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,569评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,130评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,648评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,655评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,268评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,835评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,740评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,286评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,375评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,505评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,873评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,357评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,466评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,921评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,515评论 2 359

推荐阅读更多精彩内容