10 12Hibernate之锁机制

    技术2025-08-11  6

    文章目录

    1 悲观锁2 乐观锁

    既然是数据库的开发,那么一定有可能出现这样一种情况,不同的Session读取了同一个数据,并且针对于同一个数据同时发出更新操作。所以为了保证数据可以正常的同步进行操作,就需要使用锁这一概念完成,在Hibernate中提供了两种锁的处理机制: (1)悲观锁(Optimistic):指的是使用数据库中锁的概念来进行数据的锁定; (2)乐观锁(Pessimistic):利用程序的逻辑来实现锁的处理。

    1 悲观锁

    如果要想实现悲观锁主要是依靠数据库的支持完成的。悲观锁的操作就是利用了同样的处理机制完成的,它是在SQL上的处理,悲观锁的操作通过程序编码实现,可以打开查询接口来查询操作: (1)【Query接口】:Query<R> setLockMode(String alias, LockMode lockMode) (2)【Criteria接口】:public Criteria setLockMode(LockMode lockMode) 在使用此接口方法锁定数据的时候使用的别名为HQL中定义的查询别名,而对于锁定给出了几种模式: (1)读取时锁定:public static final LockMode READ (2)写入时锁定:public static final LockMode WRITE (3)不等待锁定:public static final LockMode UPGRADE_NOWAIT 范例:观察锁定的操作

    package org.lks.test; import org.hibernate.LockMode; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.lks.dbc.HibernateSessionFactory; public class TestQueryLock { public static void main(String[] args) { SessionFactory factory = HibernateSessionFactory.getSessionFactory(); Session sessionA = factory.openSession(); Query queryA = sessionA.createQuery("FROM Member AS m WHERE m.mid=?"); queryA.setLockMode("m", LockMode.UPGRADE_NOWAIT); queryA.setParameter(0, 1001); queryA.setCacheable(true); System.out.println(queryA.uniqueResult()); } } Hibernate: select member0_.mid as mid1_0_, member0_.mname as mname2_0_, member0_.mage as mage3_0_, member0_.mversion as mversion4_0_ from hedb.member member0_ where member0_.mid=? for update org.lks.pojo.Member@4aac85fa

    意味着此时,在当前Session操作的过程之中,数据将采用锁定的模式,而其它的Session将无法进行直接的更新,如果出现了不能够更新的情况也不会进行等待。

    2 乐观锁

    它认为数据库里面不应该进行同步的锁处理,所有的操作都应该通过程序完成处理,那么乐观锁采用的是一种逻辑模式的处理方式,但是如果要想使用乐观锁,就必须进行表结构的修改,也就是说除了正常可以使用的数据字段之外,在乐观锁的操作过程之中还需要准备出一个用于进行锁定逻辑判断的字段,这个字段可以是int或者是date。乐观锁的操作流程,以一个产品销售为背景: (1)现在假设有一本书的剩余库存为100本; (2)现在由“SessionA”读取这个剩余的库存信息,这个时候返回的内容是100,同时返回的版本编号为1; (3)同时“SessionB”也读取了这个剩余的库存信息,这个时候返回的内容是100,同时返回的版本编号为1; (4)如果此时“SessionA”卖出了20本书,则应该将数据库中的100修改为80,但是同时还需要修改版本号为2; (5)这个时候“SessionB”也要针对于数据修改,卖出了50本书,但是由于它的版本号是1,数据库中的版本号是2,那么将无法进行修改。 范例:数据库脚本

    -- 删除数据表 DROP TABLE IF EXISTS member; -- 创建数据表 CREATE TABLE member( mid INT, mname varchar(50), mage INT, mversion INT, CONSTRAINT pk_mid PRIMARY KEY(mid) ); INSERT INTO member(mid,mname,mage,mversion) VALUES(1001,'lks',23,1);

    随后如果要想实现乐观锁的配置,需要在Member.hbm.xml文件之中完成,但是mversion是一个由Hibernate自己负责维护的标记,所以不应该出现在属性的配置上。 范例:观察Member.hbm.xml文件的修改

    <version name="mversion" column="mversion" type="integer"></version>

    下面将产生两个Session对象对数据进行同时的更新操作。 范例:编写程序进行测试

    package org.lks.test; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.lks.dbc.HibernateSessionFactory; import org.lks.pojo.Member; public class TestQueryLock { public static void main(String[] args) { SessionFactory factory = HibernateSessionFactory.getSessionFactory(); Session sessionA = factory.openSession(); Session sessionB = factory.openSession(); Query queryA = sessionA.createQuery("FROM Member AS m WHERE m.mid=?"); Query queryB = sessionB.createQuery("FROM Member AS m WHERE m.mid=?"); queryA.setParameter(0, 1001); queryB.setParameter(0, 1001); Member mea = (Member)queryA.uniqueResult(); Member meb = (Member)queryB.uniqueResult(); mea.setMage(40); sessionA.beginTransaction().commit(); meb.setMage(60); sessionB.beginTransaction().commit(); } } Hibernate: select → queryA.uniqueResult(); member0_.mid as mid1_0_, member0_.mversion as mversion2_0_, member0_.mname as mname3_0_, member0_.mage as mage4_0_ from hedb.member member0_ where member0_.mid=? Hibernate: select →queryB.uniqueResult(); member0_.mid as mid1_0_, member0_.mversion as mversion2_0_, member0_.mname as mname3_0_, member0_.mage as mage4_0_ from hedb.member member0_ where member0_.mid=? Hibernate: update →mea.setMage(40); hedb.member set mversion=?, mname=?, mage=? where mid=? and mversion=? Hibernate: update →meb.setMage(60); hedb.member set mversion=?, mname=?, mage=? where mid=? and mversion=? 七月 04, 2020 4:40:45 下午 org.hibernate.internal.SessionImpl$5 mapManagedFlushFailure ERROR: HHH000346: Error during managed flush [Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1] Exception in thread "main" org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

    此时在同一个线程之中,两个Session修改了同一条操作数据。

    下面更换为日期时间进行版本号的操作设置。 范例:修改数据库的脚本

    -- 删除数据表 DROP TABLE IF EXISTS member; -- 创建数据表 CREATE TABLE member( mid INT, mname varchar(50), mage INT, mversion DATATIME, CONSTRAINT pk_mid PRIMARY KEY(mid) ); INSERT INTO member(mid,mname,mage,mversion) VALUES(1001,'lks',23,'2020-04-04 10:10:10');

    范例:配置乐观锁

    @Version @Column(name="mversion") public Integer getMversion() { return this.mversion; }

    乐观锁的逻辑处理部分太麻烦了,而且如果只是要锁定数据,不如使用悲观锁。

    Processed: 0.010, SQL: 9