db-jdo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From m..@apache.org
Subject svn commit: r202008 - in /incubator/jdo/trunk/core20/src/java/org/apache/jdo: impl/model/jdo/ impl/model/jdo/caching/ impl/model/jdo/util/ model/jdo/
Date Mon, 27 Jun 2005 16:56:12 GMT
Author: mbo
Date: Mon Jun 27 09:56:09 2005
New Revision: 202008

URL: http://svn.apache.org/viewcvs?rev=202008&view=rev
Log:
Added support for mappedBy and improved inverse handling to JDO model

Added:
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOReferenceImplDynamic.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/UnresolvedRelationshipHelper.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOReferenceImplCaching.java
Removed:
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOReferenceImpl.java
Modified:
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOArrayImplDynamic.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOAssociatedPropertyImplDynamic.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOClassImplDynamic.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOCollectionImplDynamic.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOFieldImplDynamic.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOMapImplDynamic.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOModelImplDynamic.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDORelationshipImpl.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOArrayImplCaching.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOCollectionImplCaching.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOFieldImplCaching.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOMapImplCaching.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/util/PrintSupport.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDOField.java
    incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDORelationship.java

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOArrayImplDynamic.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOArrayImplDynamic.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOArrayImplDynamic.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOArrayImplDynamic.java Mon Jun 27 09:56:09 2005
@@ -28,7 +28,7 @@
  *
  * @author Michael Bouschen
  * @since 1.1
- * @version 1.1
+ * @version 2.0
  */
 public class JDOArrayImplDynamic extends JDORelationshipImpl 
     implements JDOArray {
@@ -85,6 +85,18 @@
      */
     public boolean isJDOArray() {
         return true;
+    }
+
+    //========= Internal helper methods ==========
+
+    /** 
+     * Get the type representation of the relationship. This will be 
+     * the JavaType for references, the element type for collections
+     * and arrays, and the value type for maps.
+     * @return the relationship type
+     */
+    public JavaType getRelatedJavaType() {
+        return getElementType();
     }
 
 }

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOAssociatedPropertyImplDynamic.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOAssociatedPropertyImplDynamic.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOAssociatedPropertyImplDynamic.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOAssociatedPropertyImplDynamic.java Mon Jun 27 09:56:09 2005
@@ -187,6 +187,18 @@
     }
 
     /** Deletegate to associatedJDOField. */
+    public String getMappedByName() {
+        return associatedJDOField.getMappedByName();
+    }
+
+    /** Throws ModelException. */
+    public void setMappedByName(String mappedByName)
+        throws ModelException {
+        throw new ModelException (
+            msg.msg("EXC_CannotModifyJDOProperty")); //NOI18N
+    }
+
+    /** Deletegate to associatedJDOField. */
     public JDORelationship getRelationship() {
         return associatedJDOField.getRelationship();
     }

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOClassImplDynamic.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOClassImplDynamic.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOClassImplDynamic.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOClassImplDynamic.java Mon Jun 27 09:56:09 2005
@@ -390,21 +390,22 @@
                 msg.msg("EXC_InvalidMember", "null")); //NOI18N
         }
         String name = member.getName();
-        if (member instanceof JDOProperty) {
-            // Check for property with associated field
+        if (member instanceof JDOField) {
+            JDOField field = (JDOField) member;
+            // nullify mappedByName which removes mappedBy info 
+            field.setMappedByName(null);
+            // nullify relationship which updates its inverse
+            field.setRelationship(null);
             if (associatedProperties.containsValue(member)) {
                 associatedProperties.remove(name);
             }
             else {
                 declaredFields.remove(name); 
             }
-        }
-        if (member instanceof JDOField) {
-            // JDOField which is not a JDOProperty
-            declaredFields.remove(name);
+
             // There might be a property with the field to be removed as
             // associated JDOField => remove the property too.
-            JDOProperty prop = getAssociatedProperty((JDOField) member);
+            JDOProperty prop = getAssociatedProperty(field);
             if (prop != null) {
                 removeDeclaredMember(prop);
             }

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOCollectionImplDynamic.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOCollectionImplDynamic.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOCollectionImplDynamic.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOCollectionImplDynamic.java Mon Jun 27 09:56:09 2005
@@ -146,4 +146,16 @@
         return true;
     }
 
+    //========= Internal helper methods ==========
+
+    /** 
+     * Get the type representation of the relationship. This will be 
+     * the JavaType for references, the element type for collections
+     * and arrays, and the value type for maps.
+     * @return the relationship type
+     */
+    public JavaType getRelatedJavaType() {
+        return getElementType();
+    }
+
 }

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOFieldImplDynamic.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOFieldImplDynamic.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOFieldImplDynamic.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOFieldImplDynamic.java Mon Jun 27 09:56:09 2005
@@ -85,6 +85,9 @@
     /** Property serializable. Defaults to <code>false</code>. */
     private boolean serializable = false;
 
+    /** Property mappedByName. Defaults to <code>null</code>. */
+    private String mappedByName = null;
+
     /** Relationship JDOField<->JDORelationship. */
     protected JDORelationship relationship;
     
@@ -255,7 +258,7 @@
     
     /**
      * Get the corresponding JavaField representation for this JDOField.
-     * @return the corresponding Java field representation
+     * @return the corresponding JavaField representation
      */
     public JavaField getJavaField() {
         if (javaField != null) {
@@ -295,6 +298,42 @@
         this.serializable = serializable;
     }
 
+    /** 
+     * Get the name of the field specified in a mappedBy attribute in the
+     * metadata. The method returns <code>null</code> if the metadata for this
+     * field does not specify the mappedBy attribute.  Note that this 
+     * can be provided at the field level to help population of the model, 
+     * but should only be specified on a field that has a corresponding
+     * relationship.
+     * @return the mappedBy field name if available; <code>null</code>
+     * otherwise.
+     */
+    public String getMappedByName() {
+        return mappedByName;
+    }
+
+    /**
+     * Set the name of the field specified in a mappedBy attribute in the
+     * metadata.  Note that this can be provided at the field level to 
+     * help population of the model, but should only be specified on a 
+     * field that has a corresponding relationship.
+     * @param mappedByName the mappedBy field name.
+     * @exception ModelException if impossible
+     */
+    public void setMappedByName(String mappedByName) throws ModelException {
+        String oldMappedByName = this.mappedByName;
+        this.mappedByName = mappedByName;
+        UnresolvedRelationshipHelper info = getUnresolvedRelationshipHelper();
+        if (oldMappedByName != null) {
+            // remove old mappedByName from unresolved relationship helper
+            info.remove(oldMappedByName, this);
+        }
+        if (mappedByName != null) {
+            // update unresolved relationship helper
+            info.register(mappedByName, this);
+        }
+    }
+
     /**
      * Get the relationship information for this JDOField. The method 
      * returns null if the field is not part of a relationship 
@@ -342,7 +381,12 @@
      * Set the relationship information for this JDOField.
      * @param relationship the JDORelationship instance
      */
-    public void setRelationship(JDORelationship relationship) {
+    public void setRelationship(JDORelationship relationship) 
+        throws ModelException {
+        JDORelationship old = this.relationship;
+        if (old != null) {
+            old.setInverseRelationship(null);
+        }
         this.relationship = relationship;
     }
 
@@ -522,7 +566,7 @@
      * @return a new JDOReference instance bound to this JDOField
      */
     protected JDOReference createJDOReferenceInternal() {
-        JDOReferenceImpl ref = new JDOReferenceImpl();
+        JDOReferenceImplDynamic ref = new JDOReferenceImplDynamic();
         // update relationship JDORelationship->JDOField
         ref.setDeclaringField(this);
         return ref;
@@ -576,6 +620,16 @@
     private boolean nameHasJDOPrefix() {
         String name = getName();
         return (name != null) && name.startsWith("jdo"); //NOI18N
+    }
+
+    /** 
+     * Returns the UnresolvedRelationshipHelper instance from the declaring
+     * JDOModel instacne of the declaring JDOClass.
+     * @return the current UnresolvedRelationshipHelper
+     */
+    UnresolvedRelationshipHelper getUnresolvedRelationshipHelper() {
+        return ((JDOModelImplDynamic) getDeclaringClass().getDeclaringModel()).
+            getUnresolvedRelationshipHelper();
     }
     
 }

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOMapImplDynamic.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOMapImplDynamic.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOMapImplDynamic.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOMapImplDynamic.java Mon Jun 27 09:56:09 2005
@@ -236,4 +236,16 @@
         return true;
     }
 
+    //========= Internal helper methods ==========
+
+    /** 
+     * Get the type representation of the relationship. This will be 
+     * the JavaType for references, the element type for collections
+     * and arrays, and the value type for maps.
+     * @return the relationship type
+     */
+    public JavaType getRelatedJavaType() {
+        return getValueType();
+    }
+
 }

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOModelImplDynamic.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOModelImplDynamic.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOModelImplDynamic.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOModelImplDynamic.java Mon Jun 27 09:56:09 2005
@@ -118,6 +118,10 @@
     /** The default for loadXMLMetadata. */ 
     private final boolean loadXMLMetadataDefault;
     
+    /** */
+    private final UnresolvedRelationshipHelper unresolvedRelationshipHelper = 
+        new UnresolvedRelationshipHelper();
+
     /** I18N support */
     protected final static I18NHelper msg =
         I18NHelper.getInstance(JDOModelImplDynamic.class);
@@ -659,6 +663,15 @@
     }
     
     /**
+     * Returns the UnresolvedRelationshipHelper instance for this JDOModel
+     * instance. 
+     * @return the current UnresolvedRelationshipHelper
+     */
+    UnresolvedRelationshipHelper getUnresolvedRelationshipHelper() {
+        return unresolvedRelationshipHelper;
+    }
+
+    /**
      * This Iterator implementation iterates resource names of possible JDO
      * metadata files for the specified class name. Chapter 18 of the JDO
      * specification defines the search order as follows: 
@@ -753,4 +766,5 @@
         }
         
     }
+
 }

Added: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOReferenceImplDynamic.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOReferenceImplDynamic.java?rev=202008&view=auto
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOReferenceImplDynamic.java (added)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDOReferenceImplDynamic.java Mon Jun 27 09:56:09 2005
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package org.apache.jdo.impl.model.jdo;
+
+import org.apache.jdo.model.java.JavaType;
+import org.apache.jdo.model.jdo.JDOReference;
+
+/**
+ * An instance of this class represents the JDO relationship metadata 
+ * of a reference relationship field.
+ *
+ * @author Michael Bouschen
+ */
+public class JDOReferenceImplDynamic extends JDORelationshipImpl 
+    implements JDOReference
+{
+
+    /**
+     * Determines whether this JDORelationship represents a reference
+     * relationship or not. A return of <code>true</code> means this
+     * JDORelationship is a JDOReference instance.
+     * @return <code>true</code> if this JDORelationship represents a
+     * reference relationship; <code>false</code> otherwise.
+     */
+    public boolean isJDOReference() {
+        return true;
+    }
+
+    //========= Internal helper methods ==========
+
+    /** 
+     * Get the type representation of the relationship. This will be 
+     * the JavaType for references, the element type for collections
+     * and arrays, and the value type for maps.
+     * @return the relationship type
+     */
+    public JavaType getRelatedJavaType() {
+        return getDeclaringField().getType();
+    }
+
+}

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDORelationshipImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDORelationshipImpl.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDORelationshipImpl.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/JDORelationshipImpl.java Mon Jun 27 09:56:09 2005
@@ -16,6 +16,9 @@
 
 package org.apache.jdo.impl.model.jdo;
 
+import org.apache.jdo.model.ModelException;
+import org.apache.jdo.model.java.JavaType;
+import org.apache.jdo.model.jdo.JDOClass;
 import org.apache.jdo.model.jdo.JDOField;
 import org.apache.jdo.model.jdo.JDORelationship;
 
@@ -24,6 +27,7 @@
  * JDO relationship metadata of a managed field of a persistence capable class.
  * 
  * @author Michael Bouschen
+ * @version 2.0
  */
 public abstract class JDORelationshipImpl extends JDOElementImpl
     implements JDORelationship {
@@ -38,7 +42,13 @@
     private JDOField declaringField;
 
     /** Relationship JDORelationship<->JDORelationship. */
-    private JDORelationship inverse;
+    protected JDORelationship mappedBy;
+
+    /** Name of the field which is the inverse relationship */
+    private String inverseName;
+
+    /** Relationship JDORelationship<->JDORelationship. */
+    protected JDORelationship inverse;
 
     /** 
      * Get the lower cardinality bound for this relationship element.
@@ -89,21 +99,185 @@
     public void setDeclaringField(JDOField declaringField) {
         this.declaringField = declaringField;
     }
-    
+
+    /**
+     * Get the JDOClass corresponding to the type or element of this 
+     * relationship.
+     * @return the related class
+     */
+    public JDOClass getRelatedJDOClass() {
+        JavaType relatedType = getRelatedJavaType();
+
+        if (relatedType != null) {
+            JDOClass myClass = getDeclaringField().getDeclaringClass();
+            String relatedTypeName = relatedType.getName();
+
+            if (relatedTypeName.equals(myClass.getName()))
+                return myClass;
+        
+            return myClass.getDeclaringModel().getJDOClass(relatedTypeName);
+        }
+
+        return null;
+    }
+
+    /** 
+     * Get the mappedBy relationship. If there is no mappedBy relationship
+     * set, the method checks the mappedBy name as specified in the declaring
+     * field and resolves the relationship. The method return
+     * <code>null</code> if there is no mappedBy relationship set and there
+     * is no mappedBy name specified on the declaring field.
+     * @return the mappedBy relationship if available; <code>null</code>
+     * otherwise.
+     */
+    public JDORelationship getMappedBy() {
+        if (mappedBy != null) {
+            // return mappedBy relationship, if explicitly set by the setter
+            return mappedBy;
+        }
+        
+        // not set => check mappedByName of declaring field
+        JDOField field = getDeclaringField();
+        String mappedByName = field.getMappedByName();
+        if (mappedByName != null) {
+            // return a JDORelationship instance for the mappedBy field name
+            // as stored in the declaring field
+            return getInverseRelationship();
+        }
+        
+        return null;
+    }
+
     /**
-     * Get the inverse JDORelationship in the case of a managed relationship.
+     * Set the mappedBy relationship for this relationship. This method
+     * automatically updates the mappedBy name of the declaring field of this
+     * relationship.
+     * @param mappedBy the mappedBy relationship.
+     * @exception ModelException if impossible
+     */
+    public void setMappedBy(JDORelationship mappedBy) throws ModelException {
+        this.mappedBy = mappedBy;
+        String mappedByName = null;
+        if (mappedBy != null) {
+            JDOField declaringField = mappedBy.getDeclaringField();
+            if (declaringField != null) {
+                mappedByName = declaringField.getName();
+            }
+        }
+        getDeclaringField().setMappedByName(mappedByName);
+        setInverseRelationship(mappedBy);
+    }
+
+    /** 
+     * Get the relative name of the inverse relationship field for this
+     * relationship.  In the case of two-way relationships, the two
+     * relationships involved are inverses of each other.  If this
+     * relationship element does not participate in a two-way relationship,
+     * this returns <code>null</code>.  Note that it is possible to have
+     * this method return a value, but because of the combination of
+     * related class and lookup, there may be no corresponding
+     * JDORelationship which can be found.
+     * @return the relative name of the inverse JDORelationship
+     * @see #getInverseRelationship
+     */
+    public String getInverseRelationshipName() {
+        if (inverseName != null) {
+            // return inverseName, if explicitly set
+            return inverseName;
+        }
+        
+        JDOField declaringField = getDeclaringField();
+        String mappedByName = declaringField.getMappedByName();
+        if (mappedByName != null) {
+            // return mappedByName, if explicitly set on the declaring field
+            return mappedByName;
+        }
+
+        // try to resolve relationship info from mappedByName of the field on
+        // the other side
+        UnresolvedRelationshipHelper info = getUnresolvedRelationshipHelper();
+        // look for an unresolved relationship entry where the name of this
+        // field is used as the mappedByName
+        JDOField inverseField = 
+            info.resolve(declaringField.getName(), getRelatedJDOClass());
+        if (inverseField != null) {
+            // found inverse => update inverseName and return it
+            inverseName = inverseField.getName();
+            return inverseName;
+        }
+
+        // no inverse name available => return null
+        return null;
+    }
+
+    /**
+     * Get the inverse JDORelationship in the case of a two-way relationship.
      * @return the inverse relationship
      */
     public JDORelationship getInverseRelationship() {
-        return inverse;
+        if (inverse != null) {
+            // return inverse relationship, if explicitly set by the setter
+            return inverse;
+        }
+
+        // not set => check inverse name 
+        String fieldName = getInverseRelationshipName();
+        if (fieldName != null) {
+            JDOClass relatedClass = getRelatedJDOClass();
+            JDOField relatedField = relatedClass.getField(fieldName);
+            if (relatedField != null)
+                return relatedField.getRelationship();
+        }
+        return null;
     }
 
     /**
-     * Set the inverse JDORelationship in the case of a managed relationship.
+     * Set the inverse JDORelationship in the case of a two-way relationship.
+     * The two relationship elements involved are set as inverses of each 
+     * other and the old inverse is unset.
+     * <p>
+     * Warning: this methods casts the existing and the specified inverse
+     * relationship instance to JDORelationshipImpl.
      * @param inverseRelationship the inverse relationship
      */
-    public void setInverseRelationship(JDORelationship inverseRelationship) {
-        this.inverse = inverseRelationship;
+    public void setInverseRelationship(JDORelationship inverseRelationship) 
+        throws ModelException {
+        
+        // Skip setting the inverse, if already it is set to the specified
+        // instance. Note, do not use the result of getInverseRelationship for
+        // this check, since it might calculate an inverse relationship
+        // instance that is identical to the specified one, although the
+        // inverse is not yet set.
+        if (this.inverse == inverseRelationship) {
+            return;
+        }
+
+        // clear old inverse which still points to here
+        JDORelationshipImpl old = 
+            (JDORelationshipImpl) getInverseRelationship();
+        if (old != null) {
+            if (this.equals(old.getInverseRelationship()))
+                old.changeInverseRelationship(null);
+        }
+
+        // link from here to new inverse
+        changeInverseRelationship(inverseRelationship);
+
+        // link from new inverse back to here
+        if (inverseRelationship != null) {
+            ((JDORelationshipImpl) inverseRelationship).
+                changeInverseRelationship(this);
+        }
+    }
+
+    /**
+     * Determines whether this side of a two-way relationship is the
+     * owning side.
+     * @return <code>true</code> if this side is the owning side;
+     * <code>false</code> otherwise. 
+     */
+    public boolean isOwner() {
+        return getMappedBy() == null;
     }
 
     /**
@@ -150,4 +324,40 @@
         return false;
     }
 
+    //========= Internal helper methods ==========
+
+    /** 
+     * Get the type representation of the relationship. This will be 
+     * the JavaType for references, the element type for collections
+     * and arrays, and the value type for maps.
+     * @return the relationship type
+     */
+    public abstract JavaType getRelatedJavaType();
+
+    /** Changes the inverse relationship element for this relationship 
+     * element.
+     * This method is invoked for both sides from
+     * {@link #setInverseRelationship} and should handle setting the 
+     * internal variable.
+     * @param inverseRelationship - a relationship element to be used as the
+     * inverse for this relationship element or <code>null</code> if this
+     * relationship element does not participate in a two-way relationship.
+     * @exception ModelException if impossible
+     */
+    private void changeInverseRelationship(JDORelationship 
+        inverseRelationship) throws ModelException {
+        this.inverse = inverseRelationship;
+        this.inverseName = ((inverseRelationship == null) ? null :
+            inverseRelationship.getDeclaringField().getName());
+    }
+
+    /** 
+     * Returns the UnresolvedRelationshipHelper instance from the declaring
+     * field.
+     * @return the current UnresolvedRelationshipHelper
+     */
+    private UnresolvedRelationshipHelper getUnresolvedRelationshipHelper() {
+        return ((JDOFieldImplDynamic) getDeclaringField()).
+            getUnresolvedRelationshipHelper();
+    }    
 }

Added: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/UnresolvedRelationshipHelper.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/UnresolvedRelationshipHelper.java?rev=202008&view=auto
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/UnresolvedRelationshipHelper.java (added)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/UnresolvedRelationshipHelper.java Mon Jun 27 09:56:09 2005
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package org.apache.jdo.impl.model.jdo;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import org.apache.jdo.model.jdo.JDOClass;
+import org.apache.jdo.model.jdo.JDOField;
+
+/**
+ * A helper class to manage unresolved relationship information. 
+ * The class maps the mappedBy name to all JDOField instances using this name
+ * (which might denote fields from different classes) as the mapped by name. 
+ * To ease the access the list of JDOField instances is organized as a map
+ * using the declaring JDOClass as key.
+ */
+class UnresolvedRelationshipHelper extends HashMap 
+{
+    /** 
+     * Stores an unresolved relationship entry. The specified JDOField uses
+     * the specified field name in its mappedBy clause. The specified
+     * mappedByName denotes the field on the owning side of the relationship. 
+     * @param mappedByName the field name used in the mappedBy clause.
+     * @param jdoField the jdoField instance using the specified field name as
+     * its mappedBy name.
+     */
+    public synchronized void register(String mappedByName, JDOField jdoField) {
+        Map fieldMap = (Map) get(mappedByName);
+        if (fieldMap == null) {
+            // new entry for field name
+            fieldMap = new HashMap();
+            put(mappedByName, fieldMap);
+        }
+        // store jdoField
+        fieldMap.put(jdoField.getDeclaringClass(), jdoField);
+    }
+
+    /**
+     * Look for a JDOField in the unresolved relationship entry having the
+     * specified mappedByName as its mappedBy name. The JDOField must be 
+     * declared by the specified jdoClass instance. This allows the owning
+     * side to find the JDOField using the name of the owning side in its
+     * mappedBy clause.
+     * @param mappedByName the field name used as mappedBy name.
+     * @param jdoClass the declaring JDOClass of the field to be returned.
+     * @return a JDOField declared by the specified jdoClass using the
+     * specified mappedByName as its mappedBy name. 
+     */
+    public synchronized JDOField resolve(String mappedByName, JDOClass jdoClass) {
+        JDOField field = null;
+        Map fieldMap = (Map) get(mappedByName);
+        if (fieldMap != null) {
+            // Get JDOField instance for specified JDOClass instance and 
+            // remove it directly since it is resolved.
+            // Please note, remove returns the removed entry, so we do not
+            // need an extra get for the instance to be returned. 
+            field = (JDOField) fieldMap.remove(jdoClass);
+            // remove the map if it was the last entry
+            if (fieldMap.isEmpty()) {
+                remove(mappedByName);
+            }
+        }
+        return field;
+    }
+
+    /**
+     * Removes the specified JDOField from this unresolved relationship
+     * helper.
+     * @param mappedByName the field name used in the mappedBy clause.
+     * @param jdoField the jdoField instance using the specified field name as
+     * its mappedBy name.
+     */
+    public void remove(String mappedByName, JDOField jdoField) {
+        // We can delegate to resolve here, because it will remove the
+        // resolved entry from the helper and we simply ignore the returned
+        // value from resolve. 
+        resolve(mappedByName, jdoField.getDeclaringClass());
+    }
+
+}

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOArrayImplCaching.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOArrayImplCaching.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOArrayImplCaching.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOArrayImplCaching.java Mon Jun 27 09:56:09 2005
@@ -17,6 +17,7 @@
 package org.apache.jdo.impl.model.jdo.caching;
 
 import org.apache.jdo.model.java.JavaType;
+import org.apache.jdo.model.jdo.JDORelationship;
 import org.apache.jdo.impl.model.jdo.JDOArrayImplDynamic;
 
 /**
@@ -27,13 +28,40 @@
  *
  * @author Michael Bouschen
  * @since 1.1
- * @version 1.1
+ * @version 2.0
  */
 public class JDOArrayImplCaching extends JDOArrayImplDynamic {
     
     /** Type of the array element. */
     private transient JavaType elementType;
     
+    /** 
+     * Get the mappedBy relationship. If there is no mappedBy relationship
+     * set, the method checks the mappedBy name as specified in the declaring
+     * field and resolves the relationship. The method returns
+     * <code>null</code> if there is no mappedBy relationship set and there
+     * is no mappedBy name specified on the declaring field.
+     * @return the mappedBy relationship if available; <code>null</code>
+     * otherwise.
+     */
+    public JDORelationship getMappedBy() {
+        if (mappedBy == null) {
+            mappedBy = super.getMappedBy();
+        }
+        return mappedBy;
+    }
+
+    /**
+     * Get the inverse JDORelationship in the case of a two-way relationship.
+     * @return the inverse relationship
+     */
+    public JDORelationship getInverseRelationship() {
+        if (inverse == null) {
+            inverse = super.getInverseRelationship();
+        }
+        return inverse;
+    }
+
     /**
      * Determines whether the values of the elements should be stored 
      * if possible as part of the instance instead of as their own instances 

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOCollectionImplCaching.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOCollectionImplCaching.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOCollectionImplCaching.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOCollectionImplCaching.java Mon Jun 27 09:56:09 2005
@@ -17,6 +17,7 @@
 package org.apache.jdo.impl.model.jdo.caching;
 
 import org.apache.jdo.impl.model.jdo.JDOCollectionImplDynamic;
+import org.apache.jdo.model.jdo.JDORelationship;
 import org.apache.jdo.model.java.JavaType;
 
 /**
@@ -27,10 +28,37 @@
  *
  * @author Michael Bouschen
  * @since 1.1
- * @version 1.1
+ * @version 2.0
  */
 public class JDOCollectionImplCaching extends JDOCollectionImplDynamic {
-    
+
+    /** 
+     * Get the mappedBy relationship. If there is no mappedBy relationship
+     * set, the method checks the mappedBy name as specified in the declaring
+     * field and resolves the relationship. The method returns
+     * <code>null</code> if there is no mappedBy relationship set and there
+     * is no mappedBy name specified on the declaring field.
+     * @return the mappedBy relationship if available; <code>null</code>
+     * otherwise.
+     */
+    public JDORelationship getMappedBy() {
+        if (mappedBy == null) {
+            mappedBy = super.getMappedBy();
+        }
+        return mappedBy;
+    }
+
+    /**
+     * Get the inverse JDORelationship in the case of a two-way relationship.
+     * @return the inverse relationship
+     */
+    public JDORelationship getInverseRelationship() {
+        if (inverse == null) {
+            inverse = super.getInverseRelationship();
+        }
+        return inverse;
+    }
+
     /**
      * Determines whether the values of the elements should be stored if 
      * possible as part of the instance instead of as their own instances 

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOFieldImplCaching.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOFieldImplCaching.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOFieldImplCaching.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOFieldImplCaching.java Mon Jun 27 09:56:09 2005
@@ -213,6 +213,19 @@
     //========= Internal helper methods ==========
 
     /**
+     * Creates and returns a new JDOReference instance. 
+     * This method automatically sets this JDOField as the declaring field of 
+     * the returned instance.
+     * @return a new JDOReference instance bound to this JDOField
+     */
+    protected JDOReference createJDOReferenceInternal() {
+        JDOReferenceImplCaching ref = new JDOReferenceImplCaching();
+        // update relationship JDORelationship->JDOField
+        ref.setDeclaringField(this);
+        return ref;
+    }
+
+    /**
      * Creates and returns a new JDOCollection instance. 
      * This method automatically this JDOField as the declarinmg field of 
      * the returned instance.

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOMapImplCaching.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOMapImplCaching.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOMapImplCaching.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOMapImplCaching.java Mon Jun 27 09:56:09 2005
@@ -17,6 +17,7 @@
 package org.apache.jdo.impl.model.jdo.caching;
 
 import org.apache.jdo.model.java.JavaType;
+import org.apache.jdo.model.jdo.JDORelationship;
 import org.apache.jdo.impl.model.jdo.JDOMapImplDynamic;
 
 /**
@@ -27,10 +28,37 @@
  *
  * @author Michael Bouschen
  * @since 1.1
- * @version 1.1
+ * @version 2.0
  */
 public class JDOMapImplCaching extends JDOMapImplDynamic {
-    
+
+    /** 
+     * Get the mappedBy relationship. If there is no mappedBy relationship
+     * set, the method checks the mappedBy name as specified in the declaring
+     * field and resolves the relationship. The method returns
+     * <code>null</code> if there is no mappedBy relationship set and there
+     * is no mappedBy name specified on the declaring field.
+     * @return the mappedBy relationship if available; <code>null</code>
+     * otherwise.
+     */
+    public JDORelationship getMappedBy() {
+        if (mappedBy == null) {
+            mappedBy = super.getMappedBy();
+        }
+        return mappedBy;
+    }
+
+    /**
+     * Get the inverse JDORelationship in the case of a two-way relationship.
+     * @return the inverse relationship
+     */
+    public JDORelationship getInverseRelationship() {
+        if (inverse == null) {
+            inverse = super.getInverseRelationship();
+        }
+        return inverse;
+    }    
+
     /**
      * Determines whether the keys of the map should be stored if possible as 
      * part of the instance instead of as their own instances in the datastore.

Added: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOReferenceImplCaching.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOReferenceImplCaching.java?rev=202008&view=auto
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOReferenceImplCaching.java (added)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/caching/JDOReferenceImplCaching.java Mon Jun 27 09:56:09 2005
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package org.apache.jdo.impl.model.jdo.caching;
+
+import org.apache.jdo.model.jdo.JDORelationship;
+import org.apache.jdo.impl.model.jdo.JDOReferenceImplDynamic;
+
+/**
+ * An instance of this class represents the JDO relationship metadata 
+ * of a reference relationship field. This caching implementation
+ * caches any calulated value to avoid re-calculating it if it is
+ * requested again. 
+ *
+ * @author Michael Bouschen
+ * @since 2.0
+ * @version 2.0
+ */
+public class JDOReferenceImplCaching extends JDOReferenceImplDynamic {
+    
+    /** 
+     * Get the mappedBy relationship. If there is no mappedBy relationship
+     * set, the method checks the mappedBy name as specified in the declaring
+     * field and resolves the relationship. The method returns
+     * <code>null</code> if there is no mappedBy relationship set and there
+     * is no mappedBy name specified on the declaring field.
+     * @return the mappedBy relationship if available; <code>null</code>
+     * otherwise.
+     */
+    public JDORelationship getMappedBy() {
+        if (mappedBy == null) {
+            mappedBy = super.getMappedBy();
+        }
+        return mappedBy;
+    }
+
+    /**
+     * Get the inverse JDORelationship in the case of a two-way relationship.
+     * @return the inverse relationship
+     */
+    public JDORelationship getInverseRelationship() {
+        if (inverse == null) {
+            inverse = super.getInverseRelationship();
+        }
+        return inverse;
+    }
+}

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/util/PrintSupport.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/util/PrintSupport.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/util/PrintSupport.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/jdo/util/PrintSupport.java Mon Jun 27 09:56:09 2005
@@ -153,6 +153,7 @@
         //println(indent+1, "typeName            = " + jdoField.getTypeName()); //NOI18N
         println(indent+1, "javaField           = " + jdoField.getJavaField()); //NOI18N
         println(indent+1, "serializable        = " + jdoField.isSerializable()); //NOI18N
+        println(indent+1, "mappedByName        = " + jdoField.getMappedByName()); //NOI18N
         println(indent+1, "fieldNumber         = " + jdoField.getFieldNumber()); //NOI18N
         println(indent+1, "relativeFieldNumber = " + jdoField.getRelativeFieldNumber()); //NOI18N
         println(indent+1, "isProperty          = " + jdoField.isProperty()); //NOI18N
@@ -185,9 +186,14 @@
         if (jdoRelationship == null)
             return;
 
+        JDORelationship mappedBy = jdoRelationship.getMappedBy();
         JDORelationship inverse = jdoRelationship.getInverseRelationship();
         println(indent+1, "declaringField  = " + jdoRelationship.getDeclaringField().getName()); //NOI18N
         println(indent+1, "bounds          = " + jdoRelationship.getLowerBound() + " / " +  jdoRelationship.getUpperBound()); //NOI18N
+        println(indent+1, "mappedBy        = " + ((mappedBy==null) ? "null" : //NOI18N
+            mappedBy.getDeclaringField().getDeclaringClass().getName() + "." + //NOI18N
+            mappedBy.getDeclaringField().getName()));
+        println(indent+1, "inverseName     = " + jdoRelationship.getInverseRelationshipName());
         println(indent+1, "inverse         = " + ((inverse==null) ? "null" : //NOI18N
             inverse.getDeclaringField().getDeclaringClass().getName() + "." + //NOI18N
             inverse.getDeclaringField().getName()));

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDOField.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDOField.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDOField.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDOField.java Mon Jun 27 09:56:09 2005
@@ -140,6 +140,28 @@
     public void setSerializable(boolean serializable)
         throws ModelException;
 
+    /** 
+     * Get the name of the field specified in a mappedBy attribute in the
+     * metadata. The method returns <code>null</code> if the metadata for this
+     * field does not specify the mappedBy attribute.  Note that this 
+     * can be provided at the field level to help population of the model, 
+     * but should only be specified on a field that has a corresponding
+     * relationship.
+     * @return the mappedBy field name if available; <code>null</code>
+     * otherwise.
+     */
+    public String getMappedByName();
+
+    /**
+     * Set the name of the field specified in a mappedBy attribute in the
+     * metadata.  Note that this can be provided at the field level to 
+     * help population of the model, but should only be specified on a 
+     * field that has a corresponding relationship.
+     * @param mappedByName the mappedBy field name.
+     * @exception ModelException if impossible
+     */
+    public void setMappedByName(String mappedByName) throws ModelException;
+
     /**
      * Get the corresponding Java field representation for this JDOField.
      * @return the corresponding Java field representation

Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDORelationship.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDORelationship.java?rev=202008&r1=202007&r2=202008&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDORelationship.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/model/jdo/JDORelationship.java Mon Jun 27 09:56:09 2005
@@ -87,18 +87,70 @@
         throws ModelException;
 
     /**
-     * Get the inverse JDORelationship in the case of a managed relationship.
+     * Get the JDOClass corresponding to the type or element of this 
+     * relationship.
+     * @return the related class
+     */
+    public JDOClass getRelatedJDOClass();
+
+    /** 
+     * Get the mappedBy relationship. If there is no mappedBy relationship
+     * set, the method checks the mappedBy name as specified in the declaring
+     * field and resolves the relationship. The method return
+     * <code>null</code> if there is no mappedBy relationship set and there
+     * is no mappedBy name specified on the declaring field.
+     * @return the mappedBy relationship if available; <code>null</code>
+     * otherwise.
+     */
+    public JDORelationship getMappedBy();
+
+    /**
+     * Set the mappedBy relationship for this relationship. This method
+     * automatically updates the mappedBy name of the declaring field of this
+     * relationship.
+     * @param mappedBy the mappedBy relationship.
+     * @exception ModelException if impossible
+     */
+    public void setMappedBy(JDORelationship mappedBy) throws ModelException;
+
+    /** 
+     * Get the relative name of the inverse relationship field for this
+     * relationship.  In the case of two-way relationships, the two
+     * relationships involved are inverses of each other.  If this
+     * relationship element does not participate in a two-way relationship,
+     * this returns <code>null</code>.  Note that it is possible to have
+     * this method return a value, but because of the combination of
+     * related class and lookup, there may be no corresponding
+     * JDORelationship which can be found.
+     * @return the relative name of the inverse JDORelationship
+     * @see #getInverseRelationship
+     */
+    public String getInverseRelationshipName();
+
+    /**
+     * Get the inverse JDORelationship in the case of a two-way relationship.
      * @return the inverse relationship
      */
     public JDORelationship getInverseRelationship();
 
     /**
-     * Set the inverse JDORelationship in the case of a managed relationship.
+     * Set the inverse JDORelationship in the case of a two-way relationship.
+     * The two relationship elements involved are set as inverses of each 
+     * other and the old inverse is unset.
      * @param inverseRelationship the inverse relationship
      * @exception ModelException if impossible
+     * @deprecated - call setMappedBy instead
      */
     public void setInverseRelationship(JDORelationship inverseRelationship)
         throws ModelException;
+
+    /**
+     * Determines whether this side of a two-way relationship is the
+     * owning side.
+     * @return <code>true</code> if this side is the owning side;
+     * <code>false</code> otherwise. 
+     */
+    public boolean isOwner();
 
     /**
      * Determines whether this JDORelationship represents a reference



Mime
View raw message