[hibernate-commits] Hibernate SVN: r10777 - in trunk/Hibernate3: src/org/hibernate/type test/org/hibernate/test/ops

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Nov 8 17:02:31 EST 2006


Author: steve.ebersole at jboss.com
Date: 2006-11-08 17:02:28 -0500 (Wed, 08 Nov 2006)
New Revision: 10777

Added:
   trunk/Hibernate3/test/org/hibernate/test/ops/Address.java
   trunk/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml
   trunk/Hibernate3/test/org/hibernate/test/ops/Person.java
   trunk/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java
Modified:
   trunk/Hibernate3/src/org/hibernate/type/EntityType.java
   trunk/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java
   trunk/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
Log:
HHH-2138 : merge and bidirectional one-to-one

Modified: trunk/Hibernate3/src/org/hibernate/type/EntityType.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/type/EntityType.java	2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/src/org/hibernate/type/EntityType.java	2006-11-08 22:02:28 UTC (rev 10777)
@@ -27,7 +27,8 @@
 import org.hibernate.util.ReflectHelper;
 
 /**
- * A reference to an entity class
+ * Base for types which map associations to persistent entities.
+ *
  * @author Gavin King
  */
 public abstract class EntityType extends AbstractType implements AssociationType {
@@ -37,38 +38,27 @@
 	protected final boolean isEmbeddedInXML;
 	private final boolean eager;
 	private final boolean unwrapProxy;
-	
-	public boolean isEmbeddedInXML() {
-		return isEmbeddedInXML;
-	}
-	
-	public final boolean isEntityType() {
-		return true;
-	}
-	
-	public String getPropertyName() {
-		return null;
-	}
 
-	public final String getAssociatedEntityName() {
-		return associatedEntityName;
-	}
+	private transient Class returnedClass;
 
-	public final boolean isSame(Object x, Object y, EntityMode entityMode) {
-		return x==y;
-	}
-	
-	public int compare(Object x, Object y, EntityMode entityMode) {
-		return 0; //TODO: entities CAN be compared, by PK, fix this!
-	}
-	
+	/**
+	 * Constructs the requested entity type mapping.
+	 *
+	 * @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".
+	 */
 	protected EntityType(
-			String entityName, 
-			String uniqueKeyPropertyName, 
-			boolean eager, 
+			String entityName,
+			String uniqueKeyPropertyName,
+			boolean eager,
 			boolean isEmbeddedInXML,
-			boolean unwrapProxy
-	) {
+			boolean unwrapProxy) {
 		this.associatedEntityName = entityName;
 		this.uniqueKeyPropertyName = uniqueKeyPropertyName;
 		this.isEmbeddedInXML = isEmbeddedInXML;
@@ -76,322 +66,209 @@
 		this.unwrapProxy = unwrapProxy;
 	}
 
-	public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner) 
-	throws HibernateException, SQLException {
-		return nullSafeGet( rs, new String[] {name}, session, owner );
+	/**
+	 * An entity type is a type of association type
+	 *
+	 * @return True.
+	 */
+	public boolean isAssociationType() {
+		return true;
 	}
 
 	/**
-	 * 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.
+	 * Explicitly, an entity type is an entity type ;)
+	 *
+	 * @return True.
 	 */
-	public final Class getReturnedClass() {
-		try {
-			return ReflectHelper.classForName(associatedEntityName);
-		}
-		catch (ClassNotFoundException cnfe) {
-			return java.util.Map.class;
-		}
+	public final boolean isEntityType() {
+		return true;
 	}
 
-	/*protected final Object getActualIdentifier(Object value, SessionImplementor session) throws HibernateException {
-		return session.getEntityIdentifierIfNotUnsaved(value); //tolerates nulls
-	}*/
-
-	protected final Object getIdentifier(Object value, SessionImplementor session) 
-	throws HibernateException {
-
-		if ( isNotEmbedded(session) ) return value;
-		
-		if ( isReferenceToPrimaryKey() ) {
-			return ForeignKeys.getEntityIdentifierIfNotUnsaved(associatedEntityName, value, session); //tolerates nulls
-		}
-		else if (value==null) {
-			return null;
-		}
-		else {
-			return session.getFactory()
-			        .getEntityPersister( getAssociatedEntityName() )
-			        .getPropertyValue( value, uniqueKeyPropertyName, session.getEntityMode() );
-		}
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isMutable() {
+		return false;
 	}
 
-	protected boolean isNotEmbedded(SessionImplementor session) {
-		return !isEmbeddedInXML && session.getEntityMode()==EntityMode.DOM4J;
+	/**
+	 * Generates a string representation of this type.
+	 *
+	 * @return string rep
+	 */
+	public String toString() {
+		return getClass().getName() + '(' + getAssociatedEntityName() + ')';
 	}
 
 	/**
-	 * Get the identifier value of an instance or proxy
+	 * For entity types, the name correlates to the associated entity name.
 	 */
-	private static Serializable getIdentifier(Object object, EntityPersister persister, EntityMode entityMode)
-	throws HibernateException {
-		if (object instanceof HibernateProxy) {
-			HibernateProxy proxy = (HibernateProxy) object;
-			LazyInitializer li = proxy.getHibernateLazyInitializer();
-			return li.getIdentifier();
-		}
-		else {
-			return persister.getIdentifier( object, entityMode );
-		}
-	}	
-	
-	public String toLoggableString(Object value, SessionFactoryImplementor factory) 
-	throws HibernateException {
-
-		if (value==null) return "null";
-		
-		EntityPersister persister = factory.getEntityPersister(associatedEntityName);
-		StringBuffer result = new StringBuffer()
-			.append(associatedEntityName);
-		
-		if ( persister.hasIdentifierProperty() ) {
-			//TODO: use of a guess here is bad...
-			final EntityMode entityMode = persister.guessEntityMode(value);
-			final Serializable id;
-			if (entityMode==null) {
-				if ( isEmbeddedInXML ) {
-					throw new ClassCastException( value.getClass().getName() );
-				}
-				id = (Serializable) value;
-			}
-			else {
-				id = getIdentifier( value, persister, entityMode );
-			}
-			
-			result.append('#')
-				.append( persister.getIdentifierType().toLoggableString(id, factory) );
-		}
-		
-		return result.toString();
+	public String getName() {
+		return associatedEntityName;
 	}
-	
-	/*public String toXMLString(Object value, SessionFactoryImplementor factory) throws HibernateException {
-		if (isEmbeddedInXML) throw new UnsupportedOperationException("entity references cannot be stringified");
-		if (factory==null) throw new AssertionFailure("null factory passed to toString");
-		return getIdentifierType(factory).toXMLString(value, factory);
-	}
 
-	public Object fromXMLString(String xml, Mapping factory) throws HibernateException {
-		if (isEmbeddedInXML) throw new UnsupportedOperationException("entity references cannot be stringified");
-		if (factory==null) throw new AssertionFailure("null factory passed to fromString");
-		return getIdentifierType(factory).fromXMLString(xml, factory);
-	}*/
-
-	public String getName() { return associatedEntityName; }
-
-	public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
-		return value; //special case ... this is the leaf of the containment graph, even though not immutable
+	/**
+	 * 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;
 	}
 
-	public boolean isMutable() {
-		return false;
+	public String getRHSUniqueKeyPropertyName() {
+		return uniqueKeyPropertyName;
 	}
 
-	public abstract boolean isOneToOne();
-
-	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;
-			//TODO: can this ever get called????
-			Object id = getIdentifier(original, session);
-			if (id==null) throw new AssertionFailure("cannot copy a reference to an object with a null id");
-			id = getIdentifierOrUniqueKeyType( session.getFactory() )
-					.replace(id, null, session, owner, copyCache);
-			return resolve(id, session, owner);
-		}
+	public String getLHSPropertyName() {
+		return null;
 	}
 
-	public boolean isAssociationType() {
-		return true;
+	public String getPropertyName() {
+		return null;
 	}
 
-	public final Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
-	throws HibernateException, SQLException {
-		return resolve( hydrate(rs, names, session, owner), session, owner );
+	/**
+	 * The name of the associated entity.
+	 *
+	 * @return The associated entity name.
+	 */
+	public final String getAssociatedEntityName() {
+		return associatedEntityName;
 	}
 
-	public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) 
-	throws MappingException {
-		return (Joinable) factory.getEntityPersister(associatedEntityName);
+	/**
+	 * The name of the associated entity.
+	 *
+	 * @param factory The session factory, for resolution.
+	 * @return The associated entity name.
+	 */
+	public String getAssociatedEntityName(SessionFactoryImplementor factory) {
+		return getAssociatedEntityName();
 	}
-	
-	Type getIdentifierType(Mapping factory) {
-		return factory.getIdentifierType( getAssociatedEntityName() );
-	}
 
-	Type getIdentifierType(SessionImplementor session) throws MappingException {
-		return getIdentifierType( session.getFactory() );
+	/**
+	 * 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.
+	 */
+	public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) throws MappingException {
+		return ( Joinable ) factory.getEntityPersister( associatedEntityName );
 	}
 
-	public final Type getIdentifierOrUniqueKeyType(Mapping factory) 
-	throws MappingException {
-		if ( isReferenceToPrimaryKey() ) {
-			return getIdentifierType(factory);
+	/**
+	 * 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.
+	 */
+	public final Class getReturnedClass() {
+		if ( returnedClass == null ) {
+			returnedClass = determineAssociatedEntityClass();
 		}
-		else {
-			return factory.getReferencedPropertyType( getAssociatedEntityName(), uniqueKeyPropertyName );
-		}
+		return returnedClass;
 	}
 
-	public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory)
-	throws MappingException {
-		if ( isReferenceToPrimaryKey() ) {
-			return factory.getIdentifierPropertyName( getAssociatedEntityName() );
+	private Class determineAssociatedEntityClass() {
+		try {
+			return ReflectHelper.classForName( getAssociatedEntityName() );
 		}
-		else {
-			return uniqueKeyPropertyName;
+		catch ( ClassNotFoundException cnfe ) {
+			return java.util.Map.class;
 		}
 	}
-	
-	protected abstract boolean isNullable();
 
 	/**
-	 * Resolve an identifier
+	 * {@inheritDoc}
 	 */
-	protected final Object resolveIdentifier(Serializable id, SessionImplementor session) 
-	throws HibernateException {
-		
-		boolean isProxyUnwrapEnabled = unwrapProxy && 
-				session.getFactory()
-						.getEntityPersister( getAssociatedEntityName() )
-						.isInstrumented( session.getEntityMode() );
-		
-		Object proxyOrEntity = session.internalLoad( 
-				getAssociatedEntityName(), 
-				id, 
-				eager, 
-				isNullable() && !isProxyUnwrapEnabled
-			);
-		
-		if (proxyOrEntity instanceof HibernateProxy) {
-			( (HibernateProxy) proxyOrEntity ).getHibernateLazyInitializer()
-					.setUnwrap(isProxyUnwrapEnabled);
-		}
-		
-		return proxyOrEntity; 
+	public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
+	throws HibernateException, SQLException {
+		return nullSafeGet( rs, new String[] {name}, session, owner );
 	}
 
-	protected boolean isNull(Object owner, SessionImplementor session) {
-		return false;
-	}
-	
 	/**
-	 * Resolve an identifier or unique key value
+	 * {@inheritDoc}
 	 */
-	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 
-					);
-			}
-		}
+	public final Object nullSafeGet(
+			ResultSet rs,
+			String[] names,
+			SessionImplementor session,
+			Object owner) throws HibernateException, SQLException {
+		return resolve( hydrate(rs, names, session, owner), session, owner );
 	}
 
-	public String getAssociatedEntityName(SessionFactoryImplementor factory) {
-		return getAssociatedEntityName();
+	/**
+	 * Two entities are considered the same when their instances are the same.
+	 *
+	 * @param x One entity instance
+	 * @param y Another entity instance
+	 * @param entityMode The entity mode.
+	 * @return True if x == y; false otherwise.
+	 */
+	public final boolean isSame(Object x, Object y, EntityMode entityMode) {
+		return x == y;
 	}
 
 	/**
-	 * Does this association foreign key reference the
-	 * primary key of the other table?
+	 * {@inheritDoc}
 	 */
-	public boolean isReferenceToPrimaryKey() {
-		return uniqueKeyPropertyName==null;
+	public int compare(Object x, Object y, EntityMode entityMode) {
+		return 0; //TODO: entities CAN be compared, by PK, fix this! -> only if/when we can extract the id values....
 	}
-	
-	public String getRHSUniqueKeyPropertyName() {
-		return uniqueKeyPropertyName;
-	}
 
-	public String toString() {
-		return getClass().getName() + '(' + getAssociatedEntityName() + ')';
+	/**
+	 * {@inheritDoc}
+	 */
+	public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
+		return value; //special case ... this is the leaf of the containment graph, even though not immutable
 	}
 
 	/**
-	 * Load an instance by a unique key that is not the primary key.
+	 * {@inheritDoc}
 	 */
-	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 ),
-				session.getEntityMode(), 
-				session.getFactory()
-			);
-		
-		final PersistenceContext persistenceContext = session.getPersistenceContext();
-		Object result = persistenceContext.getEntity(euk);
-		//if ( result==null && !persistenceContext.isNonExistant(euk) ) {
-		if ( result==null ) {
-			result = persister.loadByUniqueKey(uniqueKeyPropertyName, key, session);
+	public Object replace(
+			Object original,
+			Object target,
+			SessionImplementor session,
+			Object owner,
+			Map copyCache) throws HibernateException {
+		if ( original == null ) {
+			return null;
 		}
-		return result==null ? null : persistenceContext.proxyFor(result);
-		
-	}
-
-	public String getLHSPropertyName() {
-		return null;
-	}
-
-	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 "";
+		Object cached = copyCache.get(original);
+		if ( cached != null ) {
+			return cached;
 		}
 		else {
-			return getAssociatedJoinable(factory).filterFragment(alias, enabledFilters);
+			if ( original == target ) {
+				return target;
+			}
+			Object id = getIdentifier( original, session );
+			if ( id == null ) {
+				throw new AssertionFailure("cannot copy a reference to an object with a null id");
+			}
+			id = getIdentifierOrUniqueKeyType( session.getFactory() )
+					.replace(id, null, session, owner, copyCache);
+			return resolve( id, session, owner );
 		}
 	}
-	
-	public Type getSemiResolvedType(SessionFactoryImplementor factory) {
-		return factory.getEntityPersister(associatedEntityName).getIdentifierType();
-	}
-	
+
+	/**
+	 * {@inheritDoc}
+	 */
 	public int getHashCode(Object x, EntityMode entityMode, SessionFactoryImplementor factory) {
 		EntityPersister persister = factory.getEntityPersister(associatedEntityName);
 		if ( !persister.canExtractIdOutOfEntity() ) {
 			return super.getHashCode(x, entityMode);
 		}
-		
+
 		final Serializable id;
 		if (x instanceof HibernateProxy) {
 			id = ( (HibernateProxy) x ).getHibernateLazyInitializer().getIdentifier();
@@ -401,13 +278,16 @@
 		}
 		return persister.getIdentifierType().getHashCode(id, entityMode, factory);
 	}
-	
+
+	/**
+	 * {@inheritDoc}
+	 */
 	public boolean isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory) {
 		EntityPersister persister = factory.getEntityPersister(associatedEntityName);
 		if ( !persister.canExtractIdOutOfEntity() ) {
 			return super.isEqual(x, y, entityMode);
 		}
-		
+
 		Serializable xid;
 		if (x instanceof HibernateProxy) {
 			xid = ( (HibernateProxy) x ).getHibernateLazyInitializer()
@@ -416,7 +296,7 @@
 		else {
 			xid = persister.getIdentifier(x, entityMode);
 		}
-		
+
 		Serializable yid;
 		if (y instanceof HibernateProxy) {
 			yid = ( (HibernateProxy) y ).getHibernateLazyInitializer()
@@ -425,15 +305,28 @@
 		else {
 			yid = persister.getIdentifier(y, entityMode);
 		}
-		
+
 		return persister.getIdentifierType()
 				.isEqual(xid, yid, entityMode, factory);
 	}
 
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isEmbeddedInXML() {
+		return isEmbeddedInXML;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
 	public boolean isXMLElement() {
 		return isEmbeddedInXML;
 	}
 
+	/**
+	 * {@inheritDoc}
+	 */
 	public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
 		if ( !isEmbeddedInXML ) {
 			return getIdentifierType(factory).fromXMLNode(xml, factory);
@@ -443,8 +336,10 @@
 		}
 	}
 
-	public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) 
-	throws HibernateException {
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException {
 		if ( !isEmbeddedInXML ) {
 			getIdentifierType(factory).setToXMLNode(node, value, factory);
 		}
@@ -454,4 +349,265 @@
 		}
 	}
 
+	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
+	 */
+	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 );
+			}
+		}
+	}
+
+	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, session.getEntityMode() );
+			// 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;
+		}
+	}
+
+	protected boolean isNotEmbedded(SessionImplementor session) {
+		return !isEmbeddedInXML && session.getEntityMode()==EntityMode.DOM4J;
+	}
+
+	/**
+	 * Get the identifier value of an instance or proxy.
+	 * <p/>
+	 * Intended only for loggin purposes!!!
+	 *
+	 * @param object The object from which to extract the identifier.
+	 * @param persister The entity persister
+	 * @param entityMode The entity mode
+	 * @return The extracted identifier.
+	 */
+	private static Serializable getIdentifier(Object object, EntityPersister persister, EntityMode entityMode) {
+		if (object instanceof HibernateProxy) {
+			HibernateProxy proxy = (HibernateProxy) object;
+			LazyInitializer li = proxy.getHibernateLazyInitializer();
+			return li.getIdentifier();
+		}
+		else {
+			return persister.getIdentifier( object, entityMode );
+		}
+	}
+
+	/**
+	 * 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.
+	 */
+	public String toLoggableString(Object value, SessionFactoryImplementor factory) {
+		if ( value == null ) {
+			return "null";
+		}
+
+		EntityPersister persister = factory.getEntityPersister( associatedEntityName );
+		StringBuffer result = new StringBuffer().append( associatedEntityName );
+
+		if ( persister.hasIdentifierProperty() ) {
+			final EntityMode entityMode = persister.guessEntityMode( value );
+			final Serializable id;
+			if ( entityMode == null ) {
+				if ( isEmbeddedInXML ) {
+					throw new ClassCastException( value.getClass().getName() );
+				}
+				id = ( Serializable ) value;
+			}
+			else {
+				id = getIdentifier( value, persister, entityMode );
+			}
+
+			result.append( '#' )
+				.append( persister.getIdentifierType().toLoggableString( id, factory ) );
+		}
+
+		return result.toString();
+	}
+
+	public abstract boolean 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( session.getEntityMode() );
+
+		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 ),
+				session.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 );
+	}
+
 }

Modified: trunk/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java	2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java	2006-11-08 22:02:28 UTC (rev 10777)
@@ -1,11 +1,8 @@
 package org.hibernate.test.ops;
 
-import java.util.Iterator;
-
 import org.hibernate.test.TestCase;
 import org.hibernate.cfg.Configuration;
 import org.hibernate.cfg.Environment;
-import org.hibernate.Session;
 
 /**
  * {@inheritDoc}
@@ -23,7 +20,7 @@
 	}
 
 	protected String[] getMappings() {
-		return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml", "ops/OptLockEntity.hbm.xml" };
+		return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml", "ops/OptLockEntity.hbm.xml", "ops/OneToOne.hbm.xml" };
 	}
 
 	public String getCacheConcurrencyStrategy() {

Added: trunk/Hibernate3/test/org/hibernate/test/ops/Address.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/Address.java	2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/Address.java	2006-11-08 22:02:28 UTC (rev 10777)
@@ -0,0 +1,65 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+	private Long id;
+	private String streetAddress;
+	private String city;
+	private String country;
+	private Person resident;
+
+	public Address() {
+	}
+
+	public Address(String streetAddress, String city, String country, Person resident) {
+		this.streetAddress = streetAddress;
+		this.city = city;
+		this.country = country;
+		this.resident = resident;
+		resident.setAddress( this );
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getStreetAddress() {
+		return streetAddress;
+	}
+
+	public void setStreetAddress(String streetAddress) {
+		this.streetAddress = streetAddress;
+	}
+
+	public String getCity() {
+		return city;
+	}
+
+	public void setCity(String city) {
+		this.city = city;
+	}
+
+	public String getCountry() {
+		return country;
+	}
+
+	public void setCountry(String country) {
+		this.country = country;
+	}
+
+	public Person getResident() {
+		return resident;
+	}
+
+	public void setResident(Person resident) {
+		this.resident = resident;
+	}
+}

Modified: trunk/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/MergeTest.java	2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/MergeTest.java	2006-11-08 22:02:28 UTC (rev 10777)
@@ -26,6 +26,66 @@
 		return new TestSuite( MergeTest.class );
 	}
 
+	public void testMergeBidiPrimayKeyOneToOne() throws Exception {
+		Session s = openSession();
+        s.beginTransaction();
+		Person p = new Person( "steve" );
+		new PersonalDetails( "I have big feet", p );
+		s.persist( p );
+		s.getTransaction().commit();
+		s.close();
+
+		clearCounts();
+
+		p.getDetails().setSomePersonalDetail( p.getDetails().getSomePersonalDetail() + " and big hands too" );
+		s = openSession();
+        s.beginTransaction();
+		p = ( Person ) s.merge( p );
+		s.getTransaction().commit();
+		s.close();
+
+		assertInsertCount( 0 );
+		assertUpdateCount( 1 );
+		assertDeleteCount( 0 );
+
+		s = openSession();
+        s.beginTransaction();
+		s.delete( p );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testMergeBidiForeignKeyOneToOne() throws Exception {
+		Session s = openSession();
+        s.beginTransaction();
+		Person p = new Person( "steve" );
+		Address a = new Address( "123 Main", "Austin", "US", p );
+		s.persist( a );
+		s.persist( p );
+		s.getTransaction().commit();
+		s.close();
+
+		clearCounts();
+
+		p.getAddress().setStreetAddress( "321 Main" );
+		s = openSession();
+        s.beginTransaction();
+		p = ( Person ) s.merge( p );
+		s.getTransaction().commit();
+		s.close();
+
+		assertInsertCount( 0 );
+		assertUpdateCount( 0 ); // no cascade
+		assertDeleteCount( 0 );
+
+		s = openSession();
+        s.beginTransaction();
+		s.delete( a );
+		s.delete( p );
+		s.getTransaction().commit();
+		s.close();
+	}
+
     public void testNoExtraUpdatesOnMerge() throws Exception {
 		Session s = openSession();
         s.beginTransaction();

Added: trunk/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml	2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml	2006-11-08 22:02:28 UTC (rev 10777)
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Mappings demonstrating bidirectional one-to-one mappings for testing
+ with various operations.
+
+ Person -> Address is modeled as a bidirectional one to one based on FK.
+ Person -> Details is modeled as a bidirectional one to one based on PK.
+-->
+
+<hibernate-mapping package="org.hibernate.test.ops">
+
+    <class name="Person" table="OPS_PERSON">
+        <id name="id" column="ID" type="long">
+            <generator class="increment"/>
+        </id>
+        <property name="name" column="NAME" type="string"/>
+        <one-to-one name="address" class="Address" property-ref="resident" />
+        <one-to-one name="details" class="PersonalDetails" cascade="all" />
+    </class>
+
+    <class name="Address" table="OPS_ADDRESS">
+        <id name="id" column="ID" type="long">
+            <generator class="increment"/>
+        </id>
+        <property name="streetAddress" column="STREET" type="string" />
+        <property name="city" column="CITY" type="string" />
+        <property name="country" column="CTRY" type="string" />
+        <many-to-one name="resident" column="RESIDENT" class="Person" />
+    </class>
+
+    <class name="PersonalDetails" table="OPS_PERS_DETAIL">
+        <id name="id" column="ID" type="long">
+            <generator class="increment"/>
+        </id>
+        <property name="somePersonalDetail" column="SOME_DETAIL" type="string"/>
+        <one-to-one name="person" class="Person" cascade="none" constrained="true" />
+    </class>
+
+</hibernate-mapping>
\ No newline at end of file

Added: trunk/Hibernate3/test/org/hibernate/test/ops/Person.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/Person.java	2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/Person.java	2006-11-08 22:02:28 UTC (rev 10777)
@@ -0,0 +1,52 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+	private Long id;
+	private String name;
+	private Address address;
+	private PersonalDetails details;
+
+	public Person() {
+	}
+
+	public Person(String name) {
+		this.name = name;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Address getAddress() {
+		return address;
+	}
+
+	public void setAddress(Address address) {
+		this.address = address;
+	}
+
+	public PersonalDetails getDetails() {
+		return details;
+	}
+
+	public void setDetails(PersonalDetails details) {
+		this.details = details;
+	}
+}

Added: trunk/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java	2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java	2006-11-08 22:02:28 UTC (rev 10777)
@@ -0,0 +1,45 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PersonalDetails {
+	private Long id;
+	private String somePersonalDetail;
+	private Person person;
+
+	public PersonalDetails() {
+	}
+
+	public PersonalDetails(String somePersonalDetail, Person person) {
+		this.somePersonalDetail = somePersonalDetail;
+		this.person = person;
+		person.setDetails( this );
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getSomePersonalDetail() {
+		return somePersonalDetail;
+	}
+
+	public void setSomePersonalDetail(String somePersonalDetail) {
+		this.somePersonalDetail = somePersonalDetail;
+	}
+
+	public Person getPerson() {
+		return person;
+	}
+
+	public void setPerson(Person person) {
+		this.person = person;
+	}
+}




More information about the hibernate-commits mailing list