diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java index fbff958b8..2a68f8fe5 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java @@ -64,7 +64,6 @@ public class Compatibility { private boolean _superclassDiscriminatorStrategyByDefault = true; private boolean _isAbstractMappingUniDirectional = false; private boolean _isNonDefaultMappingAllowed = false; - private boolean _reorderMetaDataResolution = true; private boolean _reloadOnDetach = false; private boolean _ignoreDetachedStateFieldForProxySerialization = false; @@ -508,26 +507,6 @@ public class Compatibility { public boolean isNonDefaultMappingAllowed() { return _isNonDefaultMappingAllowed; } - - /** - * Whether OpenJPA should reorder entities in MetaDataRepository.processBuffer() to ensure that the metadata for - * entities with foreign keys in their identity are processed after the entities it depends on. - * - * @return true if the reordering should be performed, false if not. - */ - public boolean getReorderMetaDataResolution() { - return _reorderMetaDataResolution; - } - - /** - * Whether OpenJPA should reorder entities in MetaDataRepository.processBuffer() to ensure that the metadata for - * entities with foreign keys in their identity are processed after the entities it depends on. - * - * @param reorderProcessBuffer true if the reordering should be performed, false if not. - */ - public void setReorderMetaDataResolution(boolean reorderProcessBuffer) { - _reorderMetaDataResolution = reorderProcessBuffer; - } /** * Whether OpenJPA should attempt to load fields when the DetachState diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InheritanceOrderedMetaDataList.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InheritanceOrderedMetaDataList.java deleted file mode 100644 index 79376fb71..000000000 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InheritanceOrderedMetaDataList.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.meta; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.ListIterator; -import java.io.Serializable; - -public class InheritanceOrderedMetaDataList - implements Serializable { - - private MetaDataInheritanceComparator _comp - = new MetaDataInheritanceComparator(); - private LinkedList buffer = new LinkedList(); - - public boolean add(ClassMetaData meta) { - if (meta == null || buffer.contains(meta)) - return false; - for (ListIterator itr = buffer.listIterator(); - itr.hasNext();) { - int ord = _comp.compare(meta, itr.next()); - if (ord > 0) - continue; - if (ord == 0) - return false; - itr.previous(); - itr.add(meta); - return true; - } - buffer.add(meta); - return true; - } - - public boolean remove(ClassMetaData meta) { - return buffer.remove(meta); - } - - public ClassMetaData peek() { - return buffer.peek(); - } - - public int size() { - return buffer.size(); - } - - public Iterator iterator() { - return buffer.iterator(); - } - - public boolean isEmpty() { - return buffer.isEmpty(); - } - - public void clear() { - buffer.clear(); - } -} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java index dae47dfed..de37d2104 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java @@ -141,8 +141,8 @@ public class MetaDataRepository implements PCRegistry.RegisterClassListener, Con private final Collection> _registered = new HashSet>(); // set of metadatas we're in the process of resolving - private final InheritanceOrderedMetaDataList _resolving = new InheritanceOrderedMetaDataList(); - private final InheritanceOrderedMetaDataList _mapping = new InheritanceOrderedMetaDataList(); + private final List _resolving = new ArrayList(); + private final List _mapping = new ArrayList(); private final List _errs = new LinkedList(); // system listeners @@ -152,8 +152,6 @@ public class MetaDataRepository implements PCRegistry.RegisterClassListener, Con protected boolean _preloadComplete = false; protected boolean _locking = true; private static final String PRELOAD_STR = "Preload"; - - private boolean _reorderMetaDataResolution = false; /** * Default constructor. Configure via {@link Configurable}. @@ -770,22 +768,28 @@ public class MetaDataRepository implements PCRegistry.RegisterClassListener, Con /** * Process the given metadata and the associated buffer. */ - private List processBuffer(ClassMetaData meta, InheritanceOrderedMetaDataList buffer, int mode) { + private List processBuffer(ClassMetaData meta, List buffer, int mode) { + // add the metadata to the buffer unless an instance for the same entity + // is already there + for (ClassMetaData cmd : buffer) + if (cmd.getDescribedType().equals(meta.getDescribedType())) + return null; + // if we're already processing a metadata, just buffer this one; when // the initial metadata finishes processing, we traverse the buffer // and process all the others that were introduced during reentrant // calls - if (!buffer.add(meta) || buffer.size() != 1) + buffer.add(meta); + if (buffer.size() != 1) return null; // continually pop a metadata and process it until we run out; note // that each processing call might place more metas in the buffer as - // one class tries to access metadata for another; also note that the - // buffer orders itself from least to most derived + // one class tries to access metadata for another ClassMetaData buffered; List processed = new ArrayList(5); while (!buffer.isEmpty()) { - buffered = buffer.peek(); + buffered = buffer.get(0); try { buffered.resolve(mode); processed.add(buffered); @@ -808,11 +812,6 @@ public class MetaDataRepository implements PCRegistry.RegisterClassListener, Con } } - // Check if process buffer reordering for PCTypes that have relationships to other PCTypes in their identity - // should be performed. - if (_reorderMetaDataResolution) { - processed = resolveFKInPKDependenciesOrdering(processed); - } return processed; } @@ -1844,7 +1843,6 @@ public class MetaDataRepository implements PCRegistry.RegisterClassListener, Con public void setConfiguration(Configuration conf) { _conf = (OpenJPAConfiguration) conf; _log = _conf.getLog(OpenJPAConfiguration.LOG_METADATA); - _reorderMetaDataResolution = _conf.getCompatibilityInstance().getReorderMetaDataResolution(); } public void startConfiguration() { @@ -2413,196 +2411,6 @@ public class MetaDataRepository implements PCRegistry.RegisterClassListener, Con public XMLFieldMetaData newXMLFieldMetaData(Class type, String name) { return new XMLFieldMetaData(type, name); } - - /** - * Analyzes the list of ClassMetaData in the supplied list for any which has foreign keys to other ClassMetaData - * instances in its identity (in other words, PCTypes which have primary keys that are foreign keys to other - * tables), and returns a list arranged so that a ClassMetaData that depends on another ClassMetaData appears - * after it in the list. - * - * @param cmdList - List of ClassMetaData to examine - * @return - List of ClassMetaData, with ClassMetaData dependees moved after the last identified dependent - * ClassMetaData, if any move is necessary. - */ - private List resolveFKInPKDependenciesOrdering(List cmdList) { - HashMap nodeMap = new HashMap(); - HashSet nodesWithDependenciesSet = new HashSet(); - ArrayList nodeList = new ArrayList(cmdList.size()); - - // Initial analysis of ClassMetaData objects -- Populate the linked list with objects in the same order of - // appearance in the original list. Identify CMDs whose identities have a FK to another CMD, and catalog that - // dependency. - for (ClassMetaData cmd : cmdList) { - // Add this node to the list - CMDDependencyNode node = nodeMap.get(cmd); - if (node == null) { - node = new CMDDependencyNode(cmd); - nodeMap.put(cmd, node); - } - nodeList.add(node); - - // Examine its primary key fields, flag any references to another PCType that is defined in cmdList as a - // dependency - FieldMetaData[] fmdArr = cmd.getPrimaryKeyFields(); - for (FieldMetaData fmd : fmdArr) { - ValueMetaData vmd = fmd.getValue(); - if (vmd.isTypePC()) { - ClassMetaData targetCMD = vmd.getDeclaredTypeMetaData(); - - // Only process entries which are in the cmdList, as we don't want to be adding anything new. - if (!cmdList.contains(targetCMD)) { - continue; - } - - // Register the dependency - CMDDependencyNode targetNode = null; - if ((targetNode = nodeMap.get(targetCMD)) == null) { - targetNode = new CMDDependencyNode(targetCMD); - nodeMap.put(targetCMD, targetNode); - } - node.registerDependentNode(targetNode); - nodesWithDependenciesSet.add(node); - } - } - } - - // Analysis is complete. For each CMD that has an identity foreign key dependency on another CMD, ensure that it - // appears later in the list then the CMD it is dependent on. If it appears earlier, move it immediately after - // the CMD. If there are multiple CMDs the identity is dependent on, move it after the last dependency in - // the linked list. - for (CMDDependencyNode node : nodesWithDependenciesSet) { - // Check if there is a cycle (dependencies or subdependencies that create a cycle in the graph. If one is - // detected, then this algorithm cannot be used to reorder the CMD list. Emit a warning, and return the - // original list. - if (node.checkForCycle()) { - if (_log.isWarnEnabled()) { - _log.warn(_loc.get("cmd-discover-cycle", node.getCmd().getResourceName())); - } - return cmdList; - } - - int nodeIndex = nodeList.indexOf(node); - Set dependencies = node.getDependsOnSet(); - - // If the current node has a dependency that appears later in the list, then this node needs - // to be moved to the point immediately after that dependency. - CMDDependencyNode moveAfter = null; - int moveAfterIndex = -1; - for (CMDDependencyNode depNode : dependencies) { - int dependencyIndex = nodeList.indexOf(depNode); - if ((nodeIndex < dependencyIndex) && (moveAfterIndex < dependencyIndex)) { - moveAfter = depNode; - moveAfterIndex = dependencyIndex; - } - } - if (moveAfter != null) { - nodeList.remove(nodeIndex); - nodeList.add(nodeList.indexOf(moveAfter) + 1, node); - } - } - - // Sorting is complete, build the return list. Clear the dependsOnSet for the GC. - ArrayList returnList = new ArrayList(); - for (CMDDependencyNode current : nodeList) { - returnList.add(current.getCmd()); - current.getDependsOnSet().clear(); - } - - return returnList; - } - - - /** - * Linked list node class for managing any foreign keys in the identity of a ClassMetaData instance. - * - */ - private class CMDDependencyNode { - private ClassMetaData cmd; - - // Marker for quick determination if this node has dependencies - private boolean hasDependencies = false; - - // List of ClassMetaData objects this ClassMetaData depends on - private HashSet dependsOnSet = new HashSet(); - - /** - * Inner class constructor - */ - CMDDependencyNode(ClassMetaData cmd) { - this.cmd = cmd; - } - - /** - * Returns the ClassMetaData instance referenced by this node. - */ - public ClassMetaData getCmd() { - return cmd; - } - - /** - * - * @return true if this node's ClassMetaData has a FK in its identity that refers to another ClassMetaData; - * false if it does not. - */ - public boolean getHasDependencies() { - return hasDependencies; - } - - /** - * Registers a ClassMetaData modelled by a CMDDependencyNode as a dependency of this ClassMetaData. - * - */ - public void registerDependentNode(CMDDependencyNode node) { - getDependsOnSet().add(node); - hasDependencies = true; - } - - /** - * Returns a Set containing all of the CMDDependencyNode instances that this node has a FK in identity - * dependency on. - * - */ - public Set getDependsOnSet() { - return dependsOnSet; - } - - /** - * Checks all dependencies, and sub-dependencies, for any cycles in the dependency graph. - * - * @return true if a cycle was discovered, false if not. - */ - public boolean checkForCycle() { - java.util.Stack visitStack = new java.util.Stack(); - return internalCheckForCycle(visitStack); - } - - /** - * Internal implementation of the cycle detection. - * - * @param visitStack - * @return true if a cycle is detected, false if no cycle was detected. - */ - private boolean internalCheckForCycle(java.util.Stack visitStack) { - if (visitStack.contains(this)) { - return true; - } - visitStack.push(this); - - try { - for (CMDDependencyNode node : dependsOnSet) { - if (node.getHasDependencies()) { - if (node.internalCheckForCycle(visitStack) == true) { - return true; - } - } - } - } finally { - visitStack.pop(); - } - - return false; - } - } public static boolean needsPreload(Options o) { if (o.getBooleanProperty(PRELOAD_STR) == true) { diff --git a/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties b/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties index 0819deec3..60991e158 100644 --- a/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties +++ b/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties @@ -348,9 +348,6 @@ repos-preload-none: No persistent metadata found for loading during initializati repos-preloading: Following metadata are being loaded during initialization by "{0}": {1}. repos-preload-error: Unexpected error during early loading during initialization. \ See nested stacktrace for details. -cmd-discover-cycle: A cycle was detected while resolving the identity \ - references for type "{0}". The original process buffer ordering \ - will be used. repos-initializeEager-none: No persistent metadata found for loading during initialization. \ The persistent classes must be listed in persistence unit configuration to be loaded during initialization. repos-initializeEager-found: The following classes are being preloaded "{0}". diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java index 0b1f5be8a..413330233 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java @@ -28,9 +28,7 @@ import org.apache.openjpa.persistence.test.SingleEMFTestCase; public class TestEntityAsIdentityFields extends SingleEMFTestCase { public void setUp() { - setUp( - Account.class, AccountGroup.class, Person.class, - "openjpa.Compatibility", "reorderMetaDataResolution=true"); + setUp(Account.class, AccountGroup.class, Person.class); } /** diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Attendance.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Attendance.java new file mode 100644 index 000000000..e322cc5a5 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Attendance.java @@ -0,0 +1,65 @@ +/* + * 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.identity.entityasidentity2; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "EAI2Attendance") +@IdClass(Attendance.AttendanceId.class) +public class Attendance { + + public static class AttendanceId { + + private int student; + private int course; + + public AttendanceId() {} + + public AttendanceId(int studentId, int courseId) { + this.student = studentId; + this.course = courseId; + } + + public String toString() { + return student + ":" + course; + } + + public int hashCode() { + return (17 + student) * 37 + course; + } + + public boolean equals(Object other) { + return this == other + || other instanceof AttendanceId + && student == ((AttendanceId) other).student + && course == ((AttendanceId) other).course; + } + } + + @Id @ManyToOne + Student student; + + @Id @ManyToOne + Course course; +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Course.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Course.java new file mode 100644 index 000000000..3731c68b8 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Course.java @@ -0,0 +1,37 @@ +/* + * 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.identity.entityasidentity2; + +import java.util.Collection; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +@Entity +@Table(name = "EAI2Course") +public class Course { + + @Id + int id; + + @OneToMany(mappedBy = "course") + Collection attendances; +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Person.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Person.java new file mode 100644 index 000000000..9f9748abd --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Person.java @@ -0,0 +1,31 @@ +/* + * 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.identity.entityasidentity2; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "EAI2Person") +public class Person { + + @Id + int id; +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Student.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Student.java new file mode 100644 index 000000000..3b19ef477 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/Student.java @@ -0,0 +1,38 @@ +/* + * 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.identity.entityasidentity2; + +import java.util.Collection; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "EAI2Student") +public class Student { + + @Id @OneToOne + Person person; + + @OneToMany(mappedBy = "student") + Collection attendances; +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/TestEntityAsIdentityFields2.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/TestEntityAsIdentityFields2.java new file mode 100644 index 000000000..7e3f0cfd2 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity2/TestEntityAsIdentityFields2.java @@ -0,0 +1,40 @@ +/* + * 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.identity.entityasidentity2; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestEntityAsIdentityFields2 extends SingleEMFTestCase { + + public void setUp() { + setUp(Attendance.class, Course.class, Person.class, Student.class); + } + + public void testEntityAsIdentityField001() { + EntityManager em = emf.createEntityManager(); + + Query query = em.createQuery("select p from Person p"); + query.getResultList(); + + em.close(); + } +}