added class to override Hibernate EntityType class to fix problem with merge replace
authorCherian Mathew <c.mathew@bgbm.org>
Wed, 4 Mar 2015 12:14:05 +0000 (12:14 +0000)
committerCherian Mathew <c.mathew@bgbm.org>
Wed, 4 Mar 2015 12:14:05 +0000 (12:14 +0000)
.gitattributes
cdmlib-persistence/src/main/java/org/hibernate/type/EntityType.java [new file with mode: 0644]

index f45b5d596b2569369462a4c199b49c25a286ebfd..98f9b0527201e0ac4e52cd01389c71d7ca7d2117 100644 (file)
@@ -1537,6 +1537,7 @@ cdmlib-persistence/src/main/java/org/hibernate/dialect/HSQLCorrectedDialect.java
 cdmlib-persistence/src/main/java/org/hibernate/dialect/MySQL5InnoDBUtf8Dialect.java -text
 cdmlib-persistence/src/main/java/org/hibernate/dialect/MySQL5MyISAMUtf8Dialect.java -text
 cdmlib-persistence/src/main/java/org/hibernate/envers/synchronization/work/CollectionChangeWorkUnit.java -text
+cdmlib-persistence/src/main/java/org/hibernate/type/EntityType.java -text
 cdmlib-persistence/src/main/resources/META-INF/services/org.hibernate.integrator.spi.Integrator -text
 cdmlib-persistence/src/main/resources/c3p0-config.xml -text
 cdmlib-persistence/src/main/resources/eu/etaxonomy/cdm/cdm.datasources.xml -text
diff --git a/cdmlib-persistence/src/main/java/org/hibernate/type/EntityType.java b/cdmlib-persistence/src/main/java/org/hibernate/type/EntityType.java
new file mode 100644 (file)
index 0000000..cc0e5e5
--- /dev/null
@@ -0,0 +1,741 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.hibernate.AssertionFailure;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.internal.ForeignKeys;
+import org.hibernate.engine.spi.EntityUniqueKey;
+import org.hibernate.engine.spi.Mapping;
+import org.hibernate.engine.spi.PersistenceContext;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.engine.spi.SessionImplementor;
+import org.hibernate.internal.util.ReflectHelper;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.entity.UniqueKeyLoadable;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.tuple.ElementWrapper;
+
+/**
+ * Base for types which map associations to persistent entities.
+ *
+ * Note : This class overrides the Hibernate EntityType class to provide a
+ * workaround for problem in method 'replace'. Need to check if
+ * this problem is fixed in later versions of hibernate or open a bug report
+ * with a patch.
+ *
+ * @author Gavin King
+ */
+public abstract class EntityType extends AbstractType implements AssociationType {
+
+       private final TypeFactory.TypeScope scope;
+       private final String associatedEntityName;
+       protected final String uniqueKeyPropertyName;
+       protected final boolean isEmbeddedInXML;
+       private final boolean eager;
+       private final boolean unwrapProxy;
+
+       private transient Class returnedClass;
+
+       /**
+        * Constructs the requested entity type mapping.
+        *
+        * @param scope The type scope
+        * @param entityName The name of the associated entity.
+        * @param uniqueKeyPropertyName The property-ref name, or null if we
+        * reference the PK of the associated entity.
+        * @param eager Is eager fetching enabled.
+        * @param isEmbeddedInXML Should values of this mapping be embedded in XML modes?
+        * @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping
+        * says to return the "implementation target" of lazy prooxies; typically only possible
+        * with lazy="no-proxy".
+        *
+        * @deprecated Use {@link #EntityType(TypeFactory.TypeScope, String, String, boolean, boolean )} instead.
+        * See Jira issue: <a href="https://hibernate.onjira.com/browse/HHH-7771">HHH-7771</a>
+        */
+       @Deprecated
+       protected EntityType(
+                       TypeFactory.TypeScope scope,
+                       String entityName,
+                       String uniqueKeyPropertyName,
+                       boolean eager,
+                       boolean isEmbeddedInXML,
+                       boolean unwrapProxy) {
+               this.scope = scope;
+               this.associatedEntityName = entityName;
+               this.uniqueKeyPropertyName = uniqueKeyPropertyName;
+               this.isEmbeddedInXML = isEmbeddedInXML;
+               this.eager = eager;
+               this.unwrapProxy = unwrapProxy;
+       }
+
+       /**
+        * Constructs the requested entity type mapping.
+        *
+        * @param scope The type scope
+        * @param entityName The name of the associated entity.
+        * @param uniqueKeyPropertyName The property-ref name, or null if we
+        * reference the PK of the associated entity.
+        * @param eager Is eager fetching enabled.
+        * @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping
+        * says to return the "implementation target" of lazy prooxies; typically only possible
+        * with lazy="no-proxy".
+        */
+       protected EntityType(
+                       TypeFactory.TypeScope scope,
+                       String entityName,
+                       String uniqueKeyPropertyName,
+                       boolean eager,
+                       boolean unwrapProxy) {
+               this.scope = scope;
+               this.associatedEntityName = entityName;
+               this.uniqueKeyPropertyName = uniqueKeyPropertyName;
+               this.isEmbeddedInXML = true;
+               this.eager = eager;
+               this.unwrapProxy = unwrapProxy;
+       }
+
+       protected TypeFactory.TypeScope scope() {
+               return scope;
+       }
+
+       /**
+        * An entity type is a type of association type
+        *
+        * @return True.
+        */
+       @Override
+    public boolean isAssociationType() {
+               return true;
+       }
+
+       /**
+        * Explicitly, an entity type is an entity type ;)
+        *
+        * @return True.
+        */
+       @Override
+    public final boolean isEntityType() {
+               return true;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public boolean isMutable() {
+               return false;
+       }
+
+       /**
+        * Generates a string representation of this type.
+        *
+        * @return string rep
+        */
+       @Override
+    public String toString() {
+               return getClass().getName() + '(' + getAssociatedEntityName() + ')';
+       }
+
+       /**
+        * For entity types, the name correlates to the associated entity name.
+        */
+       @Override
+    public String getName() {
+               return associatedEntityName;
+       }
+
+       /**
+        * Does this association foreign key reference the primary key of the other table?
+        * Otherwise, it references a property-ref.
+        *
+        * @return True if this association reference the PK of the associated entity.
+        */
+       public boolean isReferenceToPrimaryKey() {
+               return uniqueKeyPropertyName==null;
+       }
+
+       @Override
+    public String getRHSUniqueKeyPropertyName() {
+               return uniqueKeyPropertyName;
+       }
+
+       @Override
+    public String getLHSPropertyName() {
+               return null;
+       }
+
+       public String getPropertyName() {
+               return null;
+       }
+
+       /**
+        * The name of the associated entity.
+        *
+        * @return The associated entity name.
+        */
+       public final String getAssociatedEntityName() {
+               return associatedEntityName;
+       }
+
+       /**
+        * The name of the associated entity.
+        *
+        * @param factory The session factory, for resolution.
+        * @return The associated entity name.
+        */
+       @Override
+    public String getAssociatedEntityName(SessionFactoryImplementor factory) {
+               return getAssociatedEntityName();
+       }
+
+       /**
+        * Retrieves the {@link Joinable} defining the associated entity.
+        *
+        * @param factory The session factory.
+        * @return The associated joinable
+        * @throws MappingException Generally indicates an invalid entity name.
+        */
+       @Override
+    public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) throws MappingException {
+               return ( Joinable ) factory.getEntityPersister( associatedEntityName );
+       }
+
+       /**
+        * This returns the wrong class for an entity with a proxy, or for a named
+        * entity.  Theoretically it should return the proxy class, but it doesn't.
+        * <p/>
+        * The problem here is that we do not necessarily have a ref to the associated
+        * entity persister (nor to the session factory, to look it up) which is really
+        * needed to "do the right thing" here...
+        *
+        * @return The entiyt class.
+        */
+       @Override
+    public final Class getReturnedClass() {
+               if ( returnedClass == null ) {
+                       returnedClass = determineAssociatedEntityClass();
+               }
+               return returnedClass;
+       }
+
+       private Class determineAssociatedEntityClass() {
+               try {
+                       return ReflectHelper.classForName( getAssociatedEntityName() );
+               }
+               catch ( ClassNotFoundException cnfe ) {
+                       return java.util.Map.class;
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
+       throws HibernateException, SQLException {
+               return nullSafeGet( rs, new String[] {name}, session, owner );
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public final Object nullSafeGet(
+                       ResultSet rs,
+                       String[] names,
+                       SessionImplementor session,
+                       Object owner) throws HibernateException, SQLException {
+               return resolve( hydrate(rs, names, session, owner), session, owner );
+       }
+
+       /**
+        * Two entities are considered the same when their instances are the same.
+        *
+        *
+        * @param x One entity instance
+        * @param y Another entity instance
+        * @return True if x == y; false otherwise.
+        */
+       @Override
+    public final boolean isSame(Object x, Object y) {
+               return x == y;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public int compare(Object x, Object y) {
+               return 0; //TODO: entities CAN be compared, by PK, fix this! -> only if/when we can extract the id values....
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public Object deepCopy(Object value, SessionFactoryImplementor factory) {
+               return value; //special case ... this is the leaf of the containment graph, even though not immutable
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public Object replace(
+                       Object original,
+                       Object target,
+                       SessionImplementor session,
+                       Object owner,
+                       Map copyCache) throws HibernateException {
+               if ( original == null ) {
+                       return null;
+               }
+               Object cached = copyCache.get(original);
+               if ( cached != null ) {
+                       return cached;
+               }
+               else {
+                       if ( original == target ) {
+                               return target;
+                       }
+                       if ( session.getContextEntityIdentifier( original ) == null  &&
+                                       ForeignKeys.isTransient( associatedEntityName, original, Boolean.FALSE, session ) ) {
+                           // final Object copy = session.getFactory().getEntityPersister( associatedEntityName )
+                //  .instantiate( null, session );
+                           // changed above call to below, because the associatedEntityName could be an abstract class
+                           // in which case it cannot be instantiated
+
+                               final Object copy = session.getFactory().getEntityPersister( original.getClass().getName() )
+                                               .instantiate( null, session );
+                               //TODO: should this be Session.instantiate(Persister, ...)?
+                               copyCache.put( original, copy );
+                               return copy;
+                       }
+                       else {
+                               Object id = getIdentifier( original, session );
+                               if ( id == null ) {
+                                       throw new AssertionFailure("non-transient entity has a null id");
+                               }
+                               id = getIdentifierOrUniqueKeyType( session.getFactory() )
+                                               .replace(id, null, session, owner, copyCache);
+                               return resolve( id, session, owner );
+                       }
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public int getHashCode(Object x, SessionFactoryImplementor factory) {
+               EntityPersister persister = factory.getEntityPersister(associatedEntityName);
+               if ( !persister.canExtractIdOutOfEntity() ) {
+                       return super.getHashCode( x );
+               }
+
+               final Serializable id;
+               if (x instanceof HibernateProxy) {
+                       id = ( (HibernateProxy) x ).getHibernateLazyInitializer().getIdentifier();
+               }
+               else {
+                       final Class mappedClass = persister.getMappedClass();
+                       if ( mappedClass.isAssignableFrom( x.getClass() ) ) {
+                               id = persister.getIdentifier( x );
+                       }
+                       else {
+                               id = (Serializable) x;
+                       }
+               }
+               return persister.getIdentifierType().getHashCode( id, factory );
+       }
+
+       @Override
+       public boolean isEqual(Object x, Object y, SessionFactoryImplementor factory) {
+               // associations (many-to-one and one-to-one) can be null...
+               if ( x == null || y == null ) {
+                       return x == y;
+               }
+
+               EntityPersister persister = factory.getEntityPersister(associatedEntityName);
+               if ( !persister.canExtractIdOutOfEntity() ) {
+                       return super.isEqual(x, y );
+               }
+
+               final Class mappedClass = persister.getMappedClass();
+               Serializable xid;
+               if (x instanceof HibernateProxy) {
+                       xid = ( (HibernateProxy) x ).getHibernateLazyInitializer()
+                                       .getIdentifier();
+               }
+               else {
+                       if ( mappedClass.isAssignableFrom( x.getClass() ) ) {
+                               xid = persister.getIdentifier( x );
+                       }
+                       else {
+                               //JPA 2 case where @IdClass contains the id and not the associated entity
+                               xid = (Serializable) x;
+                       }
+               }
+
+               Serializable yid;
+               if (y instanceof HibernateProxy) {
+                       yid = ( (HibernateProxy) y ).getHibernateLazyInitializer()
+                                       .getIdentifier();
+               }
+               else {
+                       if ( mappedClass.isAssignableFrom( y.getClass() ) ) {
+                               yid = persister.getIdentifier( y );
+                       }
+                       else {
+                               //JPA 2 case where @IdClass contains the id and not the associated entity
+                               yid = (Serializable) y;
+                       }
+               }
+
+               return persister.getIdentifierType()
+                               .isEqual(xid, yid, factory);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public boolean isEmbeddedInXML() {
+               return isEmbeddedInXML;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public boolean isXMLElement() {
+               return isEmbeddedInXML;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
+               if ( !isEmbeddedInXML ) {
+                       return getIdentifierType(factory).fromXMLNode(xml, factory);
+               }
+               else {
+                       return xml;
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+    public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException {
+               if ( !isEmbeddedInXML ) {
+                       getIdentifierType(factory).setToXMLNode(node, value, factory);
+               }
+               else {
+                       Element elt = (Element) value;
+                       replaceNode( node, new ElementWrapper(elt) );
+               }
+       }
+
+       @Override
+    public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
+       throws MappingException {
+               if ( isReferenceToPrimaryKey() ) { //TODO: this is a bit arbitrary, expose a switch to the user?
+                       return "";
+               }
+               else {
+                       return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
+               }
+       }
+
+       /**
+        * Resolve an identifier or unique key value
+        */
+       @Override
+    public Object resolve(Object value, SessionImplementor session, Object owner) throws HibernateException {
+               if ( isNotEmbedded( session ) ) {
+                       return value;
+               }
+
+               if ( value == null ) {
+                       return null;
+               }
+               else {
+                       if ( isNull( owner, session ) ) {
+                               return null; //EARLY EXIT!
+                       }
+
+                       if ( isReferenceToPrimaryKey() ) {
+                               return resolveIdentifier( (Serializable) value, session );
+                       }
+                       else {
+                               return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session );
+                       }
+               }
+       }
+
+       @Override
+    public Type getSemiResolvedType(SessionFactoryImplementor factory) {
+               return factory.getEntityPersister( associatedEntityName ).getIdentifierType();
+       }
+
+       protected final Object getIdentifier(Object value, SessionImplementor session) throws HibernateException {
+               if ( isNotEmbedded(session) ) {
+                       return value;
+               }
+
+               if ( isReferenceToPrimaryKey() ) {
+                       return ForeignKeys.getEntityIdentifierIfNotUnsaved( getAssociatedEntityName(), value, session ); //tolerates nulls
+               }
+               else if ( value == null ) {
+                       return null;
+               }
+               else {
+                       EntityPersister entityPersister = session.getFactory().getEntityPersister( getAssociatedEntityName() );
+                       Object propertyValue = entityPersister.getPropertyValue( value, uniqueKeyPropertyName );
+                       // We now have the value of the property-ref we reference.  However,
+                       // we need to dig a little deeper, as that property might also be
+                       // an entity type, in which case we need to resolve its identitifier
+                       Type type = entityPersister.getPropertyType( uniqueKeyPropertyName );
+                       if ( type.isEntityType() ) {
+                               propertyValue = ( ( EntityType ) type ).getIdentifier( propertyValue, session );
+                       }
+
+                       return propertyValue;
+               }
+       }
+
+       /**
+        * @deprecated To be removed in 5.  Removed as part of removing the notion of DOM entity-mode.
+        * See Jira issue: <a href="https://hibernate.onjira.com/browse/HHH-7771">HHH-7771</a>
+        */
+       @Deprecated
+       protected boolean isNotEmbedded(SessionImplementor session) {
+//             return !isEmbeddedInXML;
+               return false;
+       }
+
+       /**
+        * Generate a loggable representation of an instance of the value mapped by this type.
+        *
+        * @param value The instance to be logged.
+        * @param factory The session factory.
+        * @return The loggable string.
+        * @throws HibernateException Generally some form of resolution problem.
+        */
+       @Override
+    public String toLoggableString(Object value, SessionFactoryImplementor factory) {
+               if ( value == null ) {
+                       return "null";
+               }
+
+               EntityPersister persister = factory.getEntityPersister( associatedEntityName );
+               StringBuilder result = new StringBuilder().append( associatedEntityName );
+
+               if ( persister.hasIdentifierProperty() ) {
+                       final EntityMode entityMode = persister.getEntityMode();
+                       final Serializable id;
+                       if ( entityMode == null ) {
+                               if ( isEmbeddedInXML ) {
+                                       throw new ClassCastException( value.getClass().getName() );
+                               }
+                               id = ( Serializable ) value;
+                       } else if ( value instanceof HibernateProxy ) {
+                               HibernateProxy proxy = ( HibernateProxy ) value;
+                               id = proxy.getHibernateLazyInitializer().getIdentifier();
+                       }
+                       else {
+                               id = persister.getIdentifier( value );
+                       }
+
+                       result.append( '#' )
+                               .append( persister.getIdentifierType().toLoggableString( id, factory ) );
+               }
+
+               return result.toString();
+       }
+
+       /**
+        * Is the association modeled here defined as a 1-1 in the database (physical model)?
+        *
+        * @return True if a 1-1 in the database; false otherwise.
+        */
+       public abstract boolean isOneToOne();
+
+       /**
+        * Is the association modeled here a 1-1 according to the logical moidel?
+        *
+        * @return True if a 1-1 in the logical model; false otherwise.
+        */
+       public boolean isLogicalOneToOne() {
+               return isOneToOne();
+       }
+
+       /**
+        * Convenience method to locate the identifier type of the associated entity.
+        *
+        * @param factory The mappings...
+        * @return The identifier type
+        */
+       Type getIdentifierType(Mapping factory) {
+               return factory.getIdentifierType( getAssociatedEntityName() );
+       }
+
+       /**
+        * Convenience method to locate the identifier type of the associated entity.
+        *
+        * @param session The originating session
+        * @return The identifier type
+        */
+       Type getIdentifierType(SessionImplementor session) {
+               return getIdentifierType( session.getFactory() );
+       }
+
+       /**
+        * Determine the type of either (1) the identifier if we reference the
+        * associated entity's PK or (2) the unique key to which we refer (i.e.
+        * the property-ref).
+        *
+        * @param factory The mappings...
+        * @return The appropriate type.
+        * @throws MappingException Generally, if unable to resolve the associated entity name
+        * or unique key property name.
+        */
+       public final Type getIdentifierOrUniqueKeyType(Mapping factory) throws MappingException {
+               if ( isReferenceToPrimaryKey() ) {
+                       return getIdentifierType(factory);
+               }
+               else {
+                       Type type = factory.getReferencedPropertyType( getAssociatedEntityName(), uniqueKeyPropertyName );
+                       if ( type.isEntityType() ) {
+                               type = ( ( EntityType ) type).getIdentifierOrUniqueKeyType( factory );
+                       }
+                       return type;
+               }
+       }
+
+       /**
+        * The name of the property on the associated entity to which our FK
+        * refers
+        *
+        * @param factory The mappings...
+        * @return The appropriate property name.
+        * @throws MappingException Generally, if unable to resolve the associated entity name
+        */
+       public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory)
+       throws MappingException {
+               if ( isReferenceToPrimaryKey() ) {
+                       return factory.getIdentifierPropertyName( getAssociatedEntityName() );
+               }
+               else {
+                       return uniqueKeyPropertyName;
+               }
+       }
+
+       protected abstract boolean isNullable();
+
+       /**
+        * Resolve an identifier via a load.
+        *
+        * @param id The entity id to resolve
+        * @param session The orginating session.
+        * @return The resolved identifier (i.e., loaded entity).
+        * @throws org.hibernate.HibernateException Indicates problems performing the load.
+        */
+       protected final Object resolveIdentifier(Serializable id, SessionImplementor session) throws HibernateException {
+               boolean isProxyUnwrapEnabled = unwrapProxy &&
+                               session.getFactory()
+                                               .getEntityPersister( getAssociatedEntityName() )
+                                               .isInstrumented();
+
+               Object proxyOrEntity = session.internalLoad(
+                               getAssociatedEntityName(),
+                               id,
+                               eager,
+                               isNullable() && !isProxyUnwrapEnabled
+               );
+
+               if ( proxyOrEntity instanceof HibernateProxy ) {
+                       ( ( HibernateProxy ) proxyOrEntity ).getHibernateLazyInitializer()
+                                       .setUnwrap( isProxyUnwrapEnabled );
+               }
+
+               return proxyOrEntity;
+       }
+
+       protected boolean isNull(Object owner, SessionImplementor session) {
+               return false;
+       }
+
+       /**
+        * Load an instance by a unique key that is not the primary key.
+        *
+        * @param entityName The name of the entity to load
+        * @param uniqueKeyPropertyName The name of the property defining the uniqie key.
+        * @param key The unique key property value.
+        * @param session The originating session.
+        * @return The loaded entity
+        * @throws HibernateException generally indicates problems performing the load.
+        */
+       public Object loadByUniqueKey(
+                       String entityName,
+                       String uniqueKeyPropertyName,
+                       Object key,
+                       SessionImplementor session) throws HibernateException {
+               final SessionFactoryImplementor factory = session.getFactory();
+               UniqueKeyLoadable persister = ( UniqueKeyLoadable ) factory.getEntityPersister( entityName );
+
+               //TODO: implement caching?! proxies?!
+
+               EntityUniqueKey euk = new EntityUniqueKey(
+                               entityName,
+                               uniqueKeyPropertyName,
+                               key,
+                               getIdentifierOrUniqueKeyType( factory ),
+                               persister.getEntityMode(),
+                               session.getFactory()
+               );
+
+               final PersistenceContext persistenceContext = session.getPersistenceContext();
+               Object result = persistenceContext.getEntity( euk );
+               if ( result == null ) {
+                       result = persister.loadByUniqueKey( uniqueKeyPropertyName, key, session );
+               }
+               return result == null ? null : persistenceContext.proxyFor( result );
+       }
+
+}