jdbc中的事务
?
所谓事务,就是针对数据库的一组操作(多条sql)位于同一个事务的操作具备同步的特点,也就是要么都成功,要么都失败
?
在实际中,我们的很多操作都是需要由多条sql来共同完成的,例如,A账户给B账户转账就会对应两条sql
update?account?set?money=money-100?where?name=‘a’;
update?account?set?money=money+100?where?name=‘b’;
假设第一条sql成功了,而第二条sql失败了,这样就会导致a账户损失了100元,而b账户并未得到100元
?
如果将两条sql放在一个sql中,当第二条语句失败时,第一条sql语句也同样不会生效,
这样a账户就不会有任何的损失
?
?
默认情况下,我们向数据库发送的sql语句是会被自动提交的,开启事务就是相当于关闭自动提交功能,改为手动提交,我们只需要将提交事务的操作放在最后一个操作,这样一来,如果在提交事务之前出现异常,由于没有执行提交操作,事务中未提交的操作就会被回滚掉
?
account.sql
create?table?account(
id?int?primary?key?auto_increment,
name?varchar(40),
money?float
)character?set?utf8?collate?utf8_general_ci;
?
insert?into?account(name,money)?values('aaa',1000);
insert?into?account(name,money)?values('bbb',1000);
insert?into?account(name,money)?values('ccc',1000);
?
aaa?给?bbb?转账?100元
update?account?set?money=money-100?where?name='aaa';
//?异常退出
update?account?set?money=money+100?where?name='bbb';
?
//?查询结果
select?*?from?accont;
?
如果开启事务就可以避免刚才的错误发生
//?开启事务
start?transaction;
?
//?如果操作执行完毕,我们需要将事务提交
commit;
?
//?还可以手动回顾事务中所有未提交的事务
rollback;
?
命令行做实验得出结论:
如果开了一个事务,?又敲了一次?start?transaction?再次开启事务,?前一个事务会自动提交
?
在使用?jdbc?操作数据库时,需要使用?Connection?对象对事务进行管理
//?开启事务
Connection.setAutoCommit(false);//设置自动提交为false
//?回滚事务
Connection.rollback();
//提交事务
Connection.commit();
?
在?jdbc?程序中我们还可以设置回滚点,?让事务回顾到指定的回滚点,而不是自动回滚所有未提交的操作
需要将程序中的异常捕获,在catch语句块中回滚事务,在finally中提交事务
注意?,?将?Commit?操作放在?finally?中是为了保证提交未回滚的事务操作
?
事务有四大特性,一般来讲,判断一个数据库是否支持事务,就看数据库是否支持这四个特性
?
?
?
?
由于数据库是多线程并发访问的,所以很容易出现多个线程同时开启事务的情况
多线程开事务容易引起?赃读、不可重复读、幻读?的情况发生
?
赃读:dirty?read
是指一个事务读取了另一个事务未提交的数据,这是相当危险的。
设想一下,A要给B转账100元购买商品,?如果A开启了一个事务做了转账的工作
update?account?set?money=money+100?while?name=‘b’;
update?account?set?money=money?-100?while?name=‘a’;
A先不提交事务,通知B来查询
这时B来查询账户,由于会读到A开启的事务中未提交的数据,就会发现A确实给自己转了100元,
自然就会给A发货,A等B发货后就将事务回滚,不提交,此时,B就会受到损失
不可重复读:non-repeatable?read
是指事务中两次查询的结果不一致,原因是在查询的过程中其他事务做了更新的操作?update
例如,银行做报表,第一次查询A账户有100元,第二次查询A账户有200元,原因是期间A存了100元
这样就会导致一行多次统计的报表不一致
?
和脏读的区别是:
脏读是读取前一事务未提交的脏数据,不可重复读是在事务内重复读取了别的线程已提交的数据。
?
有的时候大家会认为这样的结果是正确的,没问题
我们可以考虑这样一种情况,比如银行程序需要将查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行的两次查询不一致,导致文件和屏幕中的结果不一致,银行工作人员就不知道以哪个为准了。
?
幻读:phantom?read????又名虚读
是指在一个事务内两次查询中数据笔数不一致
幻读和不可重复读有些类似,是指两次查询过程中,其他事务做了插入记录的操作,导致记录数有所增加
insert
?
例如银行做报表统计account表中所有用户的总额时,此时总共
五个账户,总金额为500元,这时有一个新的账户产生了,并且
存了100元,这时银行再统计会发现帐户总金额为600元了,造
成虚读同样会使银行遇到同样的困惑
?
实验发现不会出现虚读
来自网络的解释:
幻读只是在理论上会发生的一种情况,而现实操作中并不是一定会发生
?
为了避免多线程开事务引发的问题,我们需要将事务进行隔离
事务有四种隔离级别,不同的隔离级别可以防止不同的错误发生
serializable:可串行化,能避免脏读、不可重复读、幻读情况的发生
repeatable?read:可重读,能避免脏读、不可重复读情况的发生
read?committed:读取提交的内容,可避免脏读情况发生
read?uncommitted:读取未提交的内容最低级别,避免不了任何情况
?
操作:
设置事务隔离级别
set???transaction?isolation?level?
查询当前事务隔离级别
select?@@tx_isolation
?
查询看到的都是快照
位于事务中的多次查询,如果隔离级别设置为repeatable?read,那么多次查询读的就是一个快照
说白了就是不更新快照
?