Hibernate的悲觀鎖、樂觀鎖

多個用戶同時訪問一個數據庫,則當他們的事務同時使用相

同的數據時可能會發生問題。由於併發操作帶來的數據不一致性包括:丟失數據

修改、讀

數據(髒讀)、不可重複讀、產生幽靈數據

多個用戶同時訪問一個數據庫,當他們的事務同時使用相同的數據時可能會發生問題。由於併發操作帶來的數據不一致性包括丟失數據、修改、髒讀、不可重複讀、產生幽靈數據。爲了解決併發問題hibernate提出了悲觀鎖和樂觀鎖來解決問題。

悲觀鎖

新建一個java項目,項目結構如下


User類

public class User {
	private int id;
	private String name;
	private String pwd;
	//省略get/set
}

User.hbm.xml配置

<hibernate-mapping package="com.test.pojo">
  	  
    <class name="User" table="user">
        <id name="id">  
            <generator class="identity" />  
        </id>  
        <property name="name" />  
        <property name="pwd" />  
    </class>  
</hibernate-mapping> 
HIbernateTest

public class HibernateTest {
	@Test
	public void testCreateDB(){
		Configuration cfg=new Configuration().configure();
		SchemaExport se=new SchemaExport(cfg);
		se.create(true, true);
	}
	@Test
	public void testSave(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		User user=new User();
		user.setName("張三");
		user.setPwd("1111");
		session.save(user);
		tx.commit();
		HibernateUtil.closeSession();
	}
	@Test
	public void testGet(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		User user=(User) session.get(User.class, 1, LockOptions.UPGRADE);
		System.out.println(user.getName());
		user.setName("李四");
		session.update(user);
		tx.commit();
		HibernateUtil.closeSession();
	}
	@Test
	public void testGet1(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		User user=(User) session.get(User.class, 1,LockOptions.UPGRADE);
		System.out.println(user.getName());
		user.setName("李四");
		session.update(user);
		tx.commit();
		HibernateUtil.closeSession();
	}
}
上面代碼中testGet和testGet1是兩個測試悲觀鎖的方法,有兩個session,如果同時啓動,就相當於同時有兩個線程啓動,當第一個session執行了

User user=(User) session.get(User.class, 1, LockOptions.UPGRADE);
這句代碼,後面的線程需要等待,直到這個session關閉之後,其他線程才能繼續執行,以此類推。

樂觀鎖

樂觀鎖和悲觀鎖不同的是,樂觀鎖不是排他性,而是使用了一個版本version來記錄,當數據被更改後,version會更改。

新建java項目,項目結構如下:



User代碼

public class User {
	private int id;
	private String name;
	private String pwd;
	private int version;
	//省略get/set
}
User.hbm.xml配置

<hibernate-mapping package="com.test.pojo">
  	  
    <class name="User" table="user">
        <id name="id">  
            <generator class="identity" />  
        </id>
        <version name="version"/>  
        <property name="name" />  
        <property name="pwd" />  
    </class>  
</hibernate-mapping> 

HibernateTest測試代碼

public class HibernateTest {
	@Test
	public void testCreateDB(){
		Configuration cfg=new Configuration().configure();
		SchemaExport se=new SchemaExport(cfg);
		se.create(true, true);
	}
	@Test
	public void testSave(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		User user=new User();
		user.setName("張三");
		user.setPwd("1111");
		session.save(user);
		tx.commit();
		HibernateUtil.closeSession();
	}
	@Test
	public void testGet(){
		Session session = null;  
	    Transaction tx = null;  
	    User user = null;  
	    try {  
	        session = HibernateUtil.getSession();  
	        tx = session.beginTransaction();  
	          
	        user = (User) session.get(User.class, 1) ;  
	        user.setName("李四") ;  
	          
	        Session session1 = HibernateUtil.getSession() ;  
	        Transaction tx1 = session1.beginTransaction() ;  
	          
	        User user1 = (User) session.get(User.class, 1) ;  
	        user1.setName("李四2") ;  
	        session1.update(user1) ;  
	        tx1.commit() ;  
	          
	        session.update(user) ;  
	        tx.commit();  
	    } catch (Exception e) {  
	        e.printStackTrace();  
	    } finally {  
	        HibernateUtil.closeSession();  
	    }  
	}
	@Test
	public void testGet1(){
		Session session=HibernateUtil.getSession();
		Transaction tx=session.beginTransaction();
		User user=(User) session.get(User.class, 1);
		System.out.println(user.getName());
		user.setName("李四");
		session.update(user);
		tx.commit();
		HibernateUtil.closeSession();
	}
}



執行testSave方法,控制檯打印信息

Hibernate: insert into user (version, name, pwd) values (?, ?, ?)
數據庫表信息


執行testGet1,控制檯打印信息

Hibernate: select user0_.id as id1_0_0_, user0_.version as version2_0_0_, user0_.name as name3_0_0_, 
user0_.pwd as pwd4_0_0_ from user user0_ where user0_.id=?
張三
Hibernate: update user set version=?, name=?, pwd=? where id=? and version=?

數據庫表信息


由圖可以看出id和version都發生了改變。

再來測試testGet,控制檯打印信息

Hibernate: select user0_.id as id1_0_0_, user0_.version as version2_0_0_, user0_.name as name3_0_0_, 
user0_.pwd as pwd4_0_0_ from user user0_ where user0_.id=?
Hibernate: update user set version=?, name=?, pwd=? where id=? and version=?
Hibernate: update user set version=?, name=?, pwd=? where id=? and version=?
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction 
(or unsaved-value mapping was incorrect) : [com.test.pojo.User#1]
	at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2541)
	at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3285)
	at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3183)
	at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3525)
	at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:159)
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
	at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
	at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
	at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258)
	at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
	at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
	at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
	at com.test.test.HibernateTest.testGet(HibernateTest.java:52)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

數據庫表信息如下


由數據庫中數據得知是user1數據更新到了數據庫中,user的數據沒有更新到數據庫,報錯了,這是因爲user和user1都從數據庫中查詢出了version是1的數據,但是user1先更新了數據,然後提交到數據庫中,此時該數據庫中該數據的version變爲了2,等到user也更新數據時,where條件中的version沒有找到version是1的對應的數據,所以就報錯了。

總結:

1、悲觀鎖:安全,併發效率低

2、樂觀鎖:安全性比悲觀鎖低,併發效率高

如果數據需要大量修改,適用悲觀鎖。

如果數據是用來讀取的,適用樂觀鎖。







發佈了97 篇原創文章 · 獲贊 154 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章