Bug#500145: Nested Embeddables with matching attribute names overwrite

Signed-off-by: Will Dazey <dazeydev.3@gmail.com>
Reviewed-by: Petros Splinakis <petros.splinakis@oracle.com>
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/TestNestedEmbeddable.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/TestNestedEmbeddable.java
new file mode 100644
index 0000000..28dd000
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/TestNestedEmbeddable.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2016 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
+ * which accompanies this distribution. 
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at 
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     08/24/2016 - Will Dazey
+ *       - 500145 : Nested Embeddables Test
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.embeddable;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+
+import org.eclipse.persistence.jpa.embeddable.model.Address;
+import org.eclipse.persistence.jpa.embeddable.model.Order;
+import org.eclipse.persistence.jpa.embeddable.model.OrderPK;
+import org.eclipse.persistence.jpa.embeddable.model.Zipcode;
+import org.eclipse.persistence.jpa.test.framework.DDLGen;
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(EmfRunner.class)
+public class TestNestedEmbeddable {
+    @Emf(createTables = DDLGen.DROP_CREATE, classes = { Order.class, OrderPK.class, Address.class, Zipcode.class })
+        private EntityManagerFactory emf;
+    
+    @Test
+    public void persistTest() {
+        if (emf == null)
+            return;
+        EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            Order o = new Order();
+            
+            String billingZip = "12345";
+            String shippingZip ="54321";
+            
+            OrderPK opk = new OrderPK();
+            opk.setBillingAddress(new Address(new Zipcode(billingZip)));
+            opk.setShippingAddress(new Address(new Zipcode(shippingZip)));
+            
+            o.setId(opk);
+            
+            em.persist(o);
+            em.getTransaction().commit();
+            em.clear();
+            
+            Order foundOrder = em.find(Order.class, o.getId());
+            
+            Assert.assertNotNull(foundOrder);
+            
+            Object pk = emf.getPersistenceUnitUtil().getIdentifier(foundOrder);
+            Assert.assertTrue(pk instanceof OrderPK);
+            
+            Assert.assertNotNull(((OrderPK)pk).getBillingAddress());
+            Assert.assertNotNull(((OrderPK)pk).getShippingAddress());
+            
+            Assert.assertEquals(billingZip,((OrderPK)pk).getBillingAddress().getZipcode().getZip());
+            Assert.assertEquals(shippingZip,((OrderPK)pk).getShippingAddress().getZipcode().getZip());
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/Address.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/Address.java
new file mode 100644
index 0000000..9084866
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/Address.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2016 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
+ * which accompanies this distribution. 
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at 
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     08/24/2016 - Will Dazey
+ *       - 500145 : Nested Embeddables Test
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.embeddable.model;
+
+import javax.persistence.Embeddable;
+import javax.persistence.Embedded;
+
+@Embeddable
+public class Address {
+    
+    @Embedded 
+    protected Zipcode zipcode;
+    
+    public Address() { }
+    
+    public Address(Zipcode zipcode) {
+        this.setZipcode(zipcode);
+    }
+    
+    public Zipcode getZipcode() {
+        return zipcode;
+    }
+    public void setZipcode(Zipcode zipcode) {
+        this.zipcode = zipcode;
+    }
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((zipcode == null) ? 0 : zipcode.hashCode());
+        return result;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Address other = (Address) obj;
+        if (zipcode == null) {
+            if (other.zipcode != null)
+                return false;
+        } else if (!zipcode.equals(other.zipcode))
+            return false;
+        return true;
+    }
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/Order.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/Order.java
new file mode 100644
index 0000000..deeaf9c
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/Order.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2016 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
+ * which accompanies this distribution. 
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at 
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     08/24/2016 - Will Dazey
+ *       - 500145 : Nested Embeddables Test
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.embeddable.model;
+
+import javax.persistence.AttributeOverride;
+import javax.persistence.AttributeOverrides;
+import javax.persistence.Column;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.Version;
+
+@Entity
+@Table(name="CUST_ORDER")
+public class Order {
+    
+    @EmbeddedId
+    @AttributeOverrides({
+        @AttributeOverride(name = "billingAddress.zipcode.zip", column = @Column(name = "BILL_ZIP")),
+        @AttributeOverride(name = "shippingAddress.zipcode.zip", column = @Column(name = "SHIP_ZIP"))
+    })
+    private OrderPK id;
+    
+    @Version
+    int version;
+    
+    public Order() { }
+    
+    public OrderPK getId() {
+        return id;
+    }
+    
+    public void setId(OrderPK id) {
+        this.id = id;
+    }
+    
+    public int getVersion() {
+        return version;
+    }
+    
+    public void setVersion(int version) {
+        this.version = version;
+    }
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/OrderPK.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/OrderPK.java
new file mode 100644
index 0000000..20333d4
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/OrderPK.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2016 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
+ * which accompanies this distribution. 
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at 
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     08/24/2016 - Will Dazey
+ *       - 500145 : Nested Embeddables Test
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.embeddable.model;
+
+import javax.persistence.Embeddable;
+import javax.persistence.Embedded;
+
+@Embeddable
+public class OrderPK {
+    
+    @Embedded
+    private Address billingAddress;
+    
+    @Embedded
+    private Address shippingAddress;
+    
+    public OrderPK() { }
+    
+    public Address getBillingAddress() {
+        return billingAddress;
+    }
+    
+    public void setBillingAddress(Address billingAddress) {
+        this.billingAddress = billingAddress;
+    }
+    
+    public Address getShippingAddress() {
+        return shippingAddress;
+    }
+    
+    public void setShippingAddress(Address shippingAddress) {
+        this.shippingAddress = shippingAddress;
+    }
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((billingAddress == null) ? 0 : billingAddress.hashCode());
+        result = prime * result + ((shippingAddress == null) ? 0 : shippingAddress.hashCode());
+        return result;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        OrderPK other = (OrderPK) obj;
+        if (billingAddress == null) {
+            if (other.billingAddress != null)
+                return false;
+        } else if (!billingAddress.equals(other.billingAddress))
+            return false;
+        if (shippingAddress == null) {
+            if (other.shippingAddress != null)
+                return false;
+        } else if (!shippingAddress.equals(other.shippingAddress))
+            return false;
+        return true;
+    }
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/Zipcode.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/Zipcode.java
new file mode 100644
index 0000000..b13e7c9
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/embeddable/model/Zipcode.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2016 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
+ * which accompanies this distribution. 
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at 
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     08/24/2016 - Will Dazey
+ *       - 500145 : Nested Embeddables Test
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.embeddable.model;
+
+import javax.persistence.Embeddable;
+
+@Embeddable
+public class Zipcode {
+    
+    protected String zip;
+    
+    public Zipcode() {}
+    
+    public Zipcode(String zip) {
+        this.setZip(zip);
+    }
+    
+    public String getZip() {
+        return zip;
+    }
+    
+    public void setZip(String zip) {
+        this.zip = zip;
+    }
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((zip == null) ? 0 : zip.hashCode());
+        return result;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Zipcode other = (Zipcode) obj;
+        if (zip == null) {
+            if (other.zip != null)
+                return false;
+        } else if (!zip.equals(other.zip))
+            return false;
+        return true;
+    }
+}
diff --git a/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metadata/accessors/mappings/EmbeddedIdAccessor.java b/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metadata/accessors/mappings/EmbeddedIdAccessor.java
index b99b7c3..423cfce 100644
--- a/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metadata/accessors/mappings/EmbeddedIdAccessor.java
+++ b/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metadata/accessors/mappings/EmbeddedIdAccessor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -43,6 +43,8 @@
  *       - 331386: NPE when mapping chain of 2 multi-column relationships using JPA 2.0 and @EmbeddedId composite PK-FK
  *     03/24/2011-2.3 Guy Pelletier
  *       - 337323: Multi-tenant with shared schema support (part 1)
+ *     08/24/2016-2.6 Will Dazey  
+ *       - 500145: Nested Embeddables with matching attribute names overwrite
  ******************************************************************************/
 package org.eclipse.persistence.internal.jpa.metadata.accessors.mappings;
 
@@ -108,15 +110,13 @@
        // Update our primary key field with the attribute override field.
        // The super class will ensure the correct field is on the metadata
        // column.
-       m_idFields.put(mappingAccessor.getAttributeName(), overrideField);
+       m_idFields.put(overrideName, overrideField);
     }
 
     /**
      * INTERNAL:
      */
-    protected void addIdFieldFromAccessor(MappingAccessor accessor) {
-        String attributeName = accessor.getAttributeName();
-
+    protected void addIdFieldFromAccessor(String attributeName, MappingAccessor accessor) {
         if (m_idFields.containsKey(attributeName)) {
             // It may be in our id fields map already if an attribute override
             // was specified on the embedded mapping. Make sure the existing id
@@ -132,15 +132,16 @@
     /**
      * INTERNAL:
      */
-    protected void addIdFieldsFromAccessors(Collection<MappingAccessor> accessors) {
+    protected void addIdFieldsFromAccessors(String parentAttribute, Collection<MappingAccessor> accessors) {
         // Go through all our mappings, the fields from those mappings will
         // make up the composite primary key.
         for (MappingAccessor accessor : accessors) {
+            String attributeName = (parentAttribute == null) ? accessor.getAttributeName() : parentAttribute + "." + accessor.getAttributeName();
             if (accessor.isBasic()) {
-                addIdFieldFromAccessor(accessor);
+                addIdFieldFromAccessor(attributeName, accessor);
             } else if (accessor.isDerivedIdClass() || accessor.isEmbedded()) {
                 // Recursively bury down on the embedded or derived id class accessors.
-                addIdFieldsFromAccessors(accessor.getReferenceAccessors());
+                addIdFieldsFromAccessors(attributeName, accessor.getReferenceAccessors());
             } else {
                 // EmbeddedId is solely a JPA feature, so we will not allow
                 // the expansion of attributes for those types of Embeddable
@@ -188,7 +189,7 @@
         } else {
             // Go through all our mappings, the fields from those mappings will
             // make up the composite primary key.
-            addIdFieldsFromAccessors(getReferenceAccessors());
+            addIdFieldsFromAccessors(null, getReferenceAccessors());
 
             // Flag this id accessor as a JPA id mapping.
             getMapping().setIsJPAId();