openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ppod...@apache.org
Subject svn commit: r686037 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ openjpa-jdbc/src/main/java/org/...
Date Thu, 14 Aug 2008 20:52:17 GMT
Author: ppoddar
Date: Thu Aug 14 13:52:16 2008
New Revision: 686037

URL: http://svn.apache.org/viewvc?rev=686037&view=rev
Log:
OPENJPA-693: Support 1.5 directional mapping where Parent maps its Children but Child refers
to Parent via Parent's primary key value. Introduces a concept of implicit ForeignKey that
marks the shared mapped column without any database schema impact.

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/Child.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/IParent.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAppIdentity.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAutoIdentity.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithSequenceIdentity.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/TestOneSidedParentChildWithImplicitForeignKey.java
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerStrategies.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/PrimitiveFieldStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ForeignKey.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java?rev=686037&r1=686036&r2=686037&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java
(original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java
Thu Aug 14 13:52:16 2008
@@ -71,6 +71,7 @@
     private boolean _canIdx = true;
     private boolean _canUnq = true;
     private boolean _canFK = true;
+    private boolean _implicitRelation = false;
     private int _join = JOIN_NONE;
     private ColumnIO _io = null;
 
@@ -130,6 +131,30 @@
         _canIdx = indexable;
     }
 
+    /** 
+	 *  Affirms if this instance represents an implicit relation. For example, a 
+	 *  relation expressed as the value of primary key of the related class and 
+	 *  not as object reference.
+     *
+     * @since 1.3.0
+     */
+    public boolean isImplicitRelation() {
+    	return _implicitRelation;
+    }
+    
+    /**
+     * Sets a marker to imply a logical relation that can not have any physical
+     * manifest in the database. For example, a relation expressed as the value
+     * of primary key of the related class and not as object reference.
+     * Populated from @ForeignKey(implicit=true) annotation.
+     * The mutator can only transit from false to true but not vice versa.
+     * 
+     * @since 1.3.0
+     */
+    public void setImplicitRelation(boolean flag) {
+    	_implicitRelation |= flag;
+    }
+
     /**
      * Raw foreign key information.
      */
@@ -280,7 +305,7 @@
             else
                 _canFK = info.canForeignKey();
         }
-
+        _implicitRelation = info.isImplicitRelation();
         List cols = getColumns();
         List icols = info.getColumns();
         if (!icols.isEmpty() && (cols.isEmpty()
@@ -386,10 +411,11 @@
     }
 
     /**
-     * Assert that the user did not try to place a foreign key on this mapping.
+     * Assert that the user did not try to place a foreign key on this mapping
+     * or placed an implicit foreign key. 
      */
     public void assertNoForeignKey(MetaDataContext context, boolean die) {
-        if (_fk == null)
+        if (_fk == null || isImplicitRelation())
             return;
 
         Message msg = _loc.get("unexpected-fk", context);
@@ -610,6 +636,7 @@
         String defStr = tmplate.getDefaultString();
         boolean autoAssign = tmplate.isAutoAssigned();
         boolean relationId = tmplate.isRelationId();
+        boolean implicitRelation = tmplate.isImplicitRelation();
         String targetField = tmplate.getTargetField();
         if (given != null) {
             // use given type if provided, but warn if it isn't compatible with
@@ -640,6 +667,8 @@
                 autoAssign = true;
             if (given.isRelationId())
                 relationId = true;
+            if (given.isImplicitRelation())
+            	implicitRelation = true;
         }
 
         // default char column size if original type is char (test original
@@ -684,6 +713,7 @@
         }
         col.setAutoAssigned(autoAssign);
         col.setRelationId(relationId);
+        col.setImplicitRelation(implicitRelation);
         col.setTargetField(targetField);
 
         // we need this for runtime, and the dynamic schema factory might

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerStrategies.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerStrategies.java?rev=686037&r1=686036&r2=686037&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerStrategies.java
(original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerStrategies.java
Thu Aug 14 13:52:16 2008
@@ -62,6 +62,9 @@
         if (cols.length > 0 && cols[0].getTable() == null) {
             cols = vinfo.getColumns(vm, name, cols,
                 vm.getFieldMapping().getTable(), adapt);
+            if (vinfo.isImplicitRelation())
+            	for (int i = 0; i < cols.length; i++)
+            		cols[i].setImplicitRelation(true);
             ColumnIO mappedIO = vinfo.getColumnIO();
             vm.setColumns(cols);
             vm.setColumnIO(mappedIO);
@@ -138,7 +141,7 @@
     }
 
     /**
-     * Set a value into a row, taking care not to override column defualts
+     * Set a value into a row, taking care not to override column defaults
      * with nulls unless the user wants us to.
      */
     private static void set(Row row, Column col, Object val,

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/PrimitiveFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/PrimitiveFieldStrategy.java?rev=686037&r1=686036&r2=686037&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/PrimitiveFieldStrategy.java
(original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/PrimitiveFieldStrategy.java
Thu Aug 14 13:52:16 2008
@@ -84,7 +84,9 @@
             new Column[]{ tmpCol }, field.getTable(), adapt);
         if (field.getValueStrategy() == ValueStrategies.AUTOASSIGN)
             cols[0].setAutoAssigned(true);
-
+        if (vinfo.isImplicitRelation())
+        	for (int i = 0; i < cols.length; i++)
+        		cols[i].setImplicitRelation(true);
         field.setColumns(cols);
         field.setColumnIO(vinfo.getColumnIO());
         field.mapConstraints(field.getName(), adapt);

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java?rev=686037&r1=686036&r2=686037&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java Thu
Aug 14 13:52:16 2008
@@ -65,6 +65,7 @@
     private Boolean _notNull = null;
     private boolean _autoAssign = false;
     private boolean _rel = false;
+    private boolean _implicitRelation = false;
     private String _target = null;
     private String _targetField = null;
     private int _flags = 0;
@@ -712,6 +713,8 @@
             setAutoAssigned(from.isAutoAssigned());
         if (!isRelationId())
             setRelationId(from.isRelationId());
+        if (!isImplicitRelation())
+        	setImplicitRelation(from.isRelationId());
         if (getTarget() == null)
             setTarget(from.getTarget());
         if (getTargetField() == null)
@@ -746,4 +749,28 @@
     public void setComment(String comment) {
         _comment = comment;
     }
+    
+    /** 
+	 *  Affirms if this instance represents an implicit relation. For example, a 
+	 *  relation expressed as the value of primary key of the related class and 
+	 *  not as object reference.
+     *
+     * @since 1.3.0
+     */
+    public boolean isImplicitRelation() {
+    	return _implicitRelation;
+    }
+    
+    /**
+     * Sets a marker to imply a logical relation that can not have any physical
+     * manifest in the database. For example, a relation expressed as the value
+     * of primary key of the related class and not as object reference.
+     * Populated from @ForeignKey(implicit=true) annotation.
+     * The mutator can only transit from false to true but not vice versa.
+     * 
+     * @since 1.3.0
+     */
+    public void setImplicitRelation(boolean flag) {
+    	_implicitRelation |= flag;
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java?rev=686037&r1=686036&r2=686037&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PrimaryRow.java Thu
Aug 14 13:52:16 2008
@@ -331,20 +331,40 @@
         boolean overrideDefault)
         throws SQLException {
         // make sure we're not setting two different values
+    	// unless the given column is an implicit relationship and value
+    	// changes from logical default to non-default
         Object prev = getSet(col);
         if (prev != null) {
             if (prev == NULL)
                 prev = null;
             if (!rowValueEquals(prev, val)) {
-                throw new InvalidStateException(_loc.get("diff-values",
-                    new Object[]{ col.getFullName(),
-                        (prev == null) ? null : prev.getClass(), prev,
-                        (val == null) ? null : val.getClass(), val })).
-                    setFatal(true);
+            	if (allowsUpdate(col, prev, val)) {
+            		super.setObject(col, val, metaType, overrideDefault);
+            	} else if (!isDefaultValue(val)) {
+            		throw new InvalidStateException(_loc.get("diff-values",
+            				new Object[]{ col.getFullName(),
+            				(prev == null) ? null : prev.getClass(), prev,
+            				(val == null) ? null : val.getClass(), val })).
+            				setFatal(true);
+            	}
             }
         }
         super.setObject(col, val, metaType, overrideDefault);
     }
+    
+    /**
+     * Allow the given column value to be updated only if old or current value
+     * is a default value or was not set and the column is not a primary key.
+     */
+    boolean allowsUpdate(Column col, Object old, Object cur) {
+    	return !col.isPrimaryKey() && col.isImplicitRelation()
+    	   && (isDefaultValue(old));
+    }
+    
+    boolean isDefaultValue(Object val) {
+    	return val == null || val == NULL
+    	    || (val instanceof Number && ((Number)val).longValue() == 0);
+    }
 
     /**
      * Return true if the two values should be considered equal.

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java?rev=686037&r1=686036&r2=686037&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
(original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
Thu Aug 14 13:52:16 2008
@@ -682,8 +682,13 @@
      * Parse the given foreign key.
      */
     private void parseForeignKey(MappingInfo info, ForeignKey fk) {
-        parseForeignKey(info, fk.name(), fk.enabled(), fk.deferred(),
-            fk.deleteAction(), fk.updateAction());
+    	if (!fk.implicit()) {
+    		parseForeignKey(info, fk.name(), fk.enabled(), fk.deferred(),
+    				fk.deleteAction(), fk.updateAction());
+    	} else {
+            info.setImplicitRelation(true);
+            assertDefault(fk);
+    	}
     }
 
     /**
@@ -706,6 +711,20 @@
         fk.setUpdateAction(toForeignKeyAction(updateAction));
         info.setForeignKey(fk);
     }
+    
+    void assertDefault(ForeignKey fk) {
+    	boolean isDefault = StringUtils.isEmpty(fk.name()) 
+    		&& fk.enabled() 
+    		&& !fk.deferred() 
+    		&& fk.deleteAction() == ForeignKeyAction.RESTRICT
+    		&& fk.updateAction() == ForeignKeyAction.RESTRICT
+    		&& fk.columnNames().length == 0
+    		&& fk.specified();
+    	if (!isDefault)
+    		throw new UserException(_loc.get("implicit-non-default-fk", _cls, 
+    				getSourceFile()).getMessage());
+    }
+    
 
     /**
      * Convert our FK action enum to an internal OpenJPA action.

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ForeignKey.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ForeignKey.java?rev=686037&r1=686036&r2=686037&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ForeignKey.java
(original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ForeignKey.java
Thu Aug 14 13:52:16 2008
@@ -47,4 +47,6 @@
     String[] columnNames() default {};
 
     boolean specified() default true;
+    
+    boolean implicit() default false;
 }

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties?rev=686037&r1=686036&r2=686037&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties
(original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties
Thu Aug 14 13:52:16 2008
@@ -56,4 +56,7 @@
 discriminator-on-abstract-class: A discriminator value has been specified for \
 	the abstract class "{0}". The discriminator will never be used and may be \
 	safely removed.
-    
\ No newline at end of file
+implicit-non-default-fk: While parsing "{0}" from "{1}", found a @ForeignKey \
+	with implicit attribute set to true but one or more other attributes of \
+	ForeignKey is set to their non-default value. You can not specify any \
+	non-default value for an implicit ForeignKey.  	    
\ No newline at end of file

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/Child.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/Child.java?rev=686037&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/Child.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/Child.java
Thu Aug 14 13:52:16 2008
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jdbc.mapping.bidi;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.apache.openjpa.meta.ValueStrategies;
+import org.apache.openjpa.persistence.jdbc.ForeignKey;
+
+/**
+ * Child in a logically bidirectional but actually unidirectional parent-child 
+ * relationship where Child holds reference to Parent via primary key and not 
+ * via object reference.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@Entity
+@Table(name="CHILD_693")
+public class Child {
+	@Id
+	private long id;
+	
+	private String name;
+
+	@Column(name="FK_PARENT_SEQ_ID", nullable=true)
+	@ForeignKey(implicit=true)
+	private long seqParentId;
+	
+	@Column(name="FK_PARENT_AUTO_ID", nullable=true)
+	@ForeignKey(implicit=true)
+	private long autoParentId;
+	
+	@Column(name="FK_PARENT_APP_ID", nullable=true)
+	@ForeignKey(implicit=true)
+	private long appParentId;
+
+	public Child() {
+		
+	}
+	
+	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 long getSeqParentId() {
+		return seqParentId;
+	}
+
+	void setSeqParentId(long parentId) {
+		this.seqParentId = parentId;
+	}
+	
+	public long getAutoParentId() {
+		return autoParentId;
+	}
+
+	void setAutoParentId(long parentId) {
+		this.autoParentId = parentId;
+	}
+	public long getAppParentId() {
+		return appParentId;
+	}
+
+	void setAppParentId(long parentId) {
+		this.appParentId = parentId;
+	}
+	
+	public long getParentIdType(int idType) {
+		switch (idType) {
+		case ValueStrategies.NONE : return getAppParentId();
+		case ValueStrategies.AUTOASSIGN : return getAutoParentId();
+		case ValueStrategies.SEQUENCE : return getSeqParentId();
+		default :
+			throw new IllegalArgumentException("No parent with id strategy " + 
+					idType);
+		}
+	}
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/IParent.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/IParent.java?rev=686037&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/IParent.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/IParent.java
Thu Aug 14 13:52:16 2008
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jdbc.mapping.bidi;
+
+import java.util.Collection;
+
+public interface IParent {
+	public long getId();
+	public void setId(long id);
+	public String getName();
+	public void setName(String name);
+	public Collection<Child> getChildren();
+	public void addChild(Child child);
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAppIdentity.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAppIdentity.java?rev=686037&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAppIdentity.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAppIdentity.java
Thu Aug 14 13:52:16 2008
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jdbc.mapping.bidi;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
+import javax.persistence.PreUpdate;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import org.apache.openjpa.persistence.jdbc.ElementJoinColumn;
+
+/**
+ * Parent in a logically bidirectional but actually unidirectional parent-child 
+ * relationship where Child holds reference to Parent via primary key and not 
+ * via object reference.
+ * Parent identity is assigned by the application. Hence, Parent sets the 
+ * children's reference to Parent whenever its identity is set by the 
+ * application or a new child is added. 
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@Entity
+public class ParentWithAppIdentity implements IParent {
+	@Id
+	private long id;
+	
+	private String name;
+	
+	/**
+	 * This field is <em>not</em> mapped by the child. The child's table will 
+	 * hold an <em>implicit</em> foreign key linking to the primary key of this

+	 * Parent's table. 
+	 */
+	@OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
+	@ElementJoinColumn(name="FK_PARENT_APP_ID", referencedAttributeName="id")
+	private Set<Child> children;
+
+	public long getId() {
+		return id;
+	}
+	
+	public void setId(long id) {
+		this.id = id;
+		postIdSet();
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Collection<Child> getChildren() {
+		return children;
+	}
+
+	public void addChild(Child child) {
+		if (children == null)
+			children = new HashSet<Child>();
+		children.add(child);
+		child.setAppParentId(this.id);
+	}
+	
+	public boolean removeChild(Child child) {
+		return children != null && children.remove(child);
+	}
+	
+	/**
+	 * This method will be called when application has assigned identity
+	 * to this instance.
+	 */
+	public void postIdSet() {
+		if (children == null)
+			return;
+		for (Child child : children) {
+			child.setAppParentId(this.getId());
+		}
+	}
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAutoIdentity.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAutoIdentity.java?rev=686037&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAutoIdentity.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithAutoIdentity.java
Thu Aug 14 13:52:16 2008
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jdbc.mapping.bidi;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
+import javax.persistence.PreUpdate;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import org.apache.openjpa.persistence.jdbc.ElementJoinColumn;
+
+/**
+ * Parent in a logically bidirectional but actually unidirectional parent-child 
+ * relationship where Child holds reference to Parent via primary key and not 
+ * via object reference.
+ * Also identity for Parent is generated by the persistence provider. Hence, 
+ * Parent sets the children's reference to Parent in PostPersist callback.  
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@Entity
+public class ParentWithAutoIdentity implements IParent {
+	@Id
+	@GeneratedValue(strategy=GenerationType.AUTO)
+	private long id;
+	
+	private String name;
+	
+	/**
+	 * This field is <em>not</em> mapped by the child. The child's table will 
+	 * hold an <em>implicit</em> foreign key linking to the primary key of this

+	 * Parent's table. 
+	 */
+	@OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
+	@ElementJoinColumn(name="FK_PARENT_AUTO_ID", referencedAttributeName="id")
+	private Set<Child> children;
+
+	public long getId() {
+		return id;
+	}
+	
+	public void setId(long id) {
+		throw new RuntimeException(getClass() + ".setId() is not to be " +
+			"invoked directly. This class is using AUTO Generation Starategy");
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Collection<Child> getChildren() {
+		return children;
+	}
+
+	public void addChild(Child child) {
+		if (children == null)
+			children = new HashSet<Child>();
+		children.add(child);
+	}
+	
+	public boolean removeChild(Child child) {
+		return children != null && children.remove(child);
+	}
+	
+	/**
+	 * This method will be called back after database has assigned identity
+	 * to this instance.
+	 */
+	@PreUpdate
+	@PostPersist
+	public void postPersist() {
+		if (children == null)
+			return;
+		for (Child child : children) {
+			child.setAutoParentId(this.getId());
+		}
+	}
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithSequenceIdentity.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithSequenceIdentity.java?rev=686037&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithSequenceIdentity.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/ParentWithSequenceIdentity.java
Thu Aug 14 13:52:16 2008
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jdbc.mapping.bidi;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
+import javax.persistence.PreUpdate;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import org.apache.openjpa.persistence.jdbc.ElementJoinColumn;
+
+/**
+ * Parent in a logically bidirectional but actually unidirectional parent-child 
+ * relationship where Child holds reference to Parent via primary key and not 
+ * via object reference.
+ * Also database assigns identity for Parent. Hence, Parent sets the children's
+ * reference to Parent in PostPersist callback.  
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@Entity
+public class ParentWithSequenceIdentity  implements IParent {
+	@Id
+	@GeneratedValue(strategy=GenerationType.SEQUENCE)
+	private long id;
+	
+	private String name;
+	
+	/**
+	 * This field is <em>not</em> mapped by the child. The child's table will 
+	 * hold an <em>implicit</em> foreign key linking to the primary key of this

+	 * Parent's table. 
+	 */
+	@OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
+	@ElementJoinColumn(name="FK_PARENT_SEQ_ID", referencedAttributeName="id")
+	private Set<Child> children;
+
+	public long getId() {
+		return id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setId(long id) {
+		throw new RuntimeException(getClass() + ".setId() is not to be " +
+			"invoked directly. This class is using SEQ Generation Starategy");
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Collection<Child> getChildren() {
+		return children;
+	}
+
+	public void addChild(Child child) {
+		if (children == null)
+			children = new HashSet<Child>();
+		children.add(child);
+	}
+	
+	public boolean removeChild(Child child) {
+		return children != null && children.remove(child);
+	}
+	
+	/**
+	 * This method will be called back after database has assigned identity
+	 * to this instance.
+	 */
+	@PreUpdate
+	@PostPersist
+	public void postPersist() {
+		if (children == null)
+			return;
+		for (Child child : children) {
+			child.setSeqParentId(this.getId());
+		}
+	}
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/TestOneSidedParentChildWithImplicitForeignKey.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/TestOneSidedParentChildWithImplicitForeignKey.java?rev=686037&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/TestOneSidedParentChildWithImplicitForeignKey.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/bidi/TestOneSidedParentChildWithImplicitForeignKey.java
Thu Aug 14 13:52:16 2008
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jdbc.mapping.bidi;
+
+import javax.persistence.EntityManager;
+
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.meta.ValueStrategies;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/**
+ * Tests behavior of Parent-Child mapping under following conditions a) Parent
+ * has many references to Child b) Child refers to Parent by Parent's identity
+ * and not by object reference c) Parent's identity is assigned by the database
+ * d) PostPersist callback in Parent sets the children's reference to Parent
+ * 
+ * The use case was originally reported in <A
+ * HREF="http://n2.nabble.com/OpenJPA---two-sided-relation-between-objects-Issue-tc687050.html">
+ * OpenJPA User Forum</A>
+ * 
+ * @author Pinaki Poddar
+ */
+public class TestOneSidedParentChildWithImplicitForeignKey extends
+		SingleEMFTestCase {
+	private EntityManager em;
+	private static Class[] PARENT_ID_TYPES = { 
+		ParentWithAppIdentity.class,      // ValueStrategies.NONE = 0
+		ParentWithSequenceIdentity.class, // ValueStrategies.SEQUENCE = 2
+		ParentWithAutoIdentity.class,     // ValueStrategies.AUTOASSIGN = 3
+	};
+	private static int[] VALUE_STRATEGIES = { 
+		ValueStrategies.NONE,
+		ValueStrategies.SEQUENCE, 
+		ValueStrategies.AUTOASSIGN };
+	
+	private static long[] PARENT_IDS = new long[PARENT_ID_TYPES.length];
+
+	private static long PARENT_ID_COUNTER = System.currentTimeMillis();
+	private static long CHILD_ID_COUNTER = System.currentTimeMillis();
+	private static int CHILD_COUNT = 3;
+
+	public void setUp() {
+		setUp(DROP_TABLES, ParentWithAppIdentity.class,
+				ParentWithSequenceIdentity.class, ParentWithAutoIdentity.class,
+				Child.class);
+		em = emf.createEntityManager();
+		createData(CHILD_COUNT);
+	}
+
+	public void xtestStrategies() {
+		MetaDataRepository repos = emf.getConfiguration()
+				.getMetaDataRepositoryInstance();
+		for (int i = 0; i < VALUE_STRATEGIES.length; i++) {
+			ClassMetaData meta = repos.getMetaData(PARENT_ID_TYPES[i], null,
+					true);
+			FieldMetaData fmd = meta.getPrimaryKeyFields()[0];
+			assertEquals(fmd + " strategy is " + fmd.getValueStrategy(),
+					VALUE_STRATEGIES[i], fmd.getValueStrategy());
+		}
+	}
+
+	void createData(int nChild) {
+		em.getTransaction().begin();
+		
+		Child[] children = new Child[CHILD_COUNT];
+		for (int j = 0; j < CHILD_COUNT; j++) {
+			Child child = new Child();
+			child.setId(CHILD_ID_COUNTER++);
+			child.setName("Child" + j);
+			children[j] = child;
+		}
+		
+		for (int i = 0; i < PARENT_ID_TYPES.length; i++) {
+			IParent parent = newParent(i);
+			if (VALUE_STRATEGIES[i] == ValueStrategies.NONE)
+				parent.setId(++PARENT_ID_COUNTER);
+			for (int j = 0; j < CHILD_COUNT; j++) {
+				parent.addChild(children[j]);
+			}
+			em.persist(parent);
+			em.flush();
+			PARENT_IDS[i] = parent.getId();
+		}
+
+		em.getTransaction().commit();
+	}
+
+	public void testPersist() {
+		em.getTransaction().begin();
+
+		for (int i = 0; i < PARENT_ID_TYPES.length; i++) {
+			IParent parent = findParent(i);
+			assertFalse(parent.getId() == 0);
+			assertFalse(parent.getChildren().isEmpty());
+			assertEquals(CHILD_COUNT, parent.getChildren().size());
+			for (Child child : parent.getChildren()) {
+				assertFalse(child.getParentIdType(VALUE_STRATEGIES[i]) == 0);
+				assertTrue(child.getParentIdType(VALUE_STRATEGIES[i]) == parent
+						.getId());
+			}
+		}
+		em.getTransaction().commit();
+	}
+
+	public void testUpdate() {
+		em.getTransaction().begin();
+
+		Child newChild = new Child();
+		newChild.setId(CHILD_ID_COUNTER++);
+		newChild.setName("New Child");
+		for (int i = 0; i < PARENT_ID_TYPES.length; i++) {
+			IParent parent = findParent(i);
+			parent.addChild(newChild);
+			em.merge(parent);
+		}
+		em.flush();
+		em.getTransaction().commit();
+		em.clear();
+
+		em.getTransaction().begin();
+		for (int i = 0; i < PARENT_ID_TYPES.length; i++) {
+			IParent parent = findParent(i);
+			assertFalse(parent.getId() == 0);
+			assertFalse(parent.getChildren().isEmpty());
+			assertEquals(CHILD_COUNT + 1, parent.getChildren().size());
+			for (Child child : parent.getChildren()) {
+				assertFalse(child.getParentIdType(VALUE_STRATEGIES[i]) == 0);
+				assertTrue(child.getParentIdType(VALUE_STRATEGIES[i]) == parent
+						.getId());
+			}
+		}
+		em.getTransaction().commit();
+	}
+
+	public void tearDown() {
+
+	}
+
+	public IParent newParent(int parentType) {
+		try {
+			IParent parent = (IParent)PARENT_ID_TYPES[parentType].newInstance();
+			if (VALUE_STRATEGIES[parentType] == ValueStrategies.NONE)
+				parent.setId(++PARENT_ID_COUNTER);
+			parent.setName(PARENT_ID_TYPES[parentType].getSimpleName());
+			return parent;
+		} catch (Exception e) {
+			fail(e.toString());
+		}
+		return null;
+	}
+	
+	public IParent findParent(int parentType) {
+		return (IParent) em.find(PARENT_ID_TYPES[parentType], 
+				PARENT_IDS[parentType]);
+	}
+}
\ No newline at end of file

Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java?rev=686037&r1=686036&r2=686037&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
(original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
Thu Aug 14 13:52:16 2008
@@ -188,7 +188,7 @@
     private final Map<Package, Integer> _pkgs = new HashMap<Package, Integer>();
 
     // the class we were invoked to parse
-    private Class _cls = null;
+    protected Class _cls = null;
     private File _file = null;
 
     /**



Mime
View raw message