取款事务A
存款事务B
T1
开始事务
T2
开始事务
T3
查询账户余额为1000元
T4
查询账户余额为1000元
T5
汇入100元把余额改成1100元
T6
提交事务
T7
取出100元把余额改成900元
T8
撤销事务
T9
余额恢复为1000元(丢失更新)
b) dirty read脏读(读了另一个事务没有提交的数据)
时间
取款事务A
存款事务B
T1
开始事务
T2
开始事务
T3
查询账户余额为1000元
T4
汇入100元把余额改成1100元
T5
查询账户余额为1100元(读取脏数据)
T6
回滚
T7
取款1100元
T8
事务提交失败
c) non-repeatable-read不可重复读(同一个事务中,读取同一个数据,得到的值不同)
时间
取款事务A
转账事务B
T1
事务开始
T2
事务开始
T3
查询账户余额为1000元
T4
汇入100元把余额改成1100元
T5
提交事务
T6
查询账户余额为1100元
T7
提交事务
d)second lost update problems第二类丢失更新(不可重复读的特殊情况)
时间
转账事务A
取款事务B
T1
开始事务
T2
开始事务
T3
查询账户余额为1000元
T4
查询账户余额为1000元
T5
取出100元把余额改成900元
T6
提交事务
T7
汇入100元
T8
提交事务
T9
把余额改成1100元(丢失更新)
e) phantom read 幻读(插入、删除问题导致取得的结果不同)
时间
查询学生事务A
插入学生事务B
T1
开始事务
T2
开始事务
T3
查询学生为10人
T4
插入一个新学生
T5
查询学生为11人
T6
提交事务
T7
提交事务
3、数据库的事务隔离机制
1、read-uncommited
2、read-commited
3、repeatable-read
4、serializable
a) 只要数据库支持事务,就不会出现read-uncommited情况
b) read-uncommited会出现dirty-read,phantom read,non-repeatable-read问题
c) sead-commited不会出现dirty-read,但仍会出现non-repeatable-read,phantom read
d) repeatable-read
e) serializable解决一切问题
4、设定hibernate的事务隔离级别1、为了考虑并发效率,设置Hibernte.connection.isolate=2(即使用的是read-commited)
2、用悲观锁解决repeatable read问题(依赖于数据库的锁)
A)相当于select ...for update
悲观锁:
实验:
(1)建立Account.java类
package com.zgy.hibernate.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Account {
private int id;
private int balance; //BigDecimal
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
}
(2)HibernateUtil.java,用于获取SessionFactory
package com.zgy.hibernate.model;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {
static SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
Configuration configure = new Configuration().configure();
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configure.getProperties()).build();
return sessionFactory = configure.buildSessionFactory(serviceRegistry);
}
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
}
(3)测试类
package com.zgy.hibernate.model;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class HibernateLockTest {
private static SessionFactory sf;
@BeforeClass
public static void beforeClass() {
sf = HibernateUtil.getSessionFactory();
}
@AfterClass
public static void afterClass() {
sf.close();
}
@Test
public void testSchemaExport() {
new SchemaExport(new Configuration().configure()).create(false, true);
}
@Test
public void testSave() {
Session session = sf.openSession();
session.beginTransaction();
Account a = new Account();
a.setBalance(100);
session.save(a);
session.getTransaction().commit();
session.close();
}
@Test
public void testOperation1() {
Session session = sf.openSession();
session.beginTransaction();
Account a = (Account)session.load(Account.class, 1);
int balance = a.getBalance();
//do some caculations,此时如果别的事务在这个过程中对balance进行操作,将会导致banlance取值被此session覆盖
balance = balance - 10;
a.setBalance(balance);
session.getTransaction().commit();
session.close();
}
@Test
public void testPessimisticlock() {
Session session = sf.openSession();
session.beginTransaction();
Account a = (Account)session.load(Account.class, 1,LockMode.UPGRADE);
int balance = a.getBalance();
//do some caculations
balance = balance - 10;
a.setBalance(balance);
session.getTransaction().commit();
session.close();
}
public static void main(String[] args) {
beforeClass();
}
}
(4)测试testPessimisticlock()方法,观察产生的SQL语句如下:
Hibernate:
select
account0_.id as id1_0_0_,
account0_.balance as balance2_0_0_
from
Account account0_
where
account0_.id=? for update
Hibernate:
update
Account
set
balance=?
where
id=?
乐观锁:
实验:
(1)修改Account.java
package com.zgy.hibernate.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class Account {
private int id;
private int balance;
private int version;
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
}
(2)测试:
package com.zgy.hibernate.model;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class HibernateLockTest {
private static SessionFactory sf;
@BeforeClass
public static void beforeClass() {
sf = HibernateUtil.getSessionFactory();
}
@AfterClass
public static void afterClass() {
sf.close();
}
@Test
public void testSchemaExport() {
new SchemaExport(new Configuration().configure()).create(false, true);
}
@Test
public void testSave() {
Session session = sf.openSession();
session.beginTransaction();
Account a = new Account();
a.setBalance(100);
session.save(a);
session.getTransaction().commit();
session.close();
}
@Test
public void testOptimisticLock() {
Session session = sf.openSession();
Session session2 = sf.openSession();
session.beginTransaction();
Account a1 = (Account) session.load(Account.class, 1);
session2.beginTransaction();
Account a2 = (Account) session2.load(Account.class, 1);
a1.setBalance(900);
a2.setBalance(1100);
session.getTransaction().commit();
System.out.println(a1.getVersion());
session2.getTransaction().commit();
System.out.println(a2.getVersion());
session.close();
session2.close();
}
public static void main(String[] args) {
beforeClass();
}
}
(3)观察结果:
程序出错,因为session2中的数据被修改或者删除,所以导致session2无法正常关闭。