You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2008/08/14 22:52:17 UTC
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/...
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;
/**