Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 347168

Summary: Embedded objects have incorrect relational descriptor when some entities are not cached
Product: z_Archived Reporter: TimM <tim.martin>
Component: EclipselinkAssignee: Nobody - feel free to take it <nobody>
Status: CLOSED FIXED QA Contact:
Severity: critical    
Priority: P2 CC: eclipselink.orm-inbox, tim.martin, tom.ware
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Windows 7   
Whiteboard:
Attachments:
Description Flags
details of cache settings and eclipselink versions
none
Example of entity classes used
none
proposed fix - 2.2 stream
none
proposed fix - trunk none

Description TimM CLA 2011-05-25 10:48:56 EDT
with eclipselink 2.2.0-RC3/ 2.2.0/ 2.3.0-RC1 the descriptor between an entity and its embedded objects are incorrect if some entities are not cached. if all entites are cached or if none are cached then there is no problem. in 2.1.3 it's ok.

I have been unable to create reproduce the problem in anything other than our full code base so cant post a recreation. 

see http://www.eclipse.org/forums/index.php/t/209146/ for previous discussions and attachment eclipselink-caching-embedding.txt for details.

apparently this may be something to do with the new protected cache support in 2.2.
Comment 1 TimM CLA 2011-05-25 10:49:42 EDT
Created attachment 196558 [details]
details of cache settings and eclipselink versions
Comment 2 TimM CLA 2011-05-25 10:54:02 EDT
Created attachment 196559 [details]
Example of entity classes used
Comment 3 TimM CLA 2011-05-26 01:30:42 EDT
Changed to critical, we can't use eclipselink while this problem is evident
Comment 4 Tom Ware CLA 2011-05-31 09:57:16 EDT
Setting target to 2.2.1 as this appears to be a regression in 2.2
Comment 5 Tom Ware CLA 2011-06-13 15:19:26 EDT
I can force a similar exception in a slightly different manner than described in the attachments.

I add a OneToOneMapping to another Entity from Country and make the new Entity, and only the new Entity not use a shared cache.

    @OneToOne
    private Foo foo = null;

  <property name="eclipselink.cache.shared.default" value="true"/>
  <property name="eclipselink.cache.shared.Foo" value="false"/>

I then put a populated country in the database, clear the cache and reread it using a read-only find.

        Map properties = new HashMap();
        properties.put(QueryHints.READ_ONLY, "true");
        country = em.find(Country.class, "myId", properties);

The exception and stacktrace mimics the one posted in the bug almost perfectly.

I have not been able to get this exception with a non-read-only query, but it is possible that some, more complex set of mappings could result here.

The key code is in AbstractDirectMapping.valueFromRow:


        if (this.descriptor.isProtectedIsolation()) {
            if (this.isCacheable && isTargetProtected && cacheKey != null) {
                Object cached = cacheKey.getObject();
                if (cached != null) {
                    if (wasCacheUsed != null){
                        wasCacheUsed[0] = Boolean.TRUE;
                    }
                    Object attributeValue = getAttributeValueFromObject(cached);
                    return buildCloneValue(attributeValue, executionSession);
                }
            }
        }

Here we're trying to use the cached data to build mappings from the embeddable into the object we will return.  The problem is that the cache key we use to build the data represents to owning object and not the embeddable.

Workarounds are listed throughout the bug, but basically relate to not using protected isolation with embeddables in certain circumstances.  For me, read-only queries are the only thing that should obviously be avoided, but it is possible there are other scenarios.

Here's the exception I get:

[EL Warning]: 2011-06-13 15:18:54.523--ClientSession(30102190)--Thread(Thread[main,5,main])--Local Exception Stack: 
Exception [EclipseLink-26] (Eclipse Persistence Services - @VERSION@.@QUALIFIER@): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Trying to get value for instance variable [auditDateTime] of type [java.sql.Timestamp] from the object [foo.Country].  The specified object is not an instance of the class or interface declaring the underlying field.
Internal Exception: java.lang.IllegalArgumentException: Can not set java.sql.Timestamp field foo.LiveFields.auditDateTime to foo.Country
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[auditDateTime-->COUNTRY.AUDITDATETIME]
Descriptor: RelationalDescriptor(foo.LiveFields --> [DatabaseTable(COUNTRY)])
	at org.eclipse.persistence.exceptions.DescriptorException.illegalArgumentWhileGettingValueThruInstanceVariableAccessor(DescriptorException.java:645)
	at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.getAttributeValueFromObject(InstanceVariableAttributeAccessor.java:79)
	at org.eclipse.persistence.mappings.DatabaseMapping.getAttributeValueFromObject(DatabaseMapping.java:523)
	at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.valueFromRow(AbstractDirectMapping.java:1276)
	at org.eclipse.persistence.mappings.DatabaseMapping.readFromRowIntoObject(DatabaseMapping.java:1325)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:344)
	at org.eclipse.persistence.mappings.AggregateObjectMapping.buildAggregateFromRow(AggregateObjectMapping.java:377)
	at org.eclipse.persistence.mappings.AggregateObjectMapping.readFromRowIntoObject(AggregateObjectMapping.java:1430)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:344)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildProtectedObject(ObjectBuilder.java:821)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:657)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:499)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:456)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:723)
	at org.eclipse.persistence.queries.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:453)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1080)
	at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:808)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1040)
	at org.eclipse.persistence.queries.ReadObjectQuery.execute(ReadObjectQuery.java:412)
	at org.eclipse.persistence.internal.sessions.AbstractSession.internalExecuteQuery(AbstractSession.java:2800)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1521)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1503)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1124)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2842)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1521)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1503)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1463)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.executeQuery(EntityManagerImpl.java:781)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.findInternal(EntityManagerImpl.java:725)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:619)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:521)
	at test.Test.main(Test.java:40)
