| Summary: | Persisting an object that has the same id as a previously existing object in the same inheritance hierarchy causes caching issues | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Product: | z_Archived | Reporter: | Adrian Goerler <adrian.goerler> | ||||||||
| Component: | Eclipselink | Assignee: | Nobody - feel free to take it <nobody> | ||||||||
| Status: | NEW --- | QA Contact: | |||||||||
| Severity: | normal | ||||||||||
| Priority: | P3 | CC: | eclipselink.orm-inbox, gordon.yorke, konstantin.schwed, krum.tsvetkov, sabine.heider, tom.ware | ||||||||
| Version: | unspecified | ||||||||||
| Target Milestone: | --- | ||||||||||
| Hardware: | PC | ||||||||||
| OS: | Windows 7 | ||||||||||
| Whiteboard: | test | ||||||||||
| Attachments: |
|
||||||||||
|
Description
Adrian Goerler
Created attachment 188601 [details]
proposed patch
Proposed patch. Has no effect on test execution with disabled cache. Tested on MySQL:
cache disabled: OK.
cache enabled: Fixes the issues described in this bug. No additional test failures.
WDF tests with cache enabled are not productive yet.
It would be helpful to know what operations are failing in the test. The issue here is an Entity is using the same ID as another Entity within the same class hierarchy. The other issue is that because EclipseLink has an Object cache and not just a data cache Cache.evict* does not remove the Entity instance from the cache but notifies the cache that the data must be refreshed on next access. To remove the Entity instances, and not just the data, the EclipseLink JPAHelper.getServerSession(entityManagerFactory).getIdentityMapAccessor().initializeAllIdentityMaps() API can be used between single threaded test runs. Some improvements could be made to invalidation to ensure this issue is resolved. 1 - when invalidated CacheKeys should be removed from any hard caches. 2 - existence checking should check for invalidation if it does not. 3 - based on the failing test EclipseLink's invalidation response could be updated to ensure invalidated CacheKeys did not prevent the desired behaviour. As an example, please consider the following sequence:
// create a car with id=99
em.getTransaction().begin();
Vehicle vehicle = new Car();
vehicle.setId(99);
em.persist(vehicle);
em.getTransaction().commit();
em.clear();
// remove the car again
em.getTransaction().begin();
vehicle = em.find(Vehicle.class, 99);
em.remove(vehicle);
em.getTransaction().commit();
em.clear();
// create a bicycle with same id
em.getTransaction().begin();
vehicle = new Bicycle();
vehicle.setId(id);
em.persist(vehicle);
em.getTransaction().commit(); // fails with exception below
em.clear();
vehicle = em.find(Vehicle.class, id);
This sequence fails with the following exception:
javax.persistence.RollbackException: Exception [EclipseLink-32] (Eclipse Persistence Services - @VERSION@.@QUALIFIER@): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Trying to set value [0] for instance variable [numberOfGears] of type [short] in the object. The specified object is not an instance of the class or interface declaring the underlying field, or an unwrapping conversion has failed.
Internal Exception: java.lang.IllegalArgumentException: Can not set short field org.eclipse.persistence.testing.models.wdf.jpa1.employee.Bicycle.numberOfGears to org.eclipse.persistence.testing.models.wdf.jpa1.employee.Car
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[numberOfGears-->TMP_VEHICLE.NUM_GEARS]
Descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.wdf.jpa1.employee.Bicycle --> [DatabaseTable(TMP_VEHICLE)])
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
at org.eclipse.persistence.testing.framework.wdf.AbstractBaseTest$ResourceLocalEnvironment.commitTransaction(AbstractBaseTest.java:132)
at org.eclipse.persistence.testing.framework.wdf.AbstractBaseTest$ResourceLocalEnvironment.commitTransactionAndClear(AbstractBaseTest.java:138)
at org.eclipse.persistence.testing.tests.wdf.jpa1.inheritance.ChangeSubclassTest.testFindVehicle(ChangeSubclassTest.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.eclipse.persistence.testing.framework.wdf.SkipBugzillaTestRunner.runChild(SkipBugzillaTestRunner.java:184)
at org.eclipse.persistence.testing.framework.wdf.SkipBugzillaTestRunner.runChild(SkipBugzillaTestRunner.java:38)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.persistence.testing.framework.wdf.SkipBugzillaTestRunner.run(SkipBugzillaTestRunner.java:50)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: Exception [EclipseLink-32] (Eclipse Persistence Services - @VERSION@.@QUALIFIER@): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Trying to set value [0] for instance variable [numberOfGears] of type [short] in the object. The specified object is not an instance of the class or interface declaring the underlying field, or an unwrapping conversion has failed.
Internal Exception: java.lang.IllegalArgumentException: Can not set short field org.eclipse.persistence.testing.models.wdf.jpa1.employee.Bicycle.numberOfGears to org.eclipse.persistence.testing.models.wdf.jpa1.employee.Car
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[numberOfGears-->TMP_VEHICLE.NUM_GEARS]
Descriptor: RelationalDescriptor(org.eclipse.persistence.testing.models.wdf.jpa1.employee.Bicycle --> [DatabaseTable(TMP_VEHICLE)])
at org.eclipse.persistence.exceptions.DescriptorException.illegalArgumentWhileSettingValueThruInstanceVariableAccessor(DescriptorException.java:700)
at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.setAttributeValueInObject(InstanceVariableAttributeAccessor.java:188)
at org.eclipse.persistence.mappings.DatabaseMapping.setAttributeValueInObject(DatabaseMapping.java:1428)
at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.mergeIntoObject(AbstractDirectMapping.java:1097)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeIntoObject(ObjectBuilder.java:3444)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeChangesIntoObject(ObjectBuilder.java:3381)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfWorkingCopyIntoOriginal(MergeManager.java:748)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfWorkingCopyIntoOriginal(MergeManager.java:623)
at org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:267)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeChangesIntoParent(UnitOfWorkImpl.java:3246)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.mergeChangesIntoParent(RepeatableWriteUnitOfWork.java:368)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:282)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1135)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:84)
... 29 more
Caused by: java.lang.IllegalArgumentException: Can not set short field org.eclipse.persistence.testing.models.wdf.jpa1.employee.Bicycle.numberOfGears to org.eclipse.persistence.testing.models.wdf.jpa1.employee.Car
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(Unknown Source)
at sun.reflect.UnsafeShortFieldAccessorImpl.set(Unknown Source)
at java.lang.reflect.Field.set(Unknown Source)
at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.setAttributeValueInObject(InstanceVariableAttributeAccessor.java:141)
... 41 more
The attribute numberOfGears of the Bicycle cannot be set as the Bicycle used to be a Car (the 2nd level cache used to manage an entity with the same id, which was a Car).
Created attachment 197292 [details]
Simple Repro for this bug.
Created attachment 197564 [details]
Patch avoiding the issue for the bulk of the tests and adding one particular test to reproduce the issue
This patch fixes the issue for the bulk of the tests (it avoids changing the subclass of a cached entity).
To make sure that the issue is not lost, the test ChangeSubclassTest is added.
Tested on SE/MySQL and JBoss/MySQL
This example has nothing to do with the Cache.evict() execution path and behaviour. It seems EntityManager.remove is not operating correctly and that is a far more severe issue. Increasing priority. Requires investigation for 2.3.1 - potential issue with remove. Changing target and priority. The fix for bug 336726 will address issues with remove. The issue with reassigning the same id to a new object in the same hierarchy is not yet addressed. To investigate this bug, add the attached test and fix any issues. The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink |