Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA @Version behaviour

im using JPA2 with Hibernate 3.6.x

I have made a simple testing on the @Version.

Let's say we have 2 entities,

  1. Entity Team has a List of Player Entities, bidirectional relationship, lazy fetchtype, cascade-type All
  2. Both entities have @Version

And here are the scenarios :

  1. Whenever a modification is made to one of the team/player entity, the team/player's version will be increased when flushed/commited (version on the modified record is increased).

  2. Adding a new player entity to team's collection using persist, the entity the team's version will be assigned after persist (adding a new entity, that new entity will got it's version).

  3. Whenever an addition/modification/removal is made to one of the player entity, the team's version will be increased when flushed/commited. (add/modify/remove child record, parent's version got increased also)

I can understand the number 1 and 2, but the number 3, i dont understand, why the team's version got increased ?

And that makes me think of other questions :

  1. What if i got Parent <-> child <-> granchildren relation ship. Will an addition or modification on the grandchildren increase the version of child and parent ?
  2. In scenario number 2, how can i get the version on the team before it's commited, like perhaps by using flush ? Is it a recommended way to get the parent's version after we do something to the child[s] ?

Here's a code sample from my experiment, proving that when ReceivingGoodDetail is the owning side, and the version got increased in the ReceivingGood after flushing. Sorry that this use other entities, but ReceivingGood is like the Team, ReceivingGoodDetail is like the Player. 1 ReceivingGood/Team, many ReceivingGoodDetail/Player.

/*
Hibernate: select receivingg0_.id as id9_14_, receivingg0_.creationDate as creation2_9_14_, .. too long
Hibernate: select product0_.id as id0_4_, product0_.creationDate as creation2_0_4_, .. too long
before persisting the new detail, version of header is : 14
persisting the detail 1c9f81e1-8a49-4189-83f5-4484508e71a7
printing the size of the header : 
Hibernate: select details0_.receivinggood_id as receivi13_9_8_, details0_.id as id8_, details0_.id as id10_7_, .. too long
7
after persisting the new detail, version of header is : 14
Hibernate: insert into ReceivingGoodDetail (creationDate, modificationDate, usercreate_id, usermodify_id, version, buyQuantity, buyUnit, internalQuantity, internalUnit, product_id, receivinggood_id, supplierLotNumber, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: update ReceivingGood set creationDate=?, modificationDate=?, usercreate_id=?, usermodify_id=?, version=?, purchaseorder_id=?, supplier_id=?, transactionDate=?, transactionNumber=?, transactionType=?, transactionYearMonth=?, warehouse_id=? where id=? and version=?
after flushing, version of header is now : 15
    */
public void addDetailWithoutTouchingCollection() {
    String headerId = "3b373f6a-9cd1-4c9c-9d46-240de37f6b0f";
    ReceivingGood receivingGood = em.find(ReceivingGood.class, headerId);

    // create a new detail
    ReceivingGoodDetail receivingGoodDetailCumi = new ReceivingGoodDetail();
    receivingGoodDetailCumi.setBuyUnit("Drum");
    receivingGoodDetailCumi.setBuyQuantity(1L);
    receivingGoodDetailCumi.setInternalUnit("Liter");
    receivingGoodDetailCumi.setInternalQuantity(10L);
    receivingGoodDetailCumi.setProduct(getProduct("b3e83b2c-d27b-4572-bf8d-ac32f6de5eaa"));
    receivingGoodDetailCumi.setSupplierLotNumber("Supplier Lot 1");
    decorateEntity(receivingGoodDetailCumi, getUser("3978fee3-9690-4377-84bd-9fb05928a6fc"));
    receivingGoodDetailCumi.setReceivingGood(receivingGood);

    System.out.println("before persisting the new detail, version of header is : " + receivingGood.getVersion());

    // persist it
    System.out.println("persisting the detail " + receivingGoodDetailCumi.getId());
    em.persist(receivingGoodDetailCumi);

    System.out.println("printing the size of the header : ");
    System.out.println(receivingGood.getDetails().size());

    System.out.println("after persisting the new detail, version of header is : " + receivingGood.getVersion());

    em.flush();

    System.out.println("after flushing, version of header is now : " + receivingGood.getVersion());
}
like image 885
Albert Gan Avatar asked Mar 10 '11 10:03

Albert Gan


1 Answers

It looks like a bug in Hibernate.

JPA Specification says:

All non-relationship fields and properties and all relationships owned by the entity are included in version checks

However, Hibernate also increments version after change of non-owned relationship properties (whereas, for example, EclipseLink doesn't do it). This behaviour can be disabled by setting @OptimisticLock(exclude = true) on the property.

Note that it's only applicable to changes of relationship property itself, not to changes in the state of referenced objects, so that version of parent wouldn't be changes due to changes in collection of grandchildren.

like image 161
axtavt Avatar answered Oct 13 '22 18:10

axtavt