Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[eclipselink-dev] FYI: New bug -> Backward compatibility using native API is broken in the handling of existing object when using RepeatableWriteUnitOfWork

Extract from: https://bugs.eclipse.org/bugs/show_bug.cgi?id=282745

Backward compatibility using native API is broken in the handling of
existing object when using RepeatableWriteUnitOfWork

RepeatableWriteUnitOfWork is the only option available to fix the 10
years old design bug about been unable to see modification done during
same unit of work.

So we have migrated our existing application to use
RepeatableWriteUnitOfWork, but EclipseLink is now trying to insert
object in DB that already exist in the database but not just that, also
object already existing in session cache!

JPA contract are hardcoded in RepeatableWriteUnitOfWork and no flag are
currently available to provide backward compatibility to native contract
we are used to, and often more appropriate. In other words, even if we
don't use the public JPA API, JPA logic still end-up to be applied.

We need backward compatibility when we are moving forward with new
enhancement/bug fix done to the product. 

Beside, even if we would not have existing code to support, we would
still want our production code try to do its best to handle use case.
There is a concept that need to be supported, we execute in two modes,
in development we try to detect all code not respecting conventions but
then in production we do everything we can to make the use case pass.

Right now, our actual legacy code is extremely complex, and it's too
difficult to fix all occurances of introducing old clone in a UOW.

In JPA, the problem is that old clone are not evaluated to see if they
are existing or new object, they are always assumed to be new object.
However, if the same object is not already loaded in the UOW, it's
should be a reasonable behavior to accept the old clone.

I'm not sure of the "previous" native EclipseLink API behavior when both
X and copy of X are modified and present in same UOW, what should win?
But if managed X was not modified it's reasonable that copy of X be
accepted. In our production code, our use case now failing is that we
end-up in same UOW with a X and its copy, both not modified. EclipseLink
try to insert the copy, so PK constraint violation.
 
Below is actual EclipseLink code not handling backward compatibility in
UnitOfWorkImpl (base class of RepeatableWriteUnitOfWork). Only option is
to get an exception instead of DB constraint violation when commit try
to insert an object that already exist.


protected void registerNotRegisteredNewObjectForPersist(Object
newObject, ClassDescriptor descriptor) {
        // Ensure that the registered object is not detached.
        newObject.getClass();

        // Only check existence if validating, as only results in an
earlier error.
        if (shouldValidateExistence()) {
            DoesExistQuery existQuery =
descriptor.getQueryManager().getDoesExistQuery();
            existQuery = (DoesExistQuery)existQuery.clone();
            existQuery.setObject(newObject);
            existQuery.setDescriptor(descriptor);
            existQuery.setIsExecutionClone(true);
            if (((Boolean)executeQuery(existQuery)).booleanValue()) {
                throw
ValidationException.cannotPersistExistingObject(newObject, this);
            }
        }


Code handling existing object like it's used to work is still present in
UnitOfWorkImpl. Method discoverUnregisteredNewObjects, extract:
...
 ````````if (isSmartMerge() && isOriginalNewObject(object)) {
                    return;
                } else if (!isObjectRegistered(object)) {// Don't need
to check for aggregates, as iterator does not iterate on them by
default.
                    if (shouldPerformNoValidation()) {
                        if (checkForUnregisteredExistingObject(object))
{
                            // If no validation is performed and the
object exists we need
                            // To keep a record of this object to ignore
it, also I need to
                            // Stop iterating over it.
                            unregisteredExistingObjects.put(object,
object);
                            this.setShouldBreak(true);
                            return;
                        }
                    } else {
                        // This will validate that the object is not
from the parent session, moved from calculate to optimize JPA.
                        getBackupClone(object, getCurrentDescriptor());
                    }
                    // This means it is a unregistered new object
                    knownNewObjects.put(object, object);
                }

We need a fix in 1.1.3 allowing us to enable "old" native API behavior
with existing object.




Back to the top