diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMapping.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMapping.java index 38de37923..09660105f 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMapping.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMapping.java @@ -189,34 +189,27 @@ public class ClassMapping return null; } } - Object oid = ApplicationIds.fromPKValues(vals, cls); - - /** - * For polymorphic relations, - * the type field in the oid is initially set to base type. - * If the discriminator value is preset in the current result, - * then the type field needs reset based on the discriminator value. - * If the discriminator value is not present or invalid, - * ignore any exceptions being thrown. - */ + + // the oid data is loaded by the base type, but if discriminator data + // is present, make sure to use it to construct the actual oid instance + // so that we get the correct app id class, etc + ClassMapping dcls = cls; + if (subs) { + res.startDataRequest(cls.getDiscriminator()); + try { + Class dtype = cls.getDiscriminator().getClass(store, cls, res); + if (dtype != cls.getDescribedType()) + dcls = cls.getMappingRepository().getMapping(dtype, + store.getContext().getClassLoader(), true); + } catch (Exception e) { + // intentionally ignored + } + res.endDataRequest(); + } + Object oid = ApplicationIds.fromPKValues(vals, dcls); if (oid instanceof OpenJPAId) { - Class type = cls.getDescribedType(); - if (!subs) - // non-polymorphic relations - ((OpenJPAId) oid).setManagedInstanceType(type); - else if (cls.getDiscriminator() != null - && !StringUtils.equals("none", - cls.getDiscriminator().getStrategy().getAlias())) { - // polymorphic relations - res.startDataRequest(cls.getDiscriminator()); - try { - type = cls.getDiscriminator().getClass(store, cls, res); - ((OpenJPAId) oid).setManagedInstanceType(type, true); - } catch (Exception e) { - // intentionally ignored - } - res.endDataRequest(); - } + ((OpenJPAId) oid).setManagedInstanceType(dcls.getDescribedType(), + subs); } return oid; } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java index af829ef7c..32c2c7b4c 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java @@ -193,12 +193,16 @@ public class ApplicationIds { return new ObjectId(meta.getDescribedType(), val); case JavaTypes.BIGDECIMAL: if (!convert && !(val instanceof BigDecimal)) - throw new ClassCastException("!(x instanceof BigDecimal)"); - return new BigDecimalId(meta.getDescribedType(), (BigDecimal)val); + throw new ClassCastException( + "!(x instanceof BigDecimal)"); + return new BigDecimalId(meta.getDescribedType(), + (BigDecimal)val); case JavaTypes.BIGINTEGER: if (!convert && !(val instanceof BigInteger)) - throw new ClassCastException("!(x instanceof BigInteger)"); - return new BigIntegerId(meta.getDescribedType(), (BigInteger)val); + throw new ClassCastException( + "!(x instanceof BigInteger)"); + return new BigIntegerId(meta.getDescribedType(), + (BigInteger)val); default: throw new InternalException(); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java index 37ad42064..8879de04b 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java @@ -55,7 +55,7 @@ public abstract class OpenJPAId } /** - * Return the persitent class which this id instance represents. + * Return the persistent class which this id instance represents. */ public Class getType() { return type; @@ -73,8 +73,7 @@ public abstract class OpenJPAId * Set the exact type of the described instance once it is known. */ public void setManagedInstanceType(Class type) { - this.type = type; - this.subs = false; + setManagedInstanceType(type, false); } /** diff --git a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties index c82129688..de70bf03d 100644 --- a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties +++ b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties @@ -117,7 +117,7 @@ trans-not-managed: This broker is not configured to use managed transactions. bad-detached-op: You cannot perform operation {0} on detached object "{1}". \ This operation only applies to managed objects. persist-detached: Attempt to persist detached object "{0}". If this is a new \ - instance, make sure any versino and/or auto-generated primary key fields are \ + instance, make sure any version and/or auto-generated primary key fields are \ null/default when persisting. null-value: The field "{0}" of instance "{1}" contained a null value; \ the metadata for this field specifies that nulls are illegal. diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/RelationOwner.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/RelationOwner.java new file mode 100644 index 000000000..0eabce419 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/RelationOwner.java @@ -0,0 +1,24 @@ +package org.apache.openjpa.persistence.inheritance.abstractjoinedappid; + +import java.util.*; +import javax.persistence.*; + +import org.apache.openjpa.persistence.jdbc.ElementJoinColumn; + +@Entity +@Table(name="TEST") +public class RelationOwner { + + private Integer id; + private Collection supers = new ArrayList(); + + @Id + @Column(name="ID") + public Integer getId() { return id; } + public void setId(Integer id) { this.id = id; } + + @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER) + @ElementJoinColumn(name="TEST", referencedColumnName="ID") + public Collection getSupers() { return supers; } + public void setSupers(Collection supers) { this.supers = supers; } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/SubID.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/SubID.java new file mode 100644 index 000000000..4c280392e --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/SubID.java @@ -0,0 +1,5 @@ +package org.apache.openjpa.persistence.inheritance.abstractjoinedappid; + +public class SubID extends SuperID { + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/Subclass.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/Subclass.java new file mode 100644 index 000000000..8ef67f4e7 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/Subclass.java @@ -0,0 +1,16 @@ +package org.apache.openjpa.persistence.inheritance.abstractjoinedappid; + +import javax.persistence.*; + +@Entity +@IdClass(SubID.class) +@Table(name="SUB") +public class Subclass extends Superclass { + + private String attr2; + + @Column(name="ATTR2") + public String getAttr2() { return attr2; } + public void setAttr2(String attr2) { this.attr2 = attr2; } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/SuperID.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/SuperID.java new file mode 100644 index 000000000..a8182f426 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/SuperID.java @@ -0,0 +1,26 @@ +package org.apache.openjpa.persistence.inheritance.abstractjoinedappid; + +import java.io.Serializable; + +public abstract class SuperID implements Serializable { + + private Integer id; + + public Integer getId() { return id; } + public void setId(Integer id) { this.id = id; } + + public int hashCode() { + return id; + } + + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof SuperID)) return false; + SuperID pk = (SuperID) obj; + if ( pk.getId().equals(id)) { + return true; + } else { + return false; + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/Superclass.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/Superclass.java new file mode 100644 index 000000000..af3e3f49c --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/Superclass.java @@ -0,0 +1,22 @@ +package org.apache.openjpa.persistence.inheritance.abstractjoinedappid; + +import javax.persistence.*; + +@Entity +@IdClass(SuperID.class) +@Table(name="SUPER") +@Inheritance(strategy=InheritanceType.JOINED) +public abstract class Superclass { + + private Integer id; + private String attr1; + + @Id + @Column(name="ID") + public Integer getId() { return id; } + public void setId(Integer id) { this.id = id; } + + @Column(name="ATTR1") + public String getAttr1() { return attr1; } + public void setAttr1(String attr1) { this.attr1 = attr1; } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/TestAbstractJoinedAppId.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/TestAbstractJoinedAppId.java new file mode 100644 index 000000000..1aa23140c --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/abstractjoinedappid/TestAbstractJoinedAppId.java @@ -0,0 +1,74 @@ +/* + * 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.inheritance.abstractjoinedappid; + +import java.util.Arrays; +import java.util.Collection; +import javax.persistence.EntityManager; + +import junit.textui.TestRunner; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +/** + * Test that you traverse an inverse key-baesd relation to an abstract + * class using joined inheritance. + */ +public class TestAbstractJoinedAppId + extends SingleEMFTestCase { + + public void setUp() { + setUp(Superclass.class, Subclass.class, RelationOwner.class, + DROP_TABLES); + + Subclass s = new Subclass(); + s.setId(99); + s.setAttr1("supattr"); + s.setAttr2("subattr"); + + RelationOwner ro = new RelationOwner(); + ro.setId(1); + ro.setSupers(Arrays.asList(new Superclass[] { s })); + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + em.persist(ro); + em.getTransaction().commit(); + em.close(); + } + + public void testTraverseRelation() { + EntityManager em = emf.createEntityManager(); + RelationOwner ro = em.find(RelationOwner.class, 1); + assertNotNull(ro); + + Collection supers = ro.getSupers(); + assertEquals(1, supers.size()); + Superclass s = (Superclass) supers.iterator().next(); + assertTrue(s instanceof Subclass); + assertEquals(new Integer(99), s.getId()); + assertEquals("supattr", s.getAttr1()); + assertEquals("subattr", ((Subclass) s).getAttr2()); + em.close(); + } + + public static void main(String[] args) { + TestRunner.run(TestAbstractJoinedAppId.class); + } +} +