(6)事务的详解(transation)
上一篇讲解到ResultSet对象的一些操作,就有提及自动事务的关闭和开启,以及发生异常的时候的事务的回滚
对应着下面的方法:
connection.setAutoCommit(false);
connection.commit();
connection.rollback();
connection.setAutoCommit(true);
//设置回滚点
Savepoint save1 = connection.setSavepoint();
//回滚到设置的回滚点
connection.rollback(save1);
回滚点的设值案例不在详细描述,详细请参考:https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html#set_roll_back_savepoints
参考的文章:https://www.cnblogs.com/melody210218/p/7120559.html
1.数据库必须具备的四个特性
-
原子性
:事务包含的所有操作要么全部成功,要么全部失败回滚;成功必须要完全应用到数据库,失败则不能对数据库产生影响; -
一致性
:事务执行前和执行后必须处于一致性状态,
例:用户A和用户B的前加起来一共是5000; 无论AB用户之间是如何相互转换的,事务结束后两个用户的钱加起来还是5000,这就是事务的一致性。 -
隔离性
:当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不被其他事务的操作所干扰,多个并发事务之间要相互隔离; -
持久性
:一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便在数据库系统遇到故障的情况下也不会丢失事物的操作。
2.事务的隔离级别
隔离性:当多个线程都开启事务来操作数据库中的数据时,数据库系统要进行隔离操作,以保证各个线程获取数据的准确性。 不考虑事务的隔离性,会产生的几种问题:
-
脏读
:
是指一个事务处理过程里读取了另一个未提交的事务中的数据,然后使用了这个数据;
例:用户A向用户B转账100元,A通知B查看账户,B发现前确实已到账,而之后无论第二条SQL是否执行,只要该事务部提交,则所有操作都将回滚,当B再次查看账户时就会发现前其实并没有到账。 -
不可重复读
:
不可重复读是指在一个事务内,多次读取同一个数据,在这个事务还没有结束 ,另一个事务也访问该同一数据,但是由于第二个事务的修改,那么第一个事务两次读取的数据可能不一样,因此称为不可重复读;即同一个事务中原始数据读取不可重复。 注:不可重复读和脏读的区别,脏读是某一个事务读取另一个事务未提交的脏数据; 不可重复读则是读取前一事务提 -
幻读
:
当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行修改,这种数据涉及到表中的全部数据行,同时,第二个事务也对这个表数据进行修改,这个修改是对表中新增/删除一条数据,那么操作第一个事务的用户发现表中的数据还没有修改的数据行,就好像发生了幻觉一样,这就是发生了幻读。
注:幻读和不可重复读都读取另一条已经提交的事务,所不同的是不可重复读查询的都是同一数据项,而幻读针对的是一批数据整体
。
数据库提供的四种隔离级别:
-
Read uncommitted(读未提交)
:最低级别,任何情况都会发生。 -
Read Committed(读已提交)
:可避免脏读的发生。 -
Repeatable read(可重复读)
:可避免脏读、不可重复读的发生。 -
Serializable(串行化)
:避免脏读、不可重复读,幻读的发生。
注: 四种隔离级别最高:Seralizable级别,最低的是Read uncommitted级别; 级别越高,执行效率就越低; 隔离级别的设置只对当前链接有效,对JDBC操作数据库来说,一个Connection对象相当于一个链接,只对该Connection对象设置的隔离级别只对该connection对象有效,与其它链接connection对象无关。
Mysql的默认隔离级别是:可重复读:Repeatable read
;
0oracle数据库中,只支持seralizable(串行化)级别和Read committed();默认的是Read committed级别;
3.设置数据库的事务隔离级别
可以使用Connection.setTransactionIsolation方法设置事务的隔离级别,不是所有的数据库支持不同的事务隔离级别
//验证mysql的支持数据隔离级别:
//设置事务隔离级别 con.setTransactionIsolation();
System.out.println("defalut TransactionIsolation"+ con.getTransactionIsolation());
System.out.println("Supports TRANSACTION_NONE? "+ dbMetaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_NONE));
System.out.println("Supports TRANSACTION_READ_UNCOMMITTED? "+ dbMetaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED));
System.out.println("Supports TRANSACTION_READ_COMMITTED? "+ dbMetaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED));
System.out.println("Supports TRANSACTION_REPEATABLE_READ? "+ dbMetaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ));
System.out.println("Supports TRANSACTION_SERIALIZABLE? "+ dbMetaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE));
/**
*输出的结果为:
* defalut TransactionIsolation ? 4
* Supports TRANSACTION_NONE? false
* Supports TRANSACTION_READ_UNCOMMITTED? true
* Supports TRANSACTION_READ_COMMITTED? true
* Supports TRANSACTION_REPEATABLE_READ? true
* Supports TRANSACTION_SERIALIZABLE? true
*默认的是 支持可重复读
* mysql 支持下面四种数据库隔离级别
*/
4.四种隔离级别进行场景设计
-
Read uncommitted 读未提交; 公司发工资了,领导把5000元打到singo的账号上,但是该事务并未提交,而singo正好去查看账户,发现工资已经到账,是5000元整,非常高兴。可是不幸的是,领导发现发给singo的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,最后singo实际的工资只有2000元,singo空欢喜一场。(
读未提交:就相当于A读了B没有提交的事务,会发生脏读
) -
Read committed 读已提交; singo拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在singo之前提交了事务,当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,为何......(
读已提交的事务,但是会发生不可重复读的情况,就是两次读取不一样,中间被另外的事务进行了修改
) -
Repeatable read 重复读 当singo拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),singo的老婆就不可能对该记录进行修改,也就是singo的老婆不能在此时转账。(
可重复读,就是通过开启事务,在读取的过程中不能修改记录,这个相当于行级锁,不能修改读取的记录,以避免不可重复的情况
) -
重复读可能出现幻读: singo的老婆工作在银行部门,她时常通过银行内部系统查看singo的信用卡消费记录。有一天,她正在查询到singo当月信用卡的总消费金额(select sum(amount) from transaction where month = 本月)为80元,而singo此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction ... ),并提交了事务,随后singo的老婆将singo当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,singo的老婆很诧异,以为出现了幻觉,幻读就这样产生了。
(设置事务级别串性化,可以解决幻读的情况,之所以是幻读,就是修改了全部记录的后,有事务新增了记录,导致了幻读,使用串性化,就相当于表级锁,使得无法对表进行新增记录
)
总结:
读未提交:就相当于A读了B没有提交的事务,会发生脏读
读已提交的事务:解决脏读,但是会发生不可重复读的情况,就是两次读取不一样,中间被另外的事务进行了修改
可重复读:可以解决不可重复读的情况,就是通过开启事务,在读取的过程中不能修改记录,这个相当于行级锁,不能修改读取的记录,以避免不可重复读的情况
别串性化:可以解决幻读的情况,之所以是幻读,就是修改了全部记录的后,有事务新增了记录,导致了幻读,使用串性化,就相当于表级锁,使得无法对表进行新增记录