2

The entity I'm trying to save is a parent and child. When I save the entity (i.e. the parent and children saved at the same time), however with normal execution (in debug mode every time) I get a HibernateOptimisticLockingFailureException thrown during session flushing. The testing is on my local machine, single thread, and nobody is changing the entity as I'm also saving it.

We are using the following:

  1. MySQL v5.5.x
  2. Hibernate 4.3.11
  3. Java 8
  4. Spring 4.1.0

Key points:

  1. The relationship between the parent and child is bi-directional one-to-many.
  2. We use optimistic locking with the version column being a timestamp created by MySQL either during insert or during update. On the version field we specify @Generated(GenerationTime.ALWAYS) to ensure that the version details are obtained from the database automatically (avoid the time precision issue between Java and MySQL)
  3. During saving a new entity (id = 0), I can see the logs that the entity is being inserted into the database, I can also see the child entities being inserted in the database (via the Hibernate logs). During this process, I can also see the a select is done to get the version details from the database.
  4. Soon after the entities are inserted and the session is being flushed, there is a dirty checking is done on the collection and I see a message in the log that the collection is unreferenced. Straight after this, I see an update statement on the parent entity's table and this is where the problem occurs as the version value used in the update statement is different to what is in the database, the HibernateOptimisticLockingFailureException exception is thrown.

Hibernate Code

getHibernateTemplate().saveOrUpdate(parentEntity);

// a break point here and wait for 1 sec before executing
// always get the HibernateOptimisticLockingFailureException
getHibernateTemplate().flush();

Parent mapping

@Access(AccessType.FIELD)
@OneToMany(mappedBy="servicePoint", fetch=FetchType.EAGER, cascade={CascadeType.ALL}, orphanRemoval=true, targetEntity=ServicingMeter.class)
private List<ServicingMeter> meters = new ArrayList<ServicingMeter>();

Child mapping

@Access(AccessType.FIELD)
@ManyToOne(fetch=FetchType.EAGER, targetEntity=ServicePoint.class)
@JoinColumn(name="service_point_id", nullable=false)
private ServicePoint servicePoint;

Questions:
1. Why is there an update date on the parent table?
2. How can I avoid this update from happening?
3. Is there something wrong with the way my one-to-many mapping is setup?

The annotated log file can be found here

Otty
  • 51
  • 6
  • Did you say that **MySQL** is managing the @Version column as a timestamp? – Klaus Groenbaek Mar 08 '18 at 10:16
  • yes, MySQL is managing the @Version column – Otty Mar 08 '18 at 11:55
  • Are you sure that is a supported use-case? normally @Version is managed exclusively by the JPA framework, you are not even allowed to use it in code. – Klaus Groenbaek Mar 08 '18 at 12:02
  • Yes, there is a mention of this particular situations in the section [5.1.2. Timestamp] (https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch05.html). – Otty Mar 08 '18 at 13:18
  • Have you tried without letting the DB manage the timestamp. If the DB generates the timestamp, JPA will have to select the column again after each insert/update (as it does with auto increment ids). This could affect batch inserts and other optimization paths. If it was me I would just use a Long for the Version, and a separate field lastModified that the DB could manage. – Klaus Groenbaek Mar 08 '18 at 13:41
  • Thank you for your suggestion @KlausGroenbaek, it has resolved the **HibernateOptimisticLockingFailureException** being raised. One problem did face during this change (moving away from DB Hibernate assign version) is the re-use of the entity after transaction rollback (see [this question here](https://stackoverflow.com/questions/35603770/hibernate-reuse-of-entities-after-a-rollback-using-versioning). The resolution of that problem was simple as do not re-use the object but instead get the latest version of the entity back from the database before using it again. – Otty Mar 10 '18 at 22:35

0 Answers0