Caused by: java.lang.IllegalArgumentException: Can not set java.sql.Timestamp field foo.LiveFields.auditDateTime to foo.Country
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
	at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:37)
	at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:18)
	at java.lang.reflect.Field.get(Field.java:358)
	at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.getAttributeValueFromObject(InstanceVariableAttributeAccessor.java:76)
	... 30 more

Exception in thread "main" Local Exception Stack: 
Exception [EclipseLink-26] (Eclipse Persistence Services - @VERSION@.@QUALIFIER@): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Trying to get value for instance variable [auditDateTime] of type [java.sql.Timestamp] from the object [foo.Country].  The specified object is not an instance of the class or interface declaring the underlying field.
Internal Exception: java.lang.IllegalArgumentException: Can not set java.sql.Timestamp field foo.LiveFields.auditDateTime to foo.Country
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[auditDateTime-->COUNTRY.AUDITDATETIME]
Descriptor: RelationalDescriptor(foo.LiveFields --> [DatabaseTable(COUNTRY)])
	at org.eclipse.persistence.exceptions.DescriptorException.illegalArgumentWhileGettingValueThruInstanceVariableAccessor(DescriptorException.java:645)
	at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.getAttributeValueFromObject(InstanceVariableAttributeAccessor.java:79)
	at org.eclipse.persistence.mappings.DatabaseMapping.getAttributeValueFromObject(DatabaseMapping.java:523)
	at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.valueFromRow(AbstractDirectMapping.java:1276)
	at org.eclipse.persistence.mappings.DatabaseMapping.readFromRowIntoObject(DatabaseMapping.java:1325)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:344)
	at org.eclipse.persistence.mappings.AggregateObjectMapping.buildAggregateFromRow(AggregateObjectMapping.java:377)
	at org.eclipse.persistence.mappings.AggregateObjectMapping.readFromRowIntoObject(AggregateObjectMapping.java:1430)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:344)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildProtectedObject(ObjectBuilder.java:821)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:657)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:499)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:456)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:723)
	at org.eclipse.persistence.queries.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:453)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1080)
	at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:808)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1040)
	at org.eclipse.persistence.queries.ReadObjectQuery.execute(ReadObjectQuery.java:412)
	at org.eclipse.persistence.internal.sessions.AbstractSession.internalExecuteQuery(AbstractSession.java:2800)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1521)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1503)
	at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1124)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2842)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1521)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1503)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1463)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.executeQuery(EntityManagerImpl.java:781)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.findInternal(EntityManagerImpl.java:725)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:619)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:521)
	at test.Test.main(Test.java:40)
Caused by: java.lang.IllegalArgumentException: Can not set java.sql.Timestamp field foo.LiveFields.auditDateTime to foo.Country
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
	at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:37)
	at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:18)
	at java.lang.reflect.Field.get(Field.java:358)
	at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.getAttributeValueFromObject(InstanceVariableAttributeAccessor.java:76)
	... 30 more
Comment 6 TimM CLA 2011-06-14 04:34:02 EDT
good stuff, although just to say that we cant really start caching the entities that are protected/isolated as it would increase the amount of locking exceptions and subsequent retrys by a fairly large amount with associated performance hit from db roundtrips - so we are looking for a fix at some point. 
I guess we could use QueryHints.REFRESH(_CASCADE) /em.refresh for all those entities when they are retrieved instead ? but that's probably not the best way forward for us...

Cheers
Comment 7 Tom Ware CLA 2011-06-14 09:15:20 EDT
I am working on a fix.  The current target is our 2.2.1 release.  I will update the bug if that changes.
Comment 8 Tom Ware CLA 2011-07-04 11:08:33 EDT
Created attachment 199054 [details]
proposed fix - 2.2 stream
Comment 9 Tom Ware CLA 2011-07-04 13:20:20 EDT
Created attachment 199065 [details]
proposed fix - trunk
Comment 10 Tom Ware CLA 2011-07-04 15:24:44 EDT
Checked in change to trunk, 2.3.1 and 2.2.1

The change builds a data-holder-cache key to hold aggregates so they can be retrieved in the valueFromRow method when using protected caching.

Reviewed by Gordon Yorke

Tested with Core and JPA LRG

Added test to CacheableModelJUnitTest
Comment 11 TimM CLA 2011-07-06 05:30:59 EDT
Cheers Tom , that appears to have fixed our problem too (nightly build 2.2.1.v20110705-r9664).
Do you know when the next milestone/release for 2.2.1/2.3.1 are due - nothing obvious under http://www.eclipse.org/projects/project_summary.php?projectid=rt.eclipselink

Tim
Comment 12 Tom Ware CLA 2011-07-06 11:52:24 EDT
We have just started our work towards 2.2.1 and 2.3.1, so there are no official milestone dates yet.  I just talked to our build engineer and we are planning on milestones for both by the end of next week.
Comment 13 Eclipse Webmaster CLA 2022-06-09 10:22:35 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink