一、事务的概念

  事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功
  例如:A——B转帐,对应于如下两条sql语句
    update from account set money=money+100 where name='B';
    update from account set money=money-100 where name='A';

二、MySQL数据库中操作事务命令

  1、编写测试SQL脚本,如下:

 1 /*创建账户表*/2 create table account(3     id int primary key auto_increment,4     name varchar(40),5     money float6 );7 8 /*插入测试数据*/9 insert into account(name,money) values('A',1000);
10 insert into account(name,money) values('B',1000);
11 insert into account(name,money) values('C',1000);

  下面我们在MySQL数据库中模拟A——B转帐这个业务场景

2.1、开启事务(start transaction)

  使用"start transaction"开启MySQL数据库的事务,如下所示:

  

  我们首先在数据库中模拟转账失败的场景,首先执行update语句让A用户的money减少100块钱,如下图所示:

  

  然后我们关闭当前操作的dos命令行窗口,这样就导致了刚才执行的update语句的数据库的事务没有被提交,那么我们对A用户的修改就不算是是真正的修改了,下次在查询A用户的money时,依然还是之前的1000,如下图所示:

  

2.2、提交事务(commit)

  下面我们在数据库模拟A——B转账成功的场景

  

  我们手动提交(commit)数据库事务之后,A——B转账100块钱的这个业务操作算是真正成功了,A账户中少了100,B账户中多了100。

2.3、回滚事务(rollback)

  

  通过手动回滚事务,让所有的操作都失效,这样数据就会回到最初的初始状态!

三、JDBC中使用事务

  当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列的JDBC控制事务语句

  • Connection.setAutoCommit(false);//开启事务(start transaction)
  • Connection.rollback();//回滚事务(rollback)
  • Connection.commit();//提交事务(commit)

3.1、JDBC使用事务范例

  在JDBC代码中演示银行转帐案例,使如下转帐操作在同一事务中执行

  "update account set money=money-100 where name='A'"

  update account set money=money+100 where name='B'

  代码如下:

  1 package me.gacl.demo;2 3 import java.sql.Connection;4 import java.sql.PreparedStatement;5 import java.sql.ResultSet;6 import java.sql.SQLException;7 import me.gacl.utils.JdbcUtils;8 import org.junit.Test;9 10 /**11 * @ClassName: TransactionDemo112 * @Description: 13 * JDBC中使用事务来模似转帐 14     create table account(15         id int primary key auto_increment,16         name varchar(40),17         money float18     );19     insert into account(name,money) values('A',1000);20     insert into account(name,money) values('B',1000);21     insert into account(name,money) values('C',1000);22 * @author: 孤傲苍狼23 * @date: 2014-9-22 下午11:16:1724 *25 */ 26 public class TransactionDemo1 {27 28     /**29     * @Method: testTransaction130     * @Description: 模拟转账成功时的业务场景31     * @Anthor:孤傲苍狼32     *33     */ 34     @Test35     public void testTransaction1(){36         Connection conn = null;37         PreparedStatement st = null;38         ResultSet rs = null;39         40         try{41             conn = JdbcUtils.getConnection();42             conn.setAutoCommit(false);//通知数据库开启事务(start transaction)43             String sql1 = "update account set money=money-100 where name='A'";44             st = conn.prepareStatement(sql1);45             st.executeUpdate();46             String sql2 = "update account set money=money+100 where name='B'";47             st = conn.prepareStatement(sql2);48             st.executeUpdate();49             conn.commit();//上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)50             System.out.println("成功!!!");  //log4j51         }catch (Exception e) {52             e.printStackTrace();53         }finally{54             JdbcUtils.release(conn, st, rs);55         }56     }57     58     /**59     * @Method: testTransaction160     * @Description: 模拟转账过程中出现异常导致有一部分SQL执行失败后让数据库自动回滚事务61     * @Anthor:孤傲苍狼62     *63     */ 64     @Test65     public void testTransaction2(){66         Connection conn = null;67         PreparedStatement st = null;68         ResultSet rs = null;69         70         try{71             conn = JdbcUtils.getConnection();72             conn.setAutoCommit(false);//通知数据库开启事务(start transaction)73             String sql1 = "update account set money=money-100 where name='A'";74             st = conn.prepareStatement(sql1);75             st.executeUpdate();76             //用这句代码模拟执行完SQL1之后程序出现了异常而导致后面的SQL无法正常执行,事务也无法正常提交,此时数据库会自动执行回滚操作77             int x = 1/0;78             String sql2 = "update account set money=money+100 where name='B'";79             st = conn.prepareStatement(sql2);80             st.executeUpdate();81             conn.commit();//上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)82             System.out.println("成功!!!");83         }catch (Exception e) {84             e.printStackTrace();85         }finally{86             JdbcUtils.release(conn, st, rs);87         }88     }89     90     /**91     * @Method: testTransaction192     * @Description: 模拟转账过程中出现异常导致有一部分SQL执行失败时手动通知数据库回滚事务93     * @Anthor:孤傲苍狼94     *95     */ 96     @Test97     public void testTransaction3(){98         Connection conn = null;99         PreparedStatement st = null;
100         ResultSet rs = null;
101
102         try{
103             conn = JdbcUtils.getConnection();
104             conn.setAutoCommit(false);//通知数据库开启事务(start transaction)
105             String sql1 = "update account set money=money-100 where name='A'";
106             st = conn.prepareStatement(sql1);
107             st.executeUpdate();
108             //用这句代码模拟执行完SQL1之后程序出现了异常而导致后面的SQL无法正常执行,事务也无法正常提交
109             int x = 1/0;
110             String sql2 = "update account set money=money+100 where name='B'";
111             st = conn.prepareStatement(sql2);
112             st.executeUpdate();
113             conn.commit();//上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)
114             System.out.println("成功!!!");
115         }catch (Exception e) {
116             try {
117                 //捕获到异常之后手动通知数据库执行回滚事务的操作
118                 conn.rollback();
119             } catch (SQLException e1) {
120                 e1.printStackTrace();
121             }
122             e.printStackTrace();
123         }finally{
124             JdbcUtils.release(conn, st, rs);
125         }
126     }
127 }

3.2、设置事务回滚点

  在开发中,有时候可能需要手动设置事务的回滚点,在JDBC中使用如下的语句设置事务回滚点

  Savepoint sp = conn.setSavepoint();
  Conn.rollback(sp);
  Conn.commit();//回滚后必须通知数据库提交事务 设置事务回滚点范例

 1 package me.gacl.demo;2 3 import java.sql.Connection;4 import java.sql.PreparedStatement;5 import java.sql.ResultSet;6 import java.sql.SQLException;7 import java.sql.Savepoint;8 9 import me.gacl.utils.JdbcUtils;
10 import org.junit.Test;
11
12 /**
13 * @ClassName: TransactionDemo1
14 * @Description:
15 * JDBC中使用事务来模似转帐
16     create table account(
17         id int primary key auto_increment,
18         name varchar(40),
19         money float
20     );
21     insert into account(name,money) values('A',1000);
22     insert into account(name,money) values('B',1000);
23     insert into account(name,money) values('C',1000);
24 * @author: 孤傲苍狼
25 * @date: 2014-9-22 下午11:16:17
26 *
27 */
28 public class TransactionDemo2 {
29
30     /**
31     * @Method: testTransaction1
32     * @Description: 模拟转账成功时的业务场景
33     * @Anthor:孤傲苍狼
34     *
35     */
36     @Test
37     public void testTransaction1(){
38         Connection conn = null;
39         PreparedStatement st = null;
40         ResultSet rs = null;
41         Savepoint sp = null;
42
43         try{
44             conn = JdbcUtils.getConnection();
45             conn.setAutoCommit(false);//通知数据库开启事务(start transaction)
46
47             String sql1 = "update account set money=money-100 where name='A'";
48             st = conn.prepareStatement(sql1);
49             st.executeUpdate();
50
51             //设置事务回滚点
52             sp = conn.setSavepoint();
53
54             String sql2 = "update account set money=money+100 where name='B'";
55             st = conn.prepareStatement(sql2);
56             st.executeUpdate();
57
58             //程序执行到这里出现异常,后面的sql3语句执行将会中断
59             int x = 1/0;
60
61             String sql3 = "update account set money=money+100 where name='C'";
62             st = conn.prepareStatement(sql3);
63             st.executeUpdate();
64
65             conn.commit();
66
67         }catch (Exception e) {
68             try {
69                 /**
70                  * 我们在上面向数据库发送了3条update语句,
71                  * sql3语句由于程序出现异常导致无法正常执行,数据库事务而已无法正常提交,
72                  * 由于设置的事务回滚点是在sql1语句正常执行完成之后,sql2语句正常执行之前,
73                  * 那么通知数据库回滚事务时,不会回滚sql1执行的update操作
74                  * 只会回滚到sql2执行的update操作,也就是说,上面的三条update语句中,sql1这条语句的修改操作起作用了
75                  * sql2的修改操作由于事务回滚没有起作用,sql3由于程序异常没有机会执行
76                  */
77                 conn.rollback(sp);//回滚到设置的事务回滚点
78                 //回滚了要记得通知数据库提交事务
79                 conn.commit();
80             } catch (SQLException e1) {
81                 e1.printStackTrace();
82             }
83             e.printStackTrace();
84         }finally{
85             JdbcUtils.release(conn, st, rs);
86         }
87     }
88 }

四、事务的四大特性(ACID)

4.1、原子性(Atomicity)

  原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败

4.2、一致性(Consistency)

  官网上事务一致性的概念是:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。以转账为例子,A向B转账,假设转账之前这两个用户的钱加起来总共是2000,那么A向B转账之后,不管这两个账户怎么转,A用户的钱和B用户的钱加起来的总额还是2000,这个就是事务的一致性。

4.3、隔离性(Isolation)

  事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

4.4、持久性(Durability)

  持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

  事务的四大特性中最麻烦的是隔离性,下面重点介绍一下事务的隔离级别

五、事务的隔离级别

  多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。

5.1、事务不考虑隔离性可能会引发的问题  

  如果事务不考虑隔离性,可能会引发如下问题:

  1、脏读

     脏读指一个事务读取了另外一个事务未提交的数据

     这是非常危险的,假设A向B转帐100元,对应sql语句如下所示
          1.update account set money=money+100 where name='B';    
          2.update account set money=money-100  where name='A';
        当第1条sql执行完,第2条还没执行(A未提交时),如果此时B查询自己的帐户,就会发现自己多了100元钱。如果A等B走后再回滚,B就会损失100元。  

  2、不可重复读

  不可重复读指在一个事务内读取表中的某一行数据,多次读取结果不同。
  例如银行想查询A帐户余额,第一次查询A帐户为200元,此时A向帐户内存了100元并提交了,银行接着又进行了一次查询,此时A帐户为300元了。银行两次查询不一致,可能就会很困惑,不知道哪次查询是准的。  不可重复读和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据
  很多人认为这种情况就对了,无须困惑,当然是后面的为准。我们可以考虑这样一种情况,比如银行程序需要将查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行的两次查询不一致,导致文件和屏幕中的结果不一致,银行工作人员就不知道以哪个为准了。

  3、虚读(幻读)

  虚读(幻读)是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致
  如丙存款100元未提交,这时银行做报表统计account表中所有用户的总额为500元,然后丙提交了,这时银行再统计发现帐户为600元了,造成虚读同样会使银行不知所措,到底以哪个为准。

5.2、事务隔离性的设置语句

  MySQL数据库共定义了四种隔离级别:

  1. Serializable(串行化):可避免脏读、不可重复读、虚读情况的发生。
  2. Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。
  3. Read committed(读已提交):可避免脏读情况发生。
  4. Read uncommitted(读未提交):最低级别,以上情况均无法保证。

  mysql数据库查询当前事务隔离级别:select @@tx_isolation

  例如:

  

  mysql数据库默认的事务隔离级别是:Repeatable read(可重复读)

  mysql数据库设置事务隔离级别:set transaction isolation level 隔离级别名

  例如:

  

5.3、使用MySQL数据库演示不同隔离级别下的并发问题

  同时打开两个窗口模拟2个用户并发访问数据库

1、当把事务的隔离级别设置为read uncommitted时,会引发脏读、不可重复读和虚读

  A窗口
    set transaction isolation level  read uncommitted;--设置A用户的数据库隔离级别为Read uncommitted(读未提交)
    start transaction;--开启事务
    select * from account;--查询A账户中现有的钱,转到B窗口进行操作
    select * from account--发现a多了100元,这时候A读到了B未提交的数据(脏读)

  B窗口
    start transaction;--开启事务
    update account set money=money+100 where name='A';--不要提交,转到A窗口查询

2、当把事务的隔离级别设置为read committed时,会引发不可重复读和虚读,但避免了脏读

  A窗口
    set transaction isolation level  read committed;
    start transaction;
    select * from account;--发现a帐户是1000元,转到b窗口
    select * from account;--发现a帐户多了100,这时候,a读到了别的事务提交的数据,两次读取a帐户读到的是不同的结果(不可重复读)
  B窗口
    start transaction;
    update account set money=money+100 where name='aaa';
    commit;--转到a窗口

3、当把事务的隔离级别设置为repeatable read(mysql默认级别)时,会引发虚读,但避免了脏读、不可重复读

  A窗口
    set transaction isolation level repeatable read;
    start transaction;
    select * from account;--发现表有4个记录,转到b窗口
    select * from account;--可能发现表有5条记录,这时候发生了a读取到另外一个事务插入的数据(虚读)
  B窗口
    start transaction;
    insert into account(name,money) values('ggg',1000);
    commit;--转到a窗口

4、当把事务的隔离级别设置为Serializable时,会避免所有问题

  A窗口
    set transaction isolation level Serializable;
    start transaction;
    select * from account;--转到b窗口
  B窗口
    start transaction;
    insert into account(name,money) values('ggg',1000);--发现不能插入,只能等待a结束事务才能插入

http://www.cnblogs.com/xdp-gacl/p/3984001.html

javaweb学习总结(三十八)——事务相关推荐

  1. javaweb学习总结(三十八):事务

    一.事务的概念 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功. 例如:A--B转帐,对应于如下两条sql语句   update from account set mon ...

  2. javaweb学习总结(三十九)——数据库连接池

    javaweb学习总结(三十九)--数据库连接池 一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10 ...

  3. 孤傲苍狼 只为成功找方法,不为失败找借口! javaweb学习总结(三十九)——数据库连接池 一、应用程序直接获取数据库连接的缺点   用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要

    孤傲苍狼 只为成功找方法,不为失败找借口! javaweb学习总结(三十九)--数据库连接池 一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对 ...

  4. JavaScript学习(三十八)—面向过程与面向对象

    JavaScript学习(三十八)-面向过程与面向对象 一.程序设计语言中的两大编程思想:面向对象.面向过程 (一).面向过程 就是指完成某个需求的时候,先分析出完成该需求时所需要经历的步骤有哪些,然 ...

  5. 系统学习深度学习(三十八)--深度确定性策略梯度(DDPG)

    转自:https://www.cnblogs.com/pinard/p/10345762.html 1. 从随机策略到确定性策略 从DDPG这个名字看,它是由D(Deep)+D(Determinist ...

  6. 深度学习(三十八)——深度强化学习(1)教程

    教程 http://incompleteideas.net/sutton/book/the-book-2nd.html <Reinforcement Learning: An Introduct ...

  7. Python学习(三十八)—— Djago之Ajax

    转载自:http://www.cnblogs.com/yuanchenqi/articles/7638956.html 一.Ajax准备知识:json 什么是json? 定义: JSON(JavaSc ...

  8. javaweb学习总结(三十二)——JDBC学习入门

    一.JDBC相关概念介绍 1.1.数据库驱动 这里的驱动的概念和平时听到的那种驱动的概念是一样的,比如平时购买的声卡,网卡直接插到计算机上面是不能用的,必须要安装相应的驱动程序之后才能够使用声卡和网卡 ...

  9. JavaWeb学习总结(三十五)——使用JDBC处理Oracle大数据

    一.Oracle中大数据处理 在Oracle中,LOB(Large Object,大型对象)类型的字段现在用得越来越多了.因为这种类型的字段,容量大(最多能容纳4GB的数据),且一个表中可以有多个这种 ...

最新文章

  1. 全国首套中小学生人工智能教材在沪亮相
  2. php进入目录,php文件,文件夹(目录)操作函数总结
  3. break、continue和return的使用
  4. Python基础——Anaconda的安装使用
  5. 搜索引擎利用机器学习排序
  6. 循环神经网络:RNN、LSTM、GRU、BPTT
  7. Autorize插件的使用方法
  8. 动手动脑及课后实践3
  9. Android Paint 画笔使用详解 Android自定义View(六)
  10. MyCat分布式数据库集群架构工作笔记0021---高可用_单表存储千万级_海量存储_水平分表全局表
  11. Php获取分类等级,PHP获取无限分类的完整等级列表
  12. ue4蓝图运行顺序_UE4蓝图解析(四)
  13. 谷歌浏览器如何正确离线网页
  14. Blender快捷键、技巧和软件配置
  15. 博客整理——事后诸葛亮
  16. 机器学习笔记之深度信念网络(一)背景介绍与结构表示
  17. 软件项目管理 3.5.敏捷生存期模型
  18. js为什么设置为单线程,怎么实现多线程
  19. 高博RGBD SLAM
  20. GIN初探,环境安装

热门文章

  1. C语言Kruskal 算法 (MST)(附完整源码)
  2. QT的QStackedLayout类的使用
  3. QT的QReadWriteLock类的使用
  4. 经典C语言程序100例之八一
  5. linux怎么查端口是否被占用,LINUX中如何查看某个端口是否被占用
  6. php代码常见的变量缩写,PHP代码简洁之道——变量部分
  7. matlab已知散点图求方程,已知空间离散点,想求出曲面方程,怎么办?
  8. adb linux 权限更改,使用命令chmod修改android文件权限
  9. 「Python-StandardLib」第十六章:并发执行( Cocurrent Executing,线程、多线程队列、子进程)
  10. Azkaban安装部署,配置文件配置,启动等