本文共 12366 字,大约阅读时间需要 41 分钟。
为了避免读写数据不及时,导致脏数据产生的问题,hibernate引入了悲观锁和乐观锁。
下面先介绍悲观锁
新建一个java项目,结构如图:
实体类User代码:
package com.robert.pojo;public class User { private int id ; private String name ; private String pwd ; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
User.hbm.xml代码:
HIbernateUtil代码
package com.robert.util;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.boot.registry.StandardServiceRegistryBuilder;import org.hibernate.cfg.Configuration;public class HibernateUtil { private static Configuration cfg = null; private static SessionFactory factory = null; private static Session session = null ; static { init(); } public static void init() { cfg = new Configuration().configure(); factory = cfg.buildSessionFactory(new StandardServiceRegistryBuilder() .applySettings(cfg.getProperties()).build()); } public static Session getSession() { if (factory != null){ session = factory.openSession(); return session ; } init(); session = factory.openSession(); return session; } public static void closeSession() { if(session!=null && session.isOpen()) session.close(); }}hibernate.cfg.xml代码:
com.mysql.jdbc.Driver jdbc:mysql:///hibernate4 root root org.hibernate.dialect.MySQL5Dialect true true update
HibernateTest测试类代码:
package com.robert.test;import org.hibernate.HibernateException;import org.hibernate.LockOptions;import org.hibernate.Session;import org.hibernate.Transaction;import org.hibernate.metamodel.domain.Hierarchical;import org.junit.Test;import com.robert.pojo.User;import com.robert.util.HibernateUtil;public class HibernateTest { /** * session的save方法,保存数据 session中状态改变:瞬时-->持久-->游离 */ @Test public void testSave() { Session session = null; Transaction tx = null; User user = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); // 构造对象--瞬时状态 user = new User(); user.setName("小明2"); user.setPwd("22221"); // 持久状态,user被session管理,并且id有值--oid session.save(user); // 在持久化状态下:hibernate会执行脏数据检查(之前的数据成为脏数据) // 当提交事务,或清理缓存时,发现session中数据和之前要放入数据库中数据(此时数据仍然在session中,并未真正在数据库中)不一致时, // 将会把session中的数据更新到数据库中。 user.setName("小友"); tx.commit(); } catch (Exception e) { e.printStackTrace(); tx.rollback(); throw new HibernateException(e.getCause()); } finally { HibernateUtil.closeSession(); } // user处于游离状态,但是在内存中仍然存在 System.out.println("name=" + user.getName()); } /** * 测试悲观锁 */ @Test public void testGetPessimistic() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); User user = (User) session.get(User.class, 1, LockOptions.UPGRADE) ; System.out.println("name=" + user.getName()); user.setName("张三") ; session.update(user) ; tx.commit(); } catch (Exception e) { e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(); } } /** * 测试悲观锁 */ @Test public void testGetPessimistic2() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); //LockOptions设置锁定策略 User user = (User) session.get(User.class, 1, LockOptions.UPGRADE) ; System.out.println("name=" + user.getName()); user.setName("张三") ; session.update(user) ; tx.commit(); } catch (Exception e) { e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(); } } }
悲观锁书写方式:
/** * 测试悲观锁 */ @Test public void testGetPessimitic() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); //LockOptions设置锁定策略 User user = (User) session.get(User.class, 1, LockOptions.UPGRADE) ; System.out.println("name=" + user.getName()); user.setName("张三") ; session.update(user) ; tx.commit(); } catch (Exception e) { e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(); } }
悲观锁一共有一下几种形式:
上面的测试代码中有两个测试悲观锁的方法,有两个session,如果同时启动,就相当于同时有两个线程启动,当第一个session执行了
User user = (User) session.get(User.class, 1, LockOptions.UPGRADE) ;这句话后,后面的线程就需要等待,直到这个session关闭之后,其他的线程才能继续执行,以此类推。
乐观锁和悲观锁不同的是,乐观锁不是排他性,而是使用了一个版本version来记录,当数据被更改后,version会更改。
下面新建一个java项目,结构如下:
实体类User代码:
package com.robert.pojo;public class User { private int id ; private String name ; private String pwd ; private int version ; public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }由代码看出,实体类User中多了一个version属性,这个属性就是用来记录版本号的
User.hbm.xml代码:
实体类对应的配置文件中增加了一个version属性
HIbernateUtil和hibernate.cfg.xml代码和上面悲观锁中的代码相同,这里就不贴了
HibernateTest测试类中的代码:
package com.robert.test;import org.hibernate.HibernateException;import org.hibernate.LockOptions;import org.hibernate.Session;import org.hibernate.Transaction;import org.hibernate.metamodel.domain.Hierarchical;import org.junit.Test;import com.robert.pojo.User;import com.robert.util.HibernateUtil;public class HibernateTest { /** * session的save方法,保存数据 session中状态改变:瞬时-->持久-->游离 */ @Test public void testSave() { Session session = null; Transaction tx = null; User user = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); // 构造对象--瞬时状态 user = new User(); user.setName("小明2"); user.setPwd("22221"); session.save(user); tx.commit(); } catch (Exception e) { e.printStackTrace(); tx.rollback(); throw new HibernateException(e.getCause()); } finally { HibernateUtil.closeSession(); } // user处于游离状态,但是在内存中仍然存在 System.out.println("name=" + user.getName()); } @Test public void testUpdate() { 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.update(user) ; tx.commit(); } catch (Exception e) { e.printStackTrace(); tx.rollback(); throw new HibernateException(e.getCause()); } finally { HibernateUtil.closeSession(); } // user处于游离状态,但是在内存中仍然存在 System.out.println("name=" + user.getName()); }}
先运行testSave()方法,保存数据,
控制台打印的sql语句如下:
Hibernate: insert into user (version, name, pwd) values (?, ?, ?)name=小明2
数据库表中的数据如图所示:
执行testUpdate()方法,控制台打印的sql语句如下:
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=?name=罗伯特
由sql语句,可以看出更新条件有两个,一个是id,另一个是version
数据库表数据,如图:
由图中可以看到version由0变为了1,
接下来再测试一个同事开启两个session,看哪个数据可以更新
测试代码:
/** * 测试乐观锁 */ @Test public void testOptimistic() { 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("珠海") ; session1.update(user1) ; tx1.commit() ; session.update(user) ; tx.commit(); } catch (Exception e) { e.printStackTrace(); } finally { HibernateUtil.closeSession(); } }
控制台打印信息:
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.robert.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.robert.test.HibernateTest.testOptimistic(HibernateTest.java:95) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) 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的对应的数据,所以就报错了。
总结:
悲观锁 :优点是安全,缺点是并发效率低;
乐观锁 :安全性比悲观锁低,并发效率高;
如果数据需要大量修改,适用悲观锁。
如果数据时用来读取的,适用乐观锁。