PessimisticLockScope.NORMAL and lock "relations"

I am studying JPA documentation and come across the following lines:

Entities for which a locked object contains a key will also be locked, but not the state of the referenced objects (unless these objects are explicitly locked). Collections of elements and relationships for which the object does not contain a foreign key (for example, relationships that are mapped to join tables or one-to-many unidirectional relationships for which the target contains a foreign key) will not be blocked by default.

from here ( PessimisticLockScope.NORMAL )

I wonder how to interpret these lines. If the PessimisticLockScope parameter is set to EXTENDED , then the rows in the connection tables are also locked (but not connected by the entities themselves), so when using the NORMAL value, what will be blocked? For a confident entity string (or rows if the inheritance strategy is JOINED or TABLE_PER_CLASS or has SecondaryTable ), but that means "entity relationships":

Entities for which a locked object contains a key will also be blocked

in the context of PessimisticLockScope.NORMAL ?

+6
source share
2 answers

Entity associations are mapped to FK database associations.

PessimisticLockScope.NORMAL will release a rather aggressive database lock:

  • sublimated table rows
  • in the combined table inheritance structure, both the base table and the subclass table will be locked.
  • all @ManyToOne and @OneToOne related table rows that have an actual FK relationship (for example, the side with @JoinColumn ). But this means that you cannot change the FK information, that is, you cannot set it to null or any other value. Thus, only the value of the FK column is locked, and not the other row associated with FK.

Associations @OneToMany , @ManyToMany and those that do not own @OneToOne and @ManyToOne will not be blocked, because these associations have only an object-oriented equivalent, and locking occurs exclusively at the database level. See this article for more details.

PessimisticLockScope.EXTENDED will expand to @OneToMany and @ManyToMany . But then again, this applies only to FK column values, not to whole rows. Thus, this lock will prevent adding / removing elements to / from @OneToMany / @ManyToMany . This does not prevent the updating of the contained elements. To do this, you will have to lock each contained object.

+4
source

Here are some experiments on this. I am using Hibernate 4.3.6 as the JPA provider and MySQL 5.6 as the database.

Several test objects - TestPerson, TestUser, TestOrder

TestUser extends TestPerson (with JOINED inheritance), and TestUser has a bidirectional list of TestOrders OneToMany

 @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** 

Data Creation Code:

  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 | +------+----+ 

Now look at the side of MnayToOne, i.e. 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); 

Note the “for update” in the pessimistic lock request. This query also contains a connection table.

  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=? 

Also, when I query for the user, this time only the tables associated with the user hierarchy are locked using "to 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 

Resulting 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=? 
+2
source

Source: https://habr.com/ru/post/972393/


All Articles