Search code examples
eclipselinkpersistencepayara

Missing field in update statement in eclipselink


I'm using payare 5.201 which uses eclipselink 2.7.

I'm using EJB's (Stateless beans) to handle my transactions.

There is a class A and method X which retrieves entity E from the database and calls method Y on class B and passes E as a parameter.

Method X is @ASynchronous which means it will run in a separate thread.

So method A.X runs in thread T1 and B.Y in T2.

When A.X calls B.Y then A.X doesn't have to wait because B.Y returns asynchronously.

Just before A.Z calls B.Y it sets value F to true in E. This is an indicator that B.Y is running.

A.X is passing E which seems strange. This is done to be sure that B.Y gets the correct object.

If I would e.g. pass the id of E and query it in B.Y then I could get race conditions because it is not guaranteed when E is committed in T1 so B.Y could get the old value of E.F.

When B.Y receives E then it is the same object but it is not registered to the (other) entitymanager in B. When B.Y is ready then E is be merged into the entitymanager which effectively updatea any changes which were made to E.

When B.Y is ready then E.F is set to false to indicate its done.

When B.Y exits the EJB takes over and updates the values in E into the database.

The problem I'm seeing now is that E.F is not updated. In the sql logs I see the record is update but the field F is not in the update statement. This would mean that eclipselink doesn't see any changes for that field. The funny part is that this happens not always.

After investigating eclipse I read about change tracking which can be used when weaving is enabled. This can e.g. be activated by using @ChangeTracking(ChangeTrackingType.ATTRIBUTE) on an entity.

I'm using Payara and weaving is enabled by default and I do not have any @ChangeTracking annotations in place which means eclipselink will choose these at runtime based on some rules.

When I set

@ChangeTracking(ChangeTrackingType.ATTRIBUTE)

on E entity then the problem disappears and E.F is updated.

Entity E has some relations with eager fetchtype. When I set the fetchtype of those relations to LAZY it also works again.

My conclusion is that the problem occurred because eclipse has assigned entity E a ChangeTrackingType other than ATTRIBUTE. I'm assuming this happened because of the eager fetchtype I had in place.

Questions:

  • Why is this problem only happening sometimes?
    • I would expect the ChangeTrackingType to be determined once`?
  • How can I runtime 'ask' the system which ChangeTrackingType is used for a table.
    • so I can verify the different ChangeTrackingType value
  • Are there debug settings where I can let eclipselink log the ChangeTrackingType values per table?

Solution

  • Thanks to @Chris I was able to confirm that the ObjectChangePolicy for my entity class is indeed DEFERRED.

    This value means (ChangeTracking) that the sql update for a field will only be done when the values differs from the value when loaded from the database.

    There is also an ATTRIBUTE setting.

    This will update a field when the setter is called and the original value is different.

    So this is actually what is happening and causes the issue:

    DEFERRED Process:

    When B.Y loads the E record before A.X was able to commit then in B.Y field F will still be false. If then A.X commits then F is true in the database.

    When B.Y is done then at the end it will set F to false. But when E was loaded the value was also false which means no update will be done.

    Now about the solution:

    First of all, it is possible (not likely though) that B.Y is done faster than A.X. To cover this B.Y must tell A.X not to set F to true. This can easily achieved by setting F to false on E which is passed as a parameter.

    For the second part there are two solutions that I know of:

    1)

    At the end of B.Y force an update using EntityManager.flush() and right after that refresh the data using EntityManager.refresh() and after that again set F to false. Refreshing the data makes sure F now is true and will update it after F is set to false.

    2)

    Use ATTRIBUTE and set the value first true then false. It sounds really stupid but this is a simple way to force the system to update the field.

    Clearly my preference would be option 2 as the first option requires reloading the object and updating it again so it causes 2 extra sql statements.