You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by mi...@apache.org on 2009/10/28 18:34:45 UTC

svn commit: r830690 - in /openjpa/trunk: openjpa-kernel/src/main/java/org/apache/openjpa/conf/ openjpa-kernel/src/main/java/org/apache/openjpa/meta/ openjpa-kernel/src/main/resources/org/apache/openjpa/meta/ openjpa-persistence-jdbc/src/test/java/org/a...

Author: mikedd
Date: Wed Oct 28 17:34:45 2009
New Revision: 830690

URL: http://svn.apache.org/viewvc?rev=830690&view=rev
Log:
OPENJPA-1141: 
Optionally reorder MetaData resolution for entities which use derived IDs
Submitted By: Jody Grassel

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Account.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountGroup.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountId.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Person.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java   (with props)
Modified:
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java
    openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java?rev=830690&r1=830689&r2=830690&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java Wed Oct 28 17:34:45 2009
@@ -64,6 +64,7 @@
     private boolean _superclassDiscriminatorStrategyByDefault = true;
     private boolean _isAbstractMappingUniDirectional = false;
     private boolean _isNonDefaultMappingAllowed = false;
+    private boolean _reorderMetaDataResolution = true;
     
     /**
      * Whether to require exact identity value types when creating object
@@ -472,5 +473,25 @@
     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;
+    }
 
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java?rev=830690&r1=830689&r2=830690&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java Wed Oct 28 17:34:45 2009
@@ -151,6 +151,8 @@
     private ReentrantLock _lock = null;
     protected boolean _preload = false;
     protected boolean _noLock = false;
+    
+    private boolean _reorderMetaDataResolution = false;
 
     /**
      * Default constructor. Configure via {@link Configurable}.
@@ -829,6 +831,12 @@
                 buffer.clear();
             }
         }
+        
+        // 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;
     }
 
@@ -1773,6 +1781,7 @@
     public void setConfiguration(Configuration conf) {
         _conf = (OpenJPAConfiguration) conf;
         _log = _conf.getLog(OpenJPAConfiguration.LOG_METADATA);
+        _reorderMetaDataResolution = _conf.getCompatibilityInstance().getReorderMetaDataResolution();
     }
 
     public void startConfiguration() {
@@ -2294,4 +2303,194 @@
     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<ClassMetaData> resolveFKInPKDependenciesOrdering(List<ClassMetaData> cmdList) {
+        HashMap<ClassMetaData, CMDDependencyNode> nodeMap = new HashMap<ClassMetaData, CMDDependencyNode>();
+        HashSet<CMDDependencyNode> nodesWithDependenciesSet = new HashSet<CMDDependencyNode>();
+        ArrayList<CMDDependencyNode> nodeList = new ArrayList<CMDDependencyNode>(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<CMDDependencyNode> 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<ClassMetaData> returnList = new ArrayList<ClassMetaData>();
+        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<CMDDependencyNode> dependsOnSet = new HashSet<CMDDependencyNode>();
+
+        /**
+         * 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<CMDDependencyNode> 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<CMDDependencyNode> visitStack = new java.util.Stack<CMDDependencyNode>();
+            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<CMDDependencyNode> 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;
+        }
+    }
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties?rev=830690&r1=830689&r2=830690&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties (original)
+++ openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties Wed Oct 28 17:34:45 2009
@@ -347,4 +347,7 @@
 	The persistent classes must be enlisted in configuration to be loaded during initialization.  
 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. 	  
\ No newline at end of file
+	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.
\ No newline at end of file

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Account.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Account.java?rev=830690&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Account.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Account.java Wed Oct 28 17:34:45 2009
@@ -0,0 +1,104 @@
+/*
+ * 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.entityasidentity;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+@Entity
+@Table(name="EAIAccount")
+@IdClass(AccountId.class)
+public class Account {
+    @Id
+    @Column(name="account_id")
+    private Integer accountId;
+    
+    @Id
+    @Column(name="group_id")
+    private Integer groupId;
+    
+    @Id
+    @ManyToOne(cascade = CascadeType.ALL)
+    @JoinColumn(name="accountHolder_id")
+    private Person accountHolder;
+    
+    private long balanceInDollars;
+    private int  balanceInCents;
+    private boolean accountLocked;
+    
+    public Account() {
+        
+    }
+
+    public Integer getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(Integer accountId) {
+        this.accountId = accountId;
+    }
+
+    public Integer getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(Integer groupId) {
+        this.groupId = groupId;
+    }
+
+    public Person getAccountHolder() {
+        return accountHolder;
+    }
+
+    public void setAccountHolder(Person accountHolder) {
+        this.accountHolder = accountHolder;
+    }
+
+    public long getBalanceInDollars() {
+        return balanceInDollars;
+    }
+
+    public void setBalanceInDollars(long balanceInDollars) {
+        this.balanceInDollars = balanceInDollars;
+    }
+
+    public int getBalanceInCents() {
+        return balanceInCents;
+    }
+
+    public void setBalanceInCents(int balanceInCents) {
+        this.balanceInCents = balanceInCents;
+    }
+
+    public boolean isAccountLocked() {
+        return accountLocked;
+    }
+
+    public void setAccountLocked(boolean accountLocked) {
+        this.accountLocked = accountLocked;
+    }
+    
+    
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Account.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountGroup.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountGroup.java?rev=830690&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountGroup.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountGroup.java Wed Oct 28 17:34:45 2009
@@ -0,0 +1,60 @@
+/*
+ * 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.entityasidentity;
+
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+
+@Entity
+@Table(name="EAIAccountGroup")
+public class AccountGroup {
+    @Id
+    private Integer id;
+    
+    @OneToMany(cascade={CascadeType.ALL}, fetch=FetchType.LAZY)
+    private Set<Account> accountSet;
+    
+    public AccountGroup() {
+        accountSet = new java.util.HashSet<Account>();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Set<Account> getAccountSet() {
+        return accountSet;
+    }
+
+    public void setAccountSet(Set<Account> accountSet) {
+        this.accountSet = accountSet;
+    }
+    
+    
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountGroup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountId.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountId.java?rev=830690&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountId.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountId.java Wed Oct 28 17:34:45 2009
@@ -0,0 +1,84 @@
+/*
+ * 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.entityasidentity;
+
+public class AccountId implements java.io.Serializable {
+    private static final long serialVersionUID = 4262907482129342511L;
+    
+    private Integer accountId;
+    private Integer groupId;
+    private Integer accountHolder;
+    
+    private Integer hashcode = null;
+    
+    public AccountId() {
+        
+    }
+
+    public Integer getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(Integer accountId) {
+        this.accountId = accountId;
+    }
+    
+
+    public Integer getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(Integer groupId) {
+        this.groupId = groupId;
+    }
+
+    public Integer getAccountHolder() {
+        return accountHolder;
+    }
+
+    public void setAccountHolder(Integer accountHolder) {
+        this.accountHolder = accountHolder;
+    }
+    
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+            
+        if (o instanceof AccountId) {
+            AccountId oId = (AccountId) o;
+            if (    oId.accountId.equals(this.accountId) && 
+                    oId.accountHolder.equals(this.accountHolder) &&
+                    oId.groupId.equals(this.groupId)) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    public int hashCode() {
+        if (hashcode == null) {
+            String hashStr = this.groupId + ":" + this.accountHolder + ":" + this.accountId;
+            hashcode = hashStr.hashCode();
+        }
+        return hashcode.intValue();
+    }
+    
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/AccountId.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Person.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Person.java?rev=830690&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Person.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Person.java Wed Oct 28 17:34:45 2009
@@ -0,0 +1,61 @@
+/*
+s * 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.entityasidentity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name="EAIPerson")
+public class Person {
+    @Id
+    private Integer id;
+    
+    private String firstName;
+    private String lastName;
+    
+    public Person() {
+        
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/Person.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java?rev=830690&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java Wed Oct 28 17:34:45 2009
@@ -0,0 +1,257 @@
+/*
+ * 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.entityasidentity;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+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");
+    }
+    
+    /**
+     * Tests for the NullPointerException in MappingInfo.mergeJoinColumn reported in OpenJPA-1141.
+     * 
+     */
+    public void testEntityAsIdentityField001() {
+        EntityManager em = null;
+        em = emf.createEntityManager();
+        
+        Query query = em.createQuery("SELECT ag from AccountGroup ag");
+        List resultList = query.getResultList();
+        
+        em.close();
+    }
+    
+    /**
+     * Test EntityManager persist() and find() with entities with entity relationships as
+     * part of their identity.  Clears persistence context between entity create and find.
+     * 
+     */
+    public void testEntityAsIdentityField002A() {
+        EntityManager em = null;
+        
+        try {
+            em = emf.createEntityManager();
+            
+            // Create population
+            createPopulation(em);
+            
+            // Clear persistence context, fetch from database
+            em.clear();
+            AccountId aId = new AccountId();
+            aId.setAccountId(1);
+            aId.setAccountHolder(1);
+            aId.setGroupId(1);
+            Account findAccount = em.find(Account.class, aId);
+            assertTrue(findAccount != null);
+        } finally {
+            // Cleanup
+            if (em != null) {
+                if (em.getTransaction().isActive()) {
+                    em.getTransaction().rollback();
+                }
+                em.close();
+            }
+        }
+    }
+    
+    /**
+     * Test EntityManager persist() and find() with entities with entity relationships as
+     * part of their identity.  Does not clear persistence context between entity create and find.
+     * 
+     */
+    public void testEntityAsIdentityField002B() {
+        EntityManager em = null;
+        
+        try {
+            em = emf.createEntityManager();
+            
+            // Create population
+            createPopulation(em);
+            
+            // Fetch from database
+            AccountId aId = new AccountId();
+            aId.setAccountId(1);
+            aId.setAccountHolder(1);
+            aId.setGroupId(1);
+            Account findAccount = em.find(Account.class, aId);
+            assertTrue(findAccount != null);
+        } finally {
+            // Cleanup
+            if (em != null) {
+                if (em.getTransaction().isActive()) {
+                    em.getTransaction().rollback();
+                }
+                em.close();
+            }
+        }
+    }
+    
+    /**
+     * Test EntityManager persist() and find() with entities with entity relationships as
+     * part of their identity.  Uses different EntityManagers for create and find.
+     * 
+     */
+    public void testEntityAsIdentityField002C() {
+        EntityManager em = null;
+        EntityManager emPop = null;
+        
+        try {
+            emPop = emf.createEntityManager();
+            em = emf.createEntityManager();
+            
+            // Create population
+            createPopulation(emPop);
+            
+            // Clear persistence context, fetch from database
+            em.clear();
+            AccountId aId = new AccountId();
+            aId.setAccountId(1);
+            aId.setAccountHolder(1);
+            aId.setGroupId(1);
+            Account findAccount = em.find(Account.class, aId);
+            assertTrue(findAccount != null);
+        } finally {
+            // Cleanup
+            if (em != null) {
+                if (em.getTransaction().isActive()) {
+                    em.getTransaction().rollback();
+                }
+                em.close();
+            }
+            if (emPop != null) {
+                if (emPop.getTransaction().isActive()) {
+                    emPop.getTransaction().rollback();
+                }
+                emPop.close();
+            }
+        }
+    }
+    
+    /**
+     * Test a query with a where clause that crosses through the identity relationship.
+     * Clear persistence context before performing the query.
+     * 
+     */
+    public void testEntityAsIdentityField003A() {
+        EntityManager em = null;
+        
+        try {
+            em = emf.createEntityManager();
+            
+            // Create population
+            createPopulation(em);
+            em.clear();
+            
+            Query query = em.createQuery("SELECT a FROM Account a WHERE a.accountHolder.id > 5");
+            List resultList = query.getResultList();
+            assertEquals(5, resultList.size());
+        } finally {
+            // Cleanup
+            if (em != null) {
+                if (em.getTransaction().isActive()) {
+                    em.getTransaction().rollback();
+                }
+                em.close();
+            }
+        }
+    }
+    
+    /**
+     * Test a query with a where clause that crosses through the identity relationship.
+     * Use a separate EntityManager to populate the database, and a separate EntityManager
+     * to perform the query
+     * 
+     */
+    public void testEntityAsIdentityField004A() {
+        EntityManager em = null;
+        EntityManager emPop = null;
+        
+        try {
+            emPop = emf.createEntityManager();
+            em = emf.createEntityManager();
+            
+            // Create population
+            createPopulation(emPop);
+            em.clear();
+            
+            Query query = em.createQuery("SELECT a FROM Account a WHERE a.accountHolder.id > 5");
+            List resultList = query.getResultList();
+            assertEquals(5, resultList.size());
+        } finally {
+            // Cleanup
+            if (em != null) {
+                if (em.getTransaction().isActive()) {
+                    em.getTransaction().rollback();
+                }
+                em.close();
+            }
+            if (emPop != null) {
+                if (emPop.getTransaction().isActive()) {
+                    emPop.getTransaction().rollback();
+                }
+                emPop.close();
+            }
+        }
+    }
+    
+    /**
+     * Database population
+     * 
+     */
+    private void createPopulation(EntityManager em) {
+        em.getTransaction().begin();
+        
+        AccountGroup ag = new AccountGroup();
+        ag.setId(1);
+        Set<Account> agAccountSet = ag.getAccountSet();
+        em.persist(ag);
+        
+        for (int index = 1; index <= 10; index++) {
+            Person peep = new Person();
+            peep.setId(index);
+            peep.setFirstName("John");
+            peep.setLastName("Doe");
+            
+            Account account = new Account();
+            account.setAccountId(index);
+            account.setAccountHolder(peep);
+            account.setGroupId((index / 5) + 1);
+            
+            account.setBalanceInCents(0);
+            account.setBalanceInDollars(index * 1000);
+                       
+            em.persist(peep);
+            em.persist(account);
+            
+            agAccountSet.add(account);
+        }    
+        
+        em.getTransaction().commit();
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/identity/entityasidentity/TestEntityAsIdentityFields.java
------------------------------------------------------------------------------
    svn:eol-style = native