diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java index 1eb49f5c9..b1fdddd34 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java @@ -644,9 +644,12 @@ public class RelationFieldStrategy if (mappedByFieldMapping != null) { ValueMapping val = mappedByFieldMapping.getValueMapping(); ClassMetaData decMeta = val.getTypeMetaData(); - // this inverse field does not have corresponding classMapping - // its value may be a collection/map etc. - if (decMeta != null) { + // eager loading a child from its toOne parent and + // the parent has @OneToOne(mappedBy="parent") child relation. + // By saving the mapped-by info in 'res' is to + // avoid unneeded SQL pushdown that would otherwise gets + // generated. + if (decMeta != null) { mappedByValue = sm.getPersistenceCapable(); res.setMappedByFieldMapping(mappedByFieldMapping); res.setMappedByValue(mappedByValue); @@ -655,6 +658,12 @@ public class RelationFieldStrategy sm.storeObject(field.getIndex(), res.load(cls, store, fetch, eagerJoin(res.newJoins(), cls, false))); + + // reset mapped by is needed for OneToOne bidirectional relations + // having a mapped-by parent to correctly set the parent-child + // relation. + res.setMappedByFieldMapping(null); + res.setMappedByValue(null); } public void load(OpenJPAStateManager sm, JDBCStore store, diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/OneOneChild.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/OneOneChild.java new file mode 100644 index 000000000..4e4250ba7 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/OneOneChild.java @@ -0,0 +1,61 @@ +/* + * 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.relations; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToOne; +import javax.persistence.Version; + +@Entity +public class OneOneChild { + + @Id + @GeneratedValue + private long id; + + private String name; + + @OneToOne + private OneOneParent parent; + + @Version + private Integer optLock; + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public OneOneParent getParent() { + return parent; + } + + public void setParent(OneOneParent parent) { + this.parent = parent; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/OneOneParent.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/OneOneParent.java new file mode 100644 index 000000000..2b1e8a66e --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/OneOneParent.java @@ -0,0 +1,61 @@ +/* + * 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.relations; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToOne; +import javax.persistence.Version; + +@Entity +public class OneOneParent { + + @Id + @GeneratedValue + private long id; + + private String name; + + @OneToOne(mappedBy="parent") + private OneOneChild child; + + @Version + private Integer optLock; + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public OneOneChild getChild() { + return child; + } + + public void setChild(OneOneChild child) { + this.child = child; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestManyEagerSQL.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestManyEagerSQL.java index 656ff90c5..6d586afc7 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestManyEagerSQL.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestManyEagerSQL.java @@ -35,7 +35,8 @@ public class TestManyEagerSQL public void setUp() { setUp( OneManyEagerParent.class, OneManyEagerChild.class, - OneManyLazyChild.class); + OneManyLazyChild.class, OneOneParent.class, + OneOneChild.class); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); @@ -55,7 +56,18 @@ public class TestManyEagerSQL } em.persist(parent); } - + + for (int i = 0; i < 3; i++) { + OneOneParent parent = new OneOneParent(); + parent.setName("parent" + i); + OneOneChild child = new OneOneChild(); + child.setName("child" + i); + parent.setChild(child); + child.setParent(parent); + em.persist(child); + em.persist(parent); + } + em.flush(); em.getTransaction().commit(); em.close(); @@ -190,6 +202,59 @@ public class TestManyEagerSQL em.close(); } + public void testOneToOneParentQuery() { + sql.clear(); + + OpenJPAEntityManager em = emf.createEntityManager(); + String query = "select p FROM OneOneParent p"; + Query q = em.createQuery(query); + List list = q.getResultList(); + assertEquals(3, list.size()); + + // Expected SQLs: + // SELECT t0.id, t0.optLock, t1.id, t1.optLock, t1.name, t2.id, + // t2.optLock, t2.CHILD_ID, t2.name, t0.name + // FROM OneOneParent t0 + // LEFT OUTER JOIN OneOneChild t1 ON t0.CHILD_ID = t1.id + // LEFT OUTER JOIN OneOneParent t2 ON t1.PARENT_ID = t2.id + + assertEquals(1, sql.size()); + + sql.clear(); + + for (int i = 0; i < list.size(); i++) { + OneOneParent p = (OneOneParent) list.get(i); + assertEquals(p, p.getChild().getParent()); + } + + assertEquals(0, sql.size()); + em.close(); + } + + public void testOneToOneChildQuery() { + sql.clear(); + + OpenJPAEntityManager em = emf.createEntityManager(); + String query = "select c FROM OneOneChild c"; + Query q = em.createQuery(query); + List list = q.getResultList(); + assertEquals(3, list.size()); + + // Expected SQLs: + + assertEquals(1, sql.size()); + + sql.clear(); + + for (int i = 0; i < list.size(); i++) { + OneOneChild c = (OneOneChild) list.get(i); + assertEquals(c, c.getParent().getChild()); + } + + assertEquals(0, sql.size()); + em.close(); + } + public static void main(String[] args) { TestRunner.run(TestManyEagerSQL.class); }