数据库事务隔离级别分类
数据库事务级别分为不可重复读、幻想读和脏读。
不可重复读
不可重复读的意思是事务T1读取了一条数据,然后T2修改了这条数据,然后T2提交事务,T1再次查询,查询到的数据与之前不同。即一个事务两次读取同一条数据,得到的数据不一致,其中的原因是在两次查询的间隔内另外一个事务更改了此数据导致的。例子如下:
数据库表tn_task_log中有这样一条数据
id task_id token optlock
2 89 110 19
id task_id token optlock
2 89 110 19
并且数据隔离级别设置为:Read Commited,有关数据库设置隔离级别请参考
Hibernate 事物隔离级别 深入探究 和 http://www.cnblogs.com/fjdingsd/p/5273008.html
执行以下代码:
Session session1 = commonDao.openSession();
Session session2 = commonDao.openSession();
Transaction t1 = session1.beginTransaction();
Transaction t2 = session2.beginTransaction();
TnTaskLog tnTaskLog1 = (TnTaskLog) session1.createSQLQuery("select * from tn_task_log where task_Id = 89")
.addEntity(TnTaskLog.class).list().get(0);
System.out.println("T1事务第一次查询数据:"+tnTaskLog1.getToken());
TnTaskLog tnTaskLog2 = (TnTaskLog) session2.createSQLQuery("select * from tn_task_log where task_Id = 89")
.addEntity(TnTaskLog.class).list().get(0);
tnTaskLog2.setToken(114);
session2.update(tnTaskLog2);
session2.flush();
t2.commit();
session1.refresh(tnTaskLog1);//一定要执行此代码,把session1中的缓存清除
System.out.println("T2事务更新数据并提交");
TnTaskLog tnTaskLog3 = (TnTaskLog) session1.createSQLQuery("select * from tn_task_log where task_Id = 89")
.addEntity(TnTaskLog.class).list().get(0);
System.out.println("T1事务第二次查询数据:"+tnTaskLog3.getToken());
t1.commit();
session1.close();
session2.close();
后台日志打印:
select
*
from
tn_task_log
where
task_Id = 89
Hibernate:
select
*
from
tn_task_log
where
task_Id = 89
*
from
tn_task_log
where
task_Id = 89
Hibernate:
select
*
from
tn_task_log
where
task_Id = 89
T1事务第一次查询数据:110
Hibernate:
update
tn_task_log
set
token=?,
optlock=?
where
id=?
and optlock=?
update
tn_task_log
set
token=?,
optlock=?
where
id=?
and optlock=?
T2事务更新数据并提交
select
*
from
tn_task_log
where
task_Id = 89
*
from
tn_task_log
where
task_Id = 89
T1事务第二次查询数据:114;
查询数据库,数据库的值显示114。
如果数据库事物隔离级别设置为:Repeatable-read 即可重复读,则日志显示如下:
select
*
from
tn_task_log
where
task_Id = 89
Hibernate:
select
*
from
tn_task_log
where
task_Id = 89
*
from
tn_task_log
where
task_Id = 89
Hibernate:
select
*
from
tn_task_log
where
task_Id = 89
T1事务第一次查询数据:110
Hibernate:
update
tn_task_log
set
token=?,
optlock=?
where
id=?
and optlock=?
update
tn_task_log
set
token=?,
optlock=?
where
id=?
and optlock=?
T2事务更新数据并提交
select
*
from
tn_task_log
where
task_Id = 89
*
from
tn_task_log
where
task_Id = 89
T1事务第二次查询数据:110;
查询数据库,数据库的值显示114。即当前事务查询不到其他事物对此数据的修改。
脏读
脏读是指T1更新某条数据,未提交,T2事物读取到了更新后的数据,然后T1回滚,T2读取到的数据无效。
例子如下:
设置数据库事物隔离级别为:READ-UNCOMMITTED
set tx_isolation = 'READ-UNCOMMITTED';
select @@tx_isolation;
select @@tx_isolation;
Session session1 = commonDao.openSession();
Session session2 = commonDao.openSession();
try {
Transaction t1 = session1.beginTransaction();
TnTaskLog tnTaskLog1 = (TnTaskLog) session1.createQuery("from TnTaskLog where taskId = 89")
.setCacheMode(CacheMode.REFRESH)
.list().get(0);
System.out.println("T1事物未更新前数据查询:"+tnTaskLog1.getToken());
tnTaskLog1.setToken(11);
session1.update(tnTaskLog1);
session1.flush();
System.out.println("T1事物更新数据");
session2.clear();
TnTaskLog tnTaskLog2 = (TnTaskLog)session2.createSQLQuery("select * from tn_task_log where task_Id = 89")
.addEntity(TnTaskLog.class).list().get(0);
System.out.println("T2事物查询:"+tnTaskLog2.getToken());
t1.rollback();
System.out.println("T1事物回滚!");
//System.in.read();
if(t1.wasRolledBack()){
session2.refresh(tnTaskLog2);
TnTaskLog tnTaskLog3 = (TnTaskLog) session2.createSQLQuery("select * from tn_task_log where task_Id = 89")
.addEntity(TnTaskLog.class).list().get(0);
System.out.println("T2事物再次查询:"+tnTaskLog3.getToken());
}
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
session1.close();
session2.close();
}
日志如下:
T1事物未更新前数据查询:114
update
tn_task_log
set
token=?,
optlock=?
where
id=?
and optlock=?
Hibernate:
update
tn_task_log
set
token=?,
optlock=?
where
id=?
and optlock=?
T1事物更新数据
tn_task_log
set
token=?,
optlock=?
where
id=?
and optlock=?
Hibernate:
update
tn_task_log
set
token=?,
optlock=?
where
id=?
and optlock=?
T1事物更新数据
T2事物查询:11 //出现脏读
T1事物回滚!
select
*
from
tn_task_log
where
task_Id = 89
Hibernate:
select
*
from
tn_task_log
where
task_Id = 89
*
from
tn_task_log
where
task_Id = 89
Hibernate:
select
*
from
tn_task_log
where
task_Id = 89
T2事物再次查询:114
当事物隔离级别设置为Read committed后,T2事物查询不到更新未提交的数据。即T2事物查询不会查询到11
幻读
幻读是指事物T1修改一条数据,事物T2又插入了跟T1修改之前一样的数据,T1再次安之前条件查询又查询到了相同数据,以为没有修改成功,就像幻觉一样。称为幻读或者幻想读。
例子如下
@Test
@Rollback(false)
public void testFantasy(){
Session session1 = commonDao.openSession();
Session session2 = commonDao.openSession();
try {
Transaction t1 = session1.beginTransaction();
TnTaskLog tnTaskLog1 = (TnTaskLog) session1.createQuery("from TnTaskLog where taskId = 89")
.setCacheMode(CacheMode.REFRESH)
.list().get(0);
System.out.println("T1事物未更新前数据查询:"+tnTaskLog1.getTaskId());//应该为89
tnTaskLog1.setTaskId(90L);
session1.update(tnTaskLog1);
session1.flush();
System.out.println("T1事物更新数据");
t1.commit();
session2.clear();
TnTaskLog tnTaskLog2 = new TnTaskLog();
BeanUtils.copyProperties(tnTaskLog1, tnTaskLog2);
tnTaskLog2.setTaskId(89L);
tnTaskLog2.setId(null);
session2.save(tnTaskLog2);
System.out.println("T2事物插入之前相同数据:"+tnTaskLog2.getTaskId());//应该为89
TnTaskLog tnTaskLog3 = (TnTaskLog) session1.createQuery("from TnTaskLog where taskId = 89")
.setCacheMode(CacheMode.REFRESH)
.list().get(0);
System.out.println("T1事物在次查询之前条件数据:"+tnTaskLog3.getTaskId());//应该为89
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
session1.close();
session2.close();
}
}
日志如下:
T1事物未更新前数据查询:89
update
tn_task_log
set
task_id=?,
optlock=?
where
id=?
and optlock=?
Hibernate:
update
tn_task_log
set
task_id=?,
optlock=?
where
id=?
and optlock=?
tn_task_log
set
task_id=?,
optlock=?
where
id=?
and optlock=?
Hibernate:
update
tn_task_log
set
task_id=?,
optlock=?
where
id=?
and optlock=?
T1事物更新数据
insert
into
tn_task_log
(cnt_is_finish, cnt_token, create_time, max_connect_time_id, max_trip_id, task_id, token, trip_is_finish, trip_token, update_time, optlock)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
into
tn_task_log
(cnt_is_finish, cnt_token, create_time, max_connect_time_id, max_trip_id, task_id, token, trip_is_finish, trip_token, update_time, optlock)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
T2事物插入之前相同数据:89
select
tntasklog0_.id as id1_47_,
tntasklog0_.cnt_is_finish as cnt_is_f2_47_,
tntasklog0_.cnt_token as cnt_toke3_47_,
tntasklog0_.create_time as create_t4_47_,
tntasklog0_.max_connect_time_id as max_conn5_47_,
tntasklog0_.max_trip_id as max_trip6_47_,
tntasklog0_.task_id as task_id7_47_,
tntasklog0_.token as token8_47_,
tntasklog0_.trip_is_finish as trip_is_9_47_,
tntasklog0_.trip_token as trip_to10_47_,
tntasklog0_.update_time as update_11_47_,
tntasklog0_.optlock as optlock12_47_
from
tn_task_log tntasklog0_
where
tntasklog0_.task_id=89
tntasklog0_.id as id1_47_,
tntasklog0_.cnt_is_finish as cnt_is_f2_47_,
tntasklog0_.cnt_token as cnt_toke3_47_,
tntasklog0_.create_time as create_t4_47_,
tntasklog0_.max_connect_time_id as max_conn5_47_,
tntasklog0_.max_trip_id as max_trip6_47_,
tntasklog0_.task_id as task_id7_47_,
tntasklog0_.token as token8_47_,
tntasklog0_.trip_is_finish as trip_is_9_47_,
tntasklog0_.trip_token as trip_to10_47_,
tntasklog0_.update_time as update_11_47_,
tntasklog0_.optlock as optlock12_47_
from
tn_task_log tntasklog0_
where
tntasklog0_.task_id=89
T1事物在次查询之前条件数据:89