锁是处理数据库事务并发的一种技术,当两个或更多数据库事务并发地访问相同数据时,锁可以保证同一时间只有一个事务可以修改数据。
锁的方法通常有两种:乐观锁和悲观锁。
乐观锁认为多个并发事务之间很少出现冲突,也就是说不会经常出现同一时间读取或修改相同数据,在乐观锁中,其目标是让并发事务自由地同时得到处理,而不是发现或预防冲突。两个事务在同一时刻可以访问相同的数据,但为了预防冲突,需要对数据执行一次检查,检查自上次读取数据以来发生的任何变化。
悲观锁认为事务会经常发生冲突,在悲观锁中,读取数据的事务会锁定数据,在前面的事务提交之前,其它事务都不能修改数据。
JPA 2.0 增加了 6 种新的锁模式,其中两个是乐观锁。JPA 2.0 也允许悲观锁,并增加了 3 种悲观锁,第 6 种锁模式是无锁。
下面是新增的两个乐观锁模式:
OPTIMISTIC:它和READ锁模式相同,JPA 2.0仍然支持READ锁模式,但明确指出在新应用程序中推荐使用OPTIMISTIC。
OPTIMISTIC_FORCE_INCREMENT:它和WRITE锁模式相同,JPA 2.0仍然支持WRITE锁模式,但明确指出在新应用程序中推荐使用OPTIMISTIC_FORCE_INCREMENT。
下面是新增的三个悲观锁模式:
PESSIMISTIC_READ:只要事务读实体,实体管理器就锁定实体,直到事务完成锁才会解开,当你想使用重复读语义查询数据时使用这种锁模式,换句话说就是,当你想确保数据在连续读期间不被修改,这种锁模式不会阻碍其它事务读取数据。
PESSIMISTIC_WRITE:select ...for update只要事务更新实体,实体管理器就会锁定实体,这种锁模式强制尝试修改实体数据的事务串行化,当多个并发更新事务出现更新失败几率较高时使用这种锁模式。
PESSIMISTIC_FORCE_INCREMENT:当事务读实体时,实体管理器就锁定实体,当事务结束时会增加实体的版本属性,即使实体没有修改。
你也可以指定新的锁模式 NONE,在这种情况下表示没有锁发生。
JPA 2.0 也提供了多种方法为实体指定锁模式,你可以使用 EntityManager 的 lock() 和 find() 方法指定锁模式。此外,EntityManager.refresh() 方法可以恢复实体实例的状态。
下面实例通过 EntityManager 的 lock() 方法指定 User 对象的锁级别为 LockModeType.OPTIMISTIC。代码如下:
EntityManagerFactory factory = Persistence.createEntityManagerFactory( PERSISTENCE_NAME, System.getProperties()); EntityManager em = factory.createEntityManager(); em.getTransaction().begin(); User user = em.find(User.class, 1); // 手动指定锁级别 em.lock(user, LockModeType.OPTIMISTIC); // 修改数据 user.setName("update name"); em.merge(user); em.getTransaction().commit(); factory.close(); System.out.println("finished.");