From 1677b481b2783ee02aeba8c7175a0ae27d18fa92 Mon Sep 17 00:00:00 2001 From: Fay Wang Date: Wed, 29 Apr 2009 19:08:11 +0000 Subject: [PATCH] OPENJPA-1053: fix update by setting an embeddable which contains a cascade delete relation with another entity. git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@769879 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/openjpa/kernel/BrokerImpl.java | 14 +++- .../openjpa/kernel/SingleFieldManager.java | 11 ++- .../openjpa/kernel/StateManagerImpl.java | 13 +++ .../embed/Embed_MappedToOneCascadeDelete.java | 67 +++++++++++++++ ...ntityA_Embed_MappedToOneCascadeDelete.java | 77 +++++++++++++++++ .../openjpa/persistence/embed/EntityB2.java | 65 +++++++++++++++ .../persistence/embed/TestEmbeddable.java | 83 ++++++++++++++++++- 7 files changed, 323 insertions(+), 7 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Embed_MappedToOneCascadeDelete.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EntityA_Embed_MappedToOneCascadeDelete.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EntityB2.java diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java index 7aab84fff..8ef9289d2 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java @@ -2658,8 +2658,11 @@ public class BrokerImpl // ACT_CASCADE if ((action & OpCallbacks.ACT_RUN) == 0) { - if (sm != null) - sm.cascadeDelete(call); + if (sm != null) { + if (!sm.isEmbedded() || !sm.getDereferencedEmbedDependent()) { + sm.cascadeDelete(call); + } + } else cascadeTransient(OpCallbacks.OP_DELETE, obj, call, "delete"); return; @@ -2669,8 +2672,11 @@ public class BrokerImpl if (sm != null) { if (sm.isDetached()) throw newDetachedException(obj, "delete"); - if ((action & OpCallbacks.ACT_CASCADE) != 0) - sm.cascadeDelete(call); + if ((action & OpCallbacks.ACT_CASCADE) != 0) { + if (!sm.isEmbedded() || !sm.getDereferencedEmbedDependent()) { + sm.cascadeDelete(call); + } + } sm.delete(); } else if (assertPersistenceCapable(obj).pcIsDetached() == Boolean.TRUE) throw newDetachedException(obj, "delete"); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java index b2f4e4071..f06880c4a 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java @@ -302,8 +302,11 @@ class SingleFieldManager // immediate cascade works on field value; dependent deref // works on external value if ((immediate || fmd.isEmbeddedPC()) - && fmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE) + && fmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE) { + if (fmd.isEmbeddedPC()) + dereferenceEmbedDependent(_sm); delete(fmd, objval, call); + } else if (fmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO) dereferenceDependent(fmd.getExternalValue(objval, _broker)); return; @@ -413,7 +416,11 @@ class SingleFieldManager if (sm != null) sm.setDereferencedDependent(true, true); } - + + void dereferenceEmbedDependent(StateManagerImpl sm) { + sm.setDereferencedEmbedDependent(true); + } + /** * Recursively invoke the broker to gather cascade-refresh objects in * the current field into the given set. This method is only called diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java index 53d47b437..10bc80907 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java @@ -107,6 +107,7 @@ public class StateManagerImpl private static final int FLAG_VERSION_CHECK = 2 << 14; private static final int FLAG_VERSION_UPDATE = 2 << 15; private static final int FLAG_DETACHING = 2 << 16; + private static final int FLAG_EMBED_DEREF = 2 << 17; private static final Localizer _loc = Localizer.forPackage (StateManagerImpl.class); @@ -1300,6 +1301,18 @@ public class StateManagerImpl _broker.addDereferencedDependent(this); } } + + void setDereferencedEmbedDependent(boolean deref) { + if (!deref && (_flags & FLAG_EMBED_DEREF) > 0) { + _flags &= ~FLAG_EMBED_DEREF; + } else if (deref && (_flags & FLAG_EMBED_DEREF) == 0) { + _flags |= FLAG_EMBED_DEREF; + } + } + + public boolean getDereferencedEmbedDependent() { + return ((_flags |= FLAG_EMBED_DEREF) == 0 ? false : true); + } /////////// // Locking diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Embed_MappedToOneCascadeDelete.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Embed_MappedToOneCascadeDelete.java new file mode 100644 index 000000000..c46449b6a --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/Embed_MappedToOneCascadeDelete.java @@ -0,0 +1,67 @@ +/* + * 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.embed; + +import javax.persistence.CascadeType; +import javax.persistence.Embeddable; +import javax.persistence.FetchType; +import javax.persistence.OneToOne; + +@Embeddable +public class Embed_MappedToOneCascadeDelete { + protected String name1; + protected String name2; + protected String name3; + + @OneToOne(mappedBy="entityA", fetch = FetchType.EAGER, cascade = CascadeType.ALL) + protected EntityB2 bm; + + + public String getName1() { + return name1; + } + + public void setName1(String name1) { + this.name1 = name1; + } + + public String getName2() { + return name2; + } + + public void setName2(String name2) { + this.name2 = name2; + } + + public String getName3() { + return name3; + } + + public void setName3(String name3) { + this.name3 = name3; + } + + public void setMappedEntityB(EntityB2 bm) { + this.bm = bm; + } + + public EntityB2 getMappedEntityB() { + return bm; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EntityA_Embed_MappedToOneCascadeDelete.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EntityA_Embed_MappedToOneCascadeDelete.java new file mode 100644 index 000000000..abc797d39 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EntityA_Embed_MappedToOneCascadeDelete.java @@ -0,0 +1,77 @@ +/* + * 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.embed; +import java.io.Serializable; + +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="TB11A") +public class EntityA_Embed_MappedToOneCascadeDelete implements Serializable { + @Id + Integer id; + + @Column(length=30) + String name; + + @Basic(fetch=FetchType.LAZY) + int age; + + @Embedded + protected Embed_MappedToOneCascadeDelete embed; + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Embed_MappedToOneCascadeDelete getEmbed() { + return embed; + } + + public void setEmbed(Embed_MappedToOneCascadeDelete embed) { + this.embed = embed; + } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EntityB2.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EntityB2.java new file mode 100644 index 000000000..bbd25855c --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/EntityB2.java @@ -0,0 +1,65 @@ +/* + * 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.embed; +import java.io.Serializable; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToOne; + +@Entity +public class EntityB2 implements Serializable { + + @Id + int id; + + @Column(length=30) + String name; + + @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) + EntityA_Embed_MappedToOneCascadeDelete entityA; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public EntityA_Embed_MappedToOneCascadeDelete getEntityA() { + return entityA; + } + + public void setEntityA(EntityA_Embed_MappedToOneCascadeDelete entityA) { + this.entityA = entityA; + } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java index 5bbdb983f..cea88a438 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java @@ -79,7 +79,9 @@ public class TestEmbeddable extends SingleEMFTestCase { Employee3.class, EmployeeName3.class, Item1.class, Item2.class, Item3.class, Company1.class, Company2.class, Division.class, VicePresident.class, EntityA_Embed_MappedToOne.class, - Embed_MappedToOne.class, DROP_TABLES); + Embed_MappedToOne.class, Embed_MappedToOneCascadeDelete.class, + EntityA_Embed_MappedToOneCascadeDelete.class, EntityB2.class, + DROP_TABLES); } public void testEntityA_Coll_String() { @@ -160,6 +162,11 @@ public class TestEmbeddable extends SingleEMFTestCase { findObjMapKeyClass(); } + public void testEntityA_Embed_MappedToOneCascadeDelete() { + createEntityA_Embed_MappedToOneCascadeDelete(); + updateEntityA_Embed_MappedToOneCascadeDelete(); + } + /* * Create EntityA_Coll_String */ @@ -257,6 +264,80 @@ public class TestEmbeddable extends SingleEMFTestCase { return embed; } + /* + * Create EntityA_Embed_MappedToOneCascadeDelete + */ + public void createEntityA_Embed_MappedToOneCascadeDelete() { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + createEntityA_Embed_MappedToOneCascadeDelete(em, ID); + tran.begin(); + em.flush(); + tran.commit(); + em.close(); + } + + public void createEntityA_Embed_MappedToOneCascadeDelete(EntityManager em, + int id) { + EntityA_Embed_MappedToOneCascadeDelete a = + new EntityA_Embed_MappedToOneCascadeDelete(); + a.setId(id); + a.setName("a" + id); + a.setAge(id); + Embed_MappedToOneCascadeDelete embed = + createEmbed_MappedToOneDeleteCascade(em, id, a); + a.setEmbed(embed); + em.persist(a); + } + + public Embed_MappedToOneCascadeDelete createEmbed_MappedToOneDeleteCascade( + EntityManager em, int id, EntityA_Embed_MappedToOneCascadeDelete a) { + Embed_MappedToOneCascadeDelete embed = new Embed_MappedToOneCascadeDelete(); + embed.setName1("name1"); + embed.setName2("name2"); + embed.setName3("name3"); + EntityB2 b = new EntityB2(); + b.setId(id); + b.setName("bm" + id); + b.setEntityA(a); + embed.setMappedEntityB(b); + em.persist(b); + return embed; + } + + /* + * Update EntityA_Embed_MappedToOneCascadeDelete + */ + public void updateEntityA_Embed_MappedToOneCascadeDelete() { + EntityManager em = emf.createEntityManager(); + EntityTransaction tran = em.getTransaction(); + updateEntityA_Embed_MappedToOneCascadeDelete(em, ID); + tran.begin(); + em.flush(); + tran.commit(); + em.clear(); + + EntityA_Embed_MappedToOneCascadeDelete a = + em.find(EntityA_Embed_MappedToOneCascadeDelete.class, ID); + assertNotNull(a); + + EntityB2 b2 = em.find(EntityB2.class, ID); + assertNotNull(b2); + + em.close(); + } + + public void updateEntityA_Embed_MappedToOneCascadeDelete(EntityManager em, + int id) { + EntityA_Embed_MappedToOneCascadeDelete a = + em.find(EntityA_Embed_MappedToOneCascadeDelete.class, id); + a.setName("newa" + id); + a.setAge(id + 1); + Embed_MappedToOneCascadeDelete embed = + createEmbed_MappedToOneDeleteCascade(em, id+1, a); + a.setEmbed(embed); + } + /* * Create EntityA_Coll_Embed_ToOne */