`

乐观锁与悲观锁

阅读更多
悲观锁
  读取数据的时候对记录加锁,直到提交数据更新后解锁。这样保证更新数据的时候,原来读取的数据和数据库一致。但加锁时间过长,以致长时间其它线程不能读取数据,造成性能急剧下降。

乐观锁
  从数据库读取数据,在写数据的时候加锁,更新数据库后解锁。在保存之前会检查之前读多的数据是不是最新的,如果不是就拒绝保存并抛异常。

Hibernate提供了3种乐观锁实现:
a. 版本控制
b. 时间戳
c. 为遗留项目添加乐观锁

a. 版本控制
描述: 读取数据的时候会记录版本号,在更新的时候检查这个版本号是否和数据库相同,不同表示在这段时间内数据库被更新过。

实现:在映射文件(如User.hbm.xml)id节点的后面,加上:
<version name="version" />

这里的type默认是integer的。

b. 时间戳
描述:和版本控制类似。有个缺点是时间戳精度的原因,可能会造成问题。

实现:和版本控制类似:
<timestamp name="timestamp" />


c. 为遗留项目添加乐观锁
描述:遗留项目,无法使用上面两种方式配置乐观锁。

实现:在<class>标签上添加optimistic-lock属性:
<class name="com.john.domain.User" optimistic-lock="all">
</class>

all表示让该记录的所有字段都为版本控制信息。

1. 乐观锁测试

<hibernate-mapping package="com.john.hibernate.domain">
	<class name="User" table="user">
		<id name="id">
			<generator class="native"/>
		</id>
		<!--<version name="version" />-->
		<!--<timestamp name="timestamp" />-->		
		...
	</class>
</hibernate-mapping>


public class User {
	private int id;
	//private Integer ver;
	//private Date timestamp;
	private String username;
	private Date birthday;
	private float money;
	// Setters and getters are omitted
}


public class VersionTest {
	public static void main(String[] args) {
		User user = new User();
		user.setUsername("john");
		user.setMoney(40000);
		AddUser(user); // the method is omitted
		updateUser();
	}

	// The try-catch and finally blocks are omitted
	static void updateUser() {
		Session s1 = HibernateUtil.getSession();
		Session s2 = HibernateUtil.getSession();
		System.out.println(s1 == s2); // false

		Transaction tx = s1.beginTransaction();
		User user1 = (User) s1.get(User.class, 1);

		Transaction tx2 = s2.beginTransaction();
		User user2 = (User) s2.get(User.class, 1);

		user1.setMoney(user1.getMoney() + 10000);
		user2.setMoney(user2.getMoney() + 20000);

		tx.commit();
		tx2.commit();
	}

完成后数据库的money值是60000,没有抛异常,tx2把tx提交的数据给覆盖了。

如果把类中的ver字段和映射文件中的<version .../>解注释,运行结果:
money值是50000,抛异常:org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

也可以解注释timestamp字段和<timestamp .../>,运行效果相同。

2. 在一个session里最多只能有一个transaction

org.hibernate.jdbc.getTransaction方法:
	public Transaction getTransaction() throws HibernateException {
		if (hibernateTransaction==null) {
			hibernateTransaction = owner.getFactory().getSettings()
					.getTransactionFactory()
					.createTransaction( this, owner );
		}
		return hibernateTransaction;
	}


	static void testTransaction() {
		Session s = HibernateUtil.getSession();
		Transaction tx1 = s.beginTransaction();
		Transaction tx2 = s.beginTransaction();
		System.out.println(tx1 == tx2); // true
	}

如果hibernateTransaction为空,使用工厂模式创建一个;否则返回已创建过的。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics