JPA悲观锁模式PessimisticLockScope.NORMAL和“关联”锁定

7
我正在学习JPA文档,并遇到了以下内容:
如果锁定实体包含外键,则锁定实体关系,但不锁定引用实体的状态(除非明确锁定这些实体)。默认情况下,不会锁定元素集合和实体不包含外键的关系(例如映射到联接表或目标实体包含外键的单向一对多关系)。
这是来自此处PessimisticLockScope.NORMAL)的内容。
我想知道如何解释这些内容。如果将PessimisticLockScope设置为EXTENDED,则联接表中的行也会被锁定(但不是相关实体本身),那么使用NORMAL值会锁定什么?当然会锁定实体行(如果继承策略为JOINEDTABLE_PER_CLASS或具有SecondaryTable时会锁定多行),但是“实体关系”是什么意思?

锁定实体包含外键的实体关系也将被锁定

PessimisticLockScope.NORMAL 的上下文中?

2个回答

10

实体关系映射到数据库的 FK 关联。

PessimisticLockScope.NORMAL 会在以下情况下在数据库上发出相当激进的独占锁定:

  • 实体分离的表行
  • 在连接的表继承结构中,基表和子类表都将被锁定
  • 所有具有实际 FK 关系(例如带有@JoinColumn的一侧)的 @ManyToOne@OneToOne 关联表行。但这意味着您不能更改 FK 信息,也就是说您不能将其设置为 null 或任何其他不同的值。因此,只锁定 FK 列值而不是其他表关联 FK 行。

@OneToMany@ManyToMany 和非拥有的 @OneToOne@ManyToOne 关联不会被锁定,因为这些关联仅具有面向对象的等效项,锁定仅在数据库级别发生。

PessimisticLockScope.EXTENDED 也会扩展到 @OneToMany@ManyToMany 关联。但同样,这仅适用于 FK 列值,而不是整个行。因此,此锁定将防止添加/删除元素到 @OneToMany/@ManyToMany 关联中。它不会防止包含的元素被更新。为此,您必须锁定每个包含实体。


谢谢!这比JPA文档清晰多了。 - Maciej Dobrowolski
很好的解释@Vlad。您能否更详细地解释一下“这仅适用于FK列值而不是整个行”的含义?您是否意味着只有一个列被锁定而不是整行。这种锁定如何转换为数据库。据我所知,锁的粒度是行而不是列,或者这种关系的锁定仅在内存中。 - Shailendra
1
我指的是包含FK的行(实际上锁定整个行,而不仅仅是单个列),而不是由FK列引用的行。 - Vlad Mihalcea
我可以在什么时候放置注释?对于内置方法,您会在哪里放置注释? - Kumaresan Perumal
如果有疑问,请查看JavaDoc。PessimisticLockScope文档中有您问题的答案。 - Vlad Mihalcea
为什么规范没有解释我们如何更好地理解? - eastwater

2
以下是与此问题相关的一些实验。我使用Hibernate 4.3.6作为JPA提供程序,MySQL 5.6作为数据库。
测试实体有几个 - TestPerson,TestUser,TestOrder。
TestUser通过JOINED继承扩展了TestPerson,并且TestUser具有双向的OneToMany TestOrders列表。
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class TestPerson {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)   
    private long id;

    private String name;
    private String address;

    //getters and setters



@Entity
public class TestUser extends TestPerson {

    @OneToMany(fetch=FetchType.LAZY,mappedBy="user")   
    private List<TestOrder> orders ;

    //getters and setters


@Entity
public class TestOrder {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)   
    private long id;

    @ManyToOne
    @JoinTable(name="test_user_orders")
    private TestUser user;

    private String orderNumber ;

    //getters and setters**

数据创建代码:

         em.getTransaction().begin();            
         TestUser user = new TestUser();
         user.setName("TestUser"+System.currentTimeMillis());
         user.setAddress("TestUserAddress1");
         em.persist(user);
         List<TestOrder> orders = new ArrayList();
         for (int i=1;i<6;i++){
         TestOrder order = new TestOrder();
         order.setOrderNumber("ON"+System.currentTimeMillis());
         order.setUser(user);
         em.persist(order);
         orders.add(order);
         }
         user.setOrders(orders);

         em.getTransaction().commit();
         em.close();




mysql> select * from test_person;
+----+------------------+-----------------------+
| id | address          | name                  |
+----+------------------+-----------------------+
|  1 | TestUserAddress1 | TestUser1406031063539 |
+----+------------------+-----------------------+
1 row in set (0.00 sec)


mysql> select * from test_user;
+----+
| id |
+----+
|  1 |
+----+


mysql> select * from test_order;
+----+-----------------+
| id | order_number    |
+----+-----------------+
|  1 | ON1406031063627 |
|  2 | ON1406031063673 |
|  3 | ON1406031063678 |
|  4 | ON1406031063683 |
|  5 | ON1406031063686 |
+----+-----------------+



mysql> select * from test_user_orders;
+------+----+
| user | id |
+------+----+
|    1 |  1 |
|    1 |  2 |
|    1 |  3 |
|    1 |  4 |
|    1 |  5 |
+------+----+

现在正在查找多对一关系的一侧,即TestOrder

Map<String, Object> map = new HashMap<String, Object>();
 map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED);
 TestOrder order  = em2.find(TestOrder.class, new Long(1), LockModeType.PESSIMISTIC_WRITE, map);

请注意查询中的“for update”,这是悲观锁定。该查询还包括联接表。
 select
        testorder0_.id as id1_8_0_,
        testorder0_.order_number as order_nu2_8_0_,
        testorder0_1_.user as user1_11_0_ 
    from
        test_order testorder0_ 
    left outer join
        test_user_orders testorder0_1_ 
            on testorder0_.id=testorder0_1_.id 
    where
        testorder0_.id=? for update

Hibernate: 
    select
        testuser0_.id as id1_9_0_,
        testuser0_1_.address as address2_9_0_,
        testuser0_1_.name as name3_9_0_ 
    from
        test_user testuser0_ 
    inner join
        test_person testuser0_1_ 
            on testuser0_.id=testuser0_1_.id 
    where
        testuser0_.id=?

当我查询用户时,这次只有与用户层次结构相关的表被“for update”锁定。

 Map<String, Object> map = new HashMap<String, Object>();
        map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED);
        TestUser user  = em2.find(TestUser.class, new Long(2), LockModeType.PESSIMISTIC_WRITE,map);
        user.getOrders().size(); // to force initialization of orders

生成的 SQL 是:
        select
        testuser0_.id as id1_9_0_,
        testuser0_1_.address as address2_9_0_,
        testuser0_1_.name as name3_9_0_ 
    from
        test_user testuser0_ 
    inner join
        test_person testuser0_1_ 
            on testuser0_.id=testuser0_1_.id 
    where
        testuser0_.id=? for update

Hibernate: 
    select
        orders0_.user as user1_9_0_,
        orders0_.id as id2_11_0_,
        testorder1_.id as id1_8_1_,
        testorder1_.order_number as order_nu2_8_1_,
        testorder1_1_.user as user1_11_1_ 
    from
        test_user_orders orders0_ 
    inner join
        test_order testorder1_ 
            on orders0_.id=testorder1_.id 
    left outer join
        test_user_orders testorder1_1_ 
            on testorder1_.id=testorder1_1_.id 
    where
        orders0_.user=?

这是对EXTENDED的很好的解释,但我的问题是关于文档中PessimisticLockScope.NORMAL被锁定实体包含外键的实体关系也将被锁定语句(放置在NORMAL值下)的。 - Maciej Dobrowolski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接