From 1e95734015c0aa6009b42355a3cfeccb57c4fb2f Mon Sep 17 00:00:00 2001 From: "A. Abram White" Date: Mon, 11 Sep 2006 23:54:01 +0000 Subject: [PATCH] OPENJPA-39 test case. Also query fixes and fix to possible data corruption issue (yikes!). git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@442388 13f79535-47bb-0310-9956-ffa450edef68 --- .../kernel/OperationOrderUpdateManager.java | 5 +- .../openjpa/jdbc/kernel/exps/PCPath.java | 6 - .../jdbc/meta/strats/IdentityJoinable.java | 3 +- .../meta/strats/RelationFieldStrategy.java | 5 +- ...RelationToManyInverseKeyFieldStrategy.java | 6 +- ...TestMultipleMappedSuperclassHierarchy.java | 1 - .../relations/CascadingOneManyChild.java | 51 +++++ .../relations/CascadingOneManyParent.java | 52 +++++ .../TestCascadingOneManyWithForeignKey.java | 181 ++++++++++++++++++ .../persistence/EntityManagerFactoryImpl.java | 5 +- 10 files changed, 299 insertions(+), 16 deletions(-) create mode 100755 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/CascadingOneManyChild.java create mode 100755 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/CascadingOneManyParent.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestCascadingOneManyWithForeignKey.java diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/OperationOrderUpdateManager.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/OperationOrderUpdateManager.java index e4965579e..186f27583 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/OperationOrderUpdateManager.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/OperationOrderUpdateManager.java @@ -133,8 +133,9 @@ public class OperationOrderUpdateManager || rel.getIndex() >= row.getIndex()) continue; - // create an update to null the offending fk before deleting - update = new RowImpl(row.getTable(), Row.ACTION_UPDATE); + // create an update to null the offending fk before deleting. use + // a primary row to be sure to copy delayed-flush pks/fks + update = new PrimaryRow(row.getTable(), Row.ACTION_UPDATE, null); row.copyInto(update, true); update.setForeignKey(fks[i], row.getForeignKeyIO(fks[i]), null); if (updates == null) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java index 5474db524..f3cdabd26 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java @@ -288,12 +288,6 @@ class PCPath if (prev.op == Action.GET || prev.op == Action.GET_OUTER || prev.op == Action.GET_KEY) return prev; - - // break if we're getting to path portions that we copied from - // our variable - if (prev.op == Action.VAR || prev.op == Action.UNBOUND_VAR - || prev.op == Action.SUBQUERY) - break; } return null; } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/IdentityJoinable.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/IdentityJoinable.java index f4ab065ac..3f588f87e 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/IdentityJoinable.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/IdentityJoinable.java @@ -71,7 +71,8 @@ class IdentityJoinable public Object getJoinValue(OpenJPAStateManager sm, Column col, JDBCStore store) { - return Numbers.valueOf(((Id) sm.getObjectId()).getId()); + Id id = (Id) sm.getObjectId(); + return (id == null) ? null : id.getIdObject(); } public void setAutoAssignedValue(OpenJPAStateManager sm, JDBCStore store, 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 824a3bcfc..629e95550 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 @@ -243,12 +243,13 @@ public class RelationFieldStrategy throws SQLException { ForeignKey fk = field.getForeignKey(); ColumnIO io = field.getColumnIO(); - if (!io.isAnyUpdatable(fk, true)) + if (fk.getDeleteAction() != ForeignKey.ACTION_NONE + || !io.isAnyUpdatable(fk, true)) return; + // null inverse if not already enforced by fk if (field.getIndependentTypeMappings().length != 1) throw RelationStrategies.uninversable(field); - Row row = rm.getAllRows(fk.getTable(), Row.ACTION_UPDATE); row.setForeignKey(fk, io, null); row.whereForeignKey(fk, sm); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java index e7c436e26..11a2d2749 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java @@ -223,10 +223,12 @@ public abstract class RelationToManyInverseKeyFieldStrategy ValueMapping elem = field.getElementMapping(); ColumnIO io = elem.getColumnIO(); ForeignKey fk = elem.getForeignKey(); - if (!io.isAnyUpdatable(fk, true)) + if (fk.getDeleteAction() != ForeignKey.ACTION_NONE + || !io.isAnyUpdatable(fk, true)) return; - // null any existing inverse columns that refer to this obj + // if the fk doesn't enforce it, null any existing inverse columns + // that refer to this obj assertInversable(); Row row = rm.getAllRows(fk.getTable(), Row.ACTION_UPDATE); row.setForeignKey(fk, io, null); diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/TestMultipleMappedSuperclassHierarchy.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/TestMultipleMappedSuperclassHierarchy.java index 1e9381e8e..9b5fbd4f6 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/TestMultipleMappedSuperclassHierarchy.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/TestMultipleMappedSuperclassHierarchy.java @@ -24,7 +24,6 @@ import javax.persistence.Persistence; import junit.framework.TestCase; import junit.textui.TestRunner; -import org.apache.openjpa.persistence.OpenJPAEntityManager; /** * Perform basic operations on an inheritance hierarchy involving multiple diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/CascadingOneManyChild.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/CascadingOneManyChild.java new file mode 100755 index 000000000..7978664b9 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/CascadingOneManyChild.java @@ -0,0 +1,51 @@ +package org.apache.openjpa.persistence.relations; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Version; + +import org.apache.openjpa.persistence.jdbc.ForeignKey; + +@Entity +public class CascadingOneManyChild { + + @Id + @GeneratedValue + private long id; + + private String name; + + @ManyToOne(optional=false) + @JoinColumn(name="PARENT_ID") + @ForeignKey + private CascadingOneManyParent parent; + + @Version + private Integer optLock; + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public CascadingOneManyParent getParent() { + return parent; + } + + public void setParent(CascadingOneManyParent parent) { + this.parent = parent; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/CascadingOneManyParent.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/CascadingOneManyParent.java new file mode 100755 index 000000000..fe7fd5eb9 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/CascadingOneManyParent.java @@ -0,0 +1,52 @@ +package org.apache.openjpa.persistence.relations; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; +import javax.persistence.Version; + +@Entity +public class CascadingOneManyParent { + + @Id + @GeneratedValue + private long id; + + private String name; + + @OneToMany(mappedBy="parent", cascade=CascadeType.ALL) + @OrderBy("name ASC") + private List children = + new ArrayList(); + + @Version + private Integer optLock; + + public long getId() { + return id; + } + + public List getChildren() { + return children; + } + + public void addChild(CascadingOneManyChild child) { + child.setParent(this); + children.add(child); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestCascadingOneManyWithForeignKey.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestCascadingOneManyWithForeignKey.java new file mode 100644 index 000000000..9c84730c7 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestCascadingOneManyWithForeignKey.java @@ -0,0 +1,181 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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 java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.EntityNotFoundException; +import javax.persistence.EntityTransaction; +import javax.persistence.Persistence; + +import junit.framework.TestCase; +import junit.textui.TestRunner; +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; + +/** + * Tests a cascading one-many backed by a foreign key. + * + * @author Abe White + */ +public class TestCascadingOneManyWithForeignKey + extends TestCase { + + private OpenJPAEntityManagerFactory emf; + + public void setUp() { + String types = CascadingOneManyParent.class.getName() + ";" + + CascadingOneManyChild.class.getName(); + Map props = new HashMap(); + props.put("openjpa.MetaDataFactory", "jpa(Types=" + types + ")"); + emf = (OpenJPAEntityManagerFactory) Persistence. + createEntityManagerFactory("test", props); + } + + public void tearDown() { + if (emf == null) + return; + try { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + em.createQuery("delete from CascadingOneManyChild").executeUpdate(); + em.createQuery("delete from CascadingOneManyParent"). + executeUpdate(); + em.getTransaction().commit(); + em.close(); + emf.close(); + } catch (Exception e) { + } + } + + public void testPersist() { + CascadingOneManyParent parent = new CascadingOneManyParent(); + parent.setName("parent"); + for (int i = 0; i < 2; i++) { + CascadingOneManyChild child = new CascadingOneManyChild(); + child.setName("child" + i); + parent.addChild(child); + } + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + em.persist(parent); + em.getTransaction().commit(); + long id = parent.getId(); + assertEquals(2, parent.getChildren().size()); + assertEquals("child0", parent.getChildren().get(0).getName()); + assertEquals("child1", parent.getChildren().get(1).getName()); + em.close(); + + em = emf.createEntityManager(); + parent = em.find(CascadingOneManyParent.class, id); + assertNotNull(parent); + assertEquals("parent", parent.getName()); + assertEquals(2, parent.getChildren().size()); + assertEquals("child0", parent.getChildren().get(0).getName()); + assertEquals("child1", parent.getChildren().get(1).getName()); + em.close(); + } + + public void testDelete() { + CascadingOneManyParent parent = new CascadingOneManyParent(); + parent.setName("parent"); + for (int i = 0; i < 2; i++) { + CascadingOneManyChild child = new CascadingOneManyChild(); + child.setName("child" + i); + parent.addChild(child); + } + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + em.persist(parent); + em.getTransaction().commit(); + long id = parent.getId(); + em.close(); + + em = emf.createEntityManager(); + parent = em.find(CascadingOneManyParent.class, id); + assertNotNull(parent); + assertEquals(2, parent.getChildren().size()); + em.getTransaction().begin(); + em.remove(parent); + em.getTransaction().commit(); + assertRemoved(em, id); + em.close(); + + em = emf.createEntityManager(); + assertRemoved(em, id); + em.close(); + } + + private void assertRemoved(EntityManager em, long id) { + assertNull(em.find(CascadingOneManyParent.class, id)); + List res = em.createQuery("select c from CascadingOneManyChild c"). + getResultList(); + assertEquals(0, res.size()); + } + + public void testForeignKey() { + JDBCConfiguration conf = (JDBCConfiguration) emf.getConfiguration(); + if (!conf.getDBDictionaryInstance().supportsForeignKeys) + return; + + CascadingOneManyParent parent = new CascadingOneManyParent(); + parent.setName("parent"); + CascadingOneManyChild child; + for (int i = 0; i < 2; i++) { + child = new CascadingOneManyChild(); + child.setName("child" + i); + parent.addChild(child); + } + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + em.persist(parent); + em.getTransaction().commit(); + long id = parent.getId(); + em.close(); + + OpenJPAEntityManager oem = (OpenJPAEntityManager) emf. + createEntityManager(); + parent = oem.find(CascadingOneManyParent.class, id); + assertNotNull(parent); + assertEquals(2, parent.getChildren().size()); + child = parent.getChildren().get(0); + oem.getTransaction().begin(); + oem.remove(parent); + // undelete one child + assertTrue(oem.isRemoved(child)); + oem.persist(child); + assertFalse(oem.isRemoved(child)); + assertEquals(parent, child.getParent()); + try { + oem.getTransaction().commit(); + fail("Commit should have failed due to FK constraint violation."); + } catch (Exception e) { + } + oem.close(); + } + + public static void main(String[] args) { + TestRunner.run(TestCascadingOneManyWithForeignKey.class); + } +} + diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java index 5a27ba05c..35eb56e2b 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java @@ -112,8 +112,9 @@ public class EntityManagerFactoryImpl _factory.lock(); try { if (_queryCache == null) - _queryCache = new QueryResultCacheImpl(_factory.getConfiguration(). - getDataCacheManagerInstance().getSystemQueryCache()); + _queryCache = new QueryResultCacheImpl(_factory. + getConfiguration().getDataCacheManagerInstance(). + getSystemQueryCache()); return _queryCache; } finally { _factory.unlock();