| Summary: | Embedded objects have incorrect relational descriptor when some entities are not cached | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Product: | z_Archived | Reporter: | TimM <tim.martin> | ||||||||||
| Component: | Eclipselink | Assignee: | 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
TimM
Created attachment 196558 [details]
details of cache settings and eclipselink versions
Created attachment 196559 [details]
Example of entity classes used
Changed to critical, we can't use eclipselink while this problem is evident Setting target to 2.2.1 as this appears to be a regression in 2.2 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
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 I am working on a fix. The current target is our 2.2.1 release. I will update the bug if that changes. Created attachment 199054 [details]
proposed fix - 2.2 stream
Created attachment 199065 [details]
proposed fix - trunk
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 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 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. The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink |