博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
悲观锁
阅读量:4290 次
发布时间:2019-05-27

本文共 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的对应的数据,所以就报错了。

总结:

悲观锁 :优点是安全,缺点是并发效率低;

乐观锁 :安全性比悲观锁低,并发效率高;

如果数据需要大量修改,适用悲观锁。

如果数据时用来读取的,适用乐观锁。

你可能感兴趣的文章
JAVA学习笔记之-servlet知识点
查看>>
apache 配置不同的端口访问不同的站点
查看>>
2017年3月Java9带来的革新!
查看>>
Log4j容器深入探究
查看>>
记glide框架使用中所遇到的问题
查看>>
学习AOP之透过Spring的Ioc理解Advisor
查看>>
Jquery一个简单的注册验证
查看>>
SpringMVC基础_ControllerAdvice
查看>>
Toast还能显示图片你知道么?
查看>>
安卓三状态切换按钮TriStateToggleButton
查看>>
Spring框架-AOP细节
查看>>
java.lang.Instrument 代理Agent使用
查看>>
Javascript:指针、帽子和女朋友
查看>>
Android中的ALERTDIALOG使用_优就业
查看>>
java使用javacsv读取csv文件 导入Mysql数据库
查看>>
Toolbar 不为人知的助手,ActionMenuView
查看>>
Java回调方法详解
查看>>
如何获取url中的参数并传递给iframe中的报表
查看>>
以前端架构师的思想看问题:解决单页应用,系统角色请求抢占session发送请求问题
查看>>
jsessionid问题解决方案
查看>>