本文研究的主要是hibernate悲观锁和乐观锁的全部内容,具体介绍如下。
悲观锁
悲观锁通常是由数据库机制实现的,在整个过程中把数据锁住(查询时),只要事物不释放(提交/回滚),那么任何用户都不能查看或修改。
下面我们通过一个案例来说明。
案例:假设货物库存为1000,当核算员1取出了数据准备修改,但临时有事,就走了。期间核算员2取出了数据把数量减去200,然后核算员1回来了把刚才取出的数量减去200,这就出现了一个问题,核算员1并没有在800的基础上做修改。这就是所谓的更新丢失,采用悲观锁可以解决。
inventory.java:
1
2
3
4
5
6
7
8
9
10
11
|
public class inventory { /* 存货编号 */ private string itemno; /* 存货名称 */ private string itemname; /* 存货数量 */ private int quantity; //省略setter和getter方法 } |
inventory.hbm.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?xml version= "1.0" ?> <!doctype hibernate-mapping public "-//hibernate/hibernate mapping dtd 3.0//en" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "com.lixue.bean.inventory" table= "t_inventory" > <!-- 主键手动分配 --> <id name= "itemno" > <generator class = "assigned" /> </id> <!-- 映射属性 --> <property name= "itemname" /> <property name= "quantity" /> </ class > </hibernate-mapping> |
测试类:
核算员1通过悲观锁的方式加载数据,并对数据进行修改!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public void testload1() { session session = null ; try { session = hibernateutils.getsession(); session.begintransaction(); /*在加载的时候就加上一把悲观锁,让其他用户都无法访问*/ inventory inv = (inventory) session.load(inventory.class, "1001", lockmode.upgrade); /*获取数据*/ system.out.println("opt1-->itemno=" + inv.getitemno()); system.out.println("opt1-->itemname=" + inv.getitemname()); system.out.println("opt1-->quantity=" + inv.getquantity()); /*数量减去200*/ inv.setquantity(inv.getquantity() - 200 ); session.gettransaction().commit(); } catch (exception e) { e.printstacktrace(); session.gettransaction().rollback(); } finally { hibernateutils.closesession(session); } } |
核算员2和核算员1的操作相同,都是对数据库中的数据进行修改!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public void testload2() { session session = null ; try { session = hibernateutils.getsession(); session.begintransaction(); /*在加载数据的时候就加上一把锁,让其他人无法获取数据*/ inventory inv = (inventory) session.load(inventory.class, "1001", lockmode.upgrade); /*获取真实数据*/ system.out.println("opt2-->itemno=" + inv.getitemno()); system.out.println("opt2-->itemname=" + inv.getitemname()); system.out.println("opt2-->quantity=" + inv.getquantity()); /*库存减去200*/ inv.setquantity(inv.getquantity() - 200 ); session.gettransaction().commit(); } catch (exception e) { e.printstacktrace(); session.gettransaction().rollback(); } finally { hibernateutils.closesession(session); } } |
注:两个核算员做的操作相同,如果加了悲观锁之后,核算员取出了数据并对数据进行修改,在核算员1没有提交事物之前,核算员2是不能对数据进行访问的,只能处于等待状态。知道核算员1把事物提交了之后,核算员2才有机会对数据库中的数据进行操作。
通过上面悲观锁的案例我们可以发现,悲观锁最大的好处就是可以防止更新丢失,当核算员1在处理数据的时候,核算员2只能处于等待状态,只有核算员1提交了事物之后,核算员2才有机会修改数据。但是也存在一个很大的问题,那就是,如果核算员1将数据查询出来后人就走掉了,那么其他人就得等上大半天,非常浪费时间,为了解决这个问题,我们可以使用乐观锁。
乐观锁
乐观锁并不是真正意义上的锁,大多数情况下是采用数据版本(version)的方式实现,一般在数据库中加入一个version字段,在读取数据的时候就将version读取出来,在保存数据的时候判断version的值是否小于数据库的version值,如果小于则不予更新,否则给予更新。
乐观锁下的javabean设置,inventory.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class inventory { /*存货编号*/ private string itemno; /*存货名称*/ private string itemname; /*存货数量*/ private int quantity; /*数据版本*/ private int version; //省略setter和getter方法 } |
inventory.hbm.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?xml version= "1.0" ?> <!doctype hibernate-mapping public "-//hibernate/hibernate mapping dtd 3.0//en" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> <!-- 在 class 标签中加上optimistc-lock属性,其值为版本信心 --> < class name= "com.lixue.bean.inventory" table= "t_inventory" optimistic-lock= "version" > <!-- 主键映射 --> <id name= "itemno" > <generator class = "assigned" /> </id> <!-- 数据版本,必须在主键后面的位置 --> <version name= "version" /> <!-- 基本属性映射 --> <property name= "itemname" /> <property name= "quantity" /> </ class > </hibernate-mapping> |
注:使用乐观锁的映射文件有规定即version字段的映射必须在主键id之后第一个被映射。
测试:
核算员1在乐观锁的情况下处理数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public void testload1() { session session = null ; try { session = hibernateutils.getsession(); session.begintransaction(); /*乐观锁下加载数据*/ inventory inv = (inventory)session.load(inventory.class, "1001"); /*实际获取数据*/ system.out.println("opt1-->itemno=" + inv.getitemno()); system.out.println("opt1-->itemname=" + inv.getitemname()); system.out.println("opt1-->version=" + inv.getversion()); system.out.println("opt1-->quantity=" + inv.getquantity()); /*数量减去200*/ inv.setquantity(inv.getquantity() - 200 ); session.gettransaction().commit(); } catch (exception e) { e.printstacktrace(); session.gettransaction().rollback(); } finally { hibernateutils.closesession(session); } } |
核算员2在乐观锁的情况下处理数据(核算员2可以在核算员1未提交数据的前提下处理数据)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public void testload2() { session session = null ; try { session = hibernateutils.getsession(); session.begintransaction(); /*乐观锁下加载数据*/ inventory inv = (inventory)session.load(inventory.class, "1001"); /*实际获取数据*/ system.out.println("opt2-->itemno=" + inv.getitemno()); system.out.println("opt2-->itemname=" + inv.getitemname()); system.out.println("opt2-->version=" + inv.getversion()); system.out.println("opt2-->quantity=" + inv.getquantity()); /*数量减去200*/ inv.setquantity(inv.getquantity() - 200 ); session.gettransaction().commit(); } catch (exception e) { e.printstacktrace(); session.gettransaction().rollback(); } finally { hibernateutils.closesession(session); } } |
注:在核算员取出数据将数量减去200之后并未提交的前提下,核算员2也可以操作数据,这就有别于悲观锁,当核算员2操作了数据并且提交之后,数据库中数据版本version就会加1,那么当核算员1在回来进行事物提交时就会出现错误提示即数据已更新,请重新加载。
总结
悲观锁会影响高并发,所以用乐观锁比较好。
以上就是本文关于hibernate悲观锁和乐观锁实例详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
原文链接:http://blog.csdn.net/lzm1340458776/article/details/35570739