[hibernate-issues] [Hibernate-JIRA] Created: (HHH-4927) Criteria queries on lazily loaded composite (embedded) identifiers fail with StatelessSession

Chris Wilson (JIRA) noreply at atlassian.com
Wed Feb 17 09:43:47 EST 2010


Criteria queries on lazily loaded composite (embedded) identifiers fail with StatelessSession
---------------------------------------------------------------------------------------------

                 Key: HHH-4927
                 URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-4927
             Project: Hibernate Core
          Issue Type: Bug
          Components: core
    Affects Versions: 3.3.2
            Reporter: Chris Wilson
         Attachments: HibernateStatelessSessionCriteriaUniqueKeyJoinTest.java

When performing a org.hibernate.Criteria query in a stateless session, and a PersistentClass links to another using a unique non-primary key reference, the referenced object is not fully populated, only enough to store the linked primary key fields. This makes Hibernate think that it's just loaded a transient object (as the ID is still left null), which it doesn't like at all:
 
{noformat:title=Exception stack trace}
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: org.wfp.rita.test.hibernate.HibernateStatelessSessionCriteriaUniqueKeyJoinTest$Project
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:242)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:430)
at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:110)
at org.hibernate.type.ComponentType.nullSafeSet(ComponentType.java:307)
at org.hibernate.loader.Loader.bindPositionalParameters(Loader.java:1732)
at org.hibernate.loader.Loader.bindParameterValues(Loader.java:1703)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1593)
at org.hibernate.loader.Loader.doQuery(Loader.java:696)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1885)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:71)
at org.hibernate.loader.entity.EntityLoader.loadByUniqueKey(EntityLoader.java:108)
at org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:1662)
at org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:641)
at org.hibernate.type.EntityType.resolve(EntityType.java:415)
at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:139)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:877)
at org.hibernate.loader.Loader.doQuery(Loader.java:752)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.doList(Loader.java:2232)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2129)
at org.hibernate.loader.Loader.list(Loader.java:2124)
at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:118)
at org.hibernate.impl.StatelessSessionImpl.list(StatelessSessionImpl.java:565)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:306)
at org.wfp.rita.test.hibernate.HibernateStatelessSessionCriteriaUniqueKeyJoinTest.assertList(HibernateStatelessSessionCriteriaUniqueKeyJoinTest.java:162)
at org.wfp.rita.test.hibernate.HibernateStatelessSessionCriteriaUniqueKeyJoinTest.testFailingDefaultLazyStatelessSession(HibernateStatelessSessionCriteriaUniqueKeyJoinTest.java:218)
{noformat}

This call chain:

* org.hibernate.type.EntityType.resolve(EntityType.java:415)
* org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:139)
* org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:877)

will find the object ({@link Project} or {@link Site}) in the StatefulPersistenceContext if it was eagerly loaded, because it has already been hydrated.

Otherwise, it calls EntityType.loadByUniqueKey() which ends up calling ForeignKeys.getEntityIdentifierIfNotUnsaved(), which calls SessionImplementor.getContextEntityIdentifier() to retrieve the Project or Site's ID, to bind to the query to retrieve the ProjectSite.

If the {@link Session} is stateful, this works fine, because SessionImpl.getContextEntityIdentifier() knows how to retrieve the identifier from the 
HibernateProxy even though the object is not loaded yet.

However, StatelessSessionImpl has a lame implementation of getContextEntityIdentifier():

{code}
public Serializable getContextEntityIdentifier(Object object) {
    errorIfClosed();
    return null;
}
{code}

And because the object is a lazy proxy which has not yet been initialized, its fields are all null, so ForeignKeys#isTransient returns true, and ForeignKeys.getEntityIdentifierIfNotUnsaved() throws the exception shown above.

I think the best fix is to improve the implementation of StatelessSessionImpl.getContextEntityIdentifier() to match the one in SessionImpl. We could instead improve ForeignKeys.isTransient() to check for a proxy object, but I think it makes more sense for there to be more shared code between SessionImpl and StatelessSessionImpl instead. Ideally these should inherit from a common base class or the statefulness should be extracted into a wrapper around a stateless Session instead.

The SessionWrapper class included in the attached test case is used by the included
testSuccessfulWorkaround() test to show that replacing the implementation of StatelessSessionImpl.getContextEntityIdentifier() will fix the problem. It can also be used to work around the problem without patching Hibernate, until the official fix is released. 

Change HibernateTestBase to org.hibernate.test.annotations.TestCase to run under Hibernate.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        


More information about the hibernate-issues mailing list