赞
踩
原子性: 事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。一个事务内的操作要么全部成功要么全部失败.
一致性: 事务执行后,数据库状态与其它业务规则保持一致。其他特性都是为了给一致性服务的. 例如买东西,张三买李四的东西, 买卖前和买卖后张三和李四的所有钱数之和是保持不变的.
隔离性: 事务和事务之间是隔离开的. 一个事务看不到另一个事务正在操作的数据(正在进行中的状态)(两个人在两个房间考试)
持久性: 一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制将数据恢复到提交后的状态。 举例: 一般的数据操作只是在事务中记录需要进行这样的操作, 即使看到了表中的数据发生了改变, 实际上表中的数据也没有发生改变只是在事务中记录需要进行这样的操作, 真正提交了事务才去表中改变表中的数据.
开启事务—A给B转账500元
A: 1000 - 500 = 500 (成功了) 在日志中记录,事务成功,A账户金额更新为500
B: 1000 + 500 = 1500 (成功了) 在日志中记录,事务成功,B账户金额更新为1500
结束事务—回滚/提交
案例: 准备数据:
– 1、创建数据库jt_db数据库(如果不存在才创建)
create database if not exists jt_db charset utf8;
use jt_db; – 选择jt_db数据库
– 2、在 jt_db 库中创建 acc 表(银行账户表),要求有id(主键),name(姓名),money(账户金额)
drop table if exists acc;
create table acc(
id int primary key auto_increment,
name varchar(50),
money double
); – 3、往 acc 表中, 插入2条记录
insert into acc values(null,‘A’,1000); insert into acc values(null,‘B’,1000);
– 查询acc表中的所有记录
select * from acc;
start transaction;(开启事务)
rollback;(回滚事务):
quit; (中断操作-会退出mysql的cmd窗口)
1.脏读(dirty read): 一个事务读到了另一个事务未提交的事务.
注意:mysql默认的是不允许出现脏读和不可重复读,所以在下面演示之前需要设置mysql允许出现脏读、不可重复读等。
set tx_isolation=‘read-uncommitted’; – 设置mysql的事务隔离级别为最低级别(脏读,不可重复度,幻读都会出现)
比如网购东西: A是买家某刻为一个商品付款了,但是没提交事务. B卖家发生了脏读读取到了支付的金额发现账户金额收到钱了就发货(此时新增的钱数就是脏数据)
2.不可重复读(unrepeatable read): 对同一记录的两次读取结果不一致,因为在两次查询期间,有另一事务对该记录做了修改(是针对修改操作)
比如查询账户金额,第一个人第一次查询账户金额是1000, 然后第二个人对此账户的钱减了500. 第二次再次查询发现钱数是500. 解决办法不用做任何设置,多查几次就行。
**3.幻读(虚读)(phantom read):**对同一张表的两次查询结果不一致,因为在两次查询期间,有另一事务进行了插入或者是删除操作(是针对插入或删除操作);
比如事务1查询没有id为3的数据,然后插入id为3的数据但又发现id重复了(已经有id为3的记录了). 原因是事务1查询没有id为3的数据后事务2给此表中插入了id为3的记录.
(1) set tx_isolation=‘read-uncommitted’; (读未提交数据)
安全性最差,容易出现脏读、不可重复读、幻读,但性能最高
(2) set tx_isolation=‘read-committed’;(读已提交数据)(Oracle默认)
安全性一般,可防止脏读,不能防止不可重复读、幻读
(3) set tx_isolation=‘repeatable-read’; (可重复读)(MySQL默认)
安全性较好,可防止脏读、不可重复读,但不能防止幻读
(4) set tx_isolation=‘serialiable’; (串行化)(就像试衣间<一个一个用>)
安全性最好,可以防止一切事务并发问题,但是性能最差(不用)。
MySQL查询当前的事务隔离级别: select @@tx_isolation;
JDBC中通过Connection提供的方法设置事务隔离级别:
Connection.setTransactionIsolation(int level)
参数可选值如下:
Connection.TRANSACTION_READ_UNCOMMITTED 1(读未提交数据)
Connection.TRANSACTION_READ_COMMITTED 2(读已提交数据)
Connection.TRANSACTION_REPEATABLE_READ 4(可重复读)
Connection.TRANSACTION_SERIALIZABLE 8(串行化)
Connection.TRANSACTION_NONE 0(不使用事务)
JDBC中实现转账例子
提示:JDBC中默认是自动提交事务,所以需要关闭自动提交改为手动提交事务
也就是说, 关闭了自动提交后, 事务就自动开启, 但是执行完后需要手动提交或者回滚!!
(1)执行下面的程序,程序执行没有异常,转账成功!A账户减去100元,B账户增加100元。
(2)将第4步、5步中间的代码放开,再次执行程序,在转账过程中抛异常,转账失败!由于事务回滚,所以A和B账户金额不变。
public static void main(String[] args) throws SQLException { Connection conn = null; Statement stat = null; ResultSet rs = null; try {//1.获取连接 conn = JdbcUtil.getConn(); //2.关闭JDBC自动提交事务(默认开启事务) conn.setAutoCommit(false); //3.获取传输器 stat = conn.createStatement(); /* ***** A给B转账100元 ***** */ //4.A账户减去100元 String sql = "update acc set money=money-100 where name='A'"; stat.executeUpdate(sql); //int i = 1/0; // 让程序抛出异常,中断转账操作 //5.B账户加上100元 sql = "update acc set money=money+100 where name='B'"; stat.executeUpdate(sql); //6.手动提交事务 conn.commit(); System.out.println("转账成功!提交事务..."); } catch (Exception e) { e.printStackTrace(); //一旦其中一个操作出错都将回滚,使两个操作都不成功 conn.rollback();System.out.println("执行失败!回滚事务..."); } finally{ JdbcUtil.close(conn, stat, rs); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。