diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java index 5d720cdaf..fa9e0f2a7 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java @@ -270,7 +270,8 @@ abstract class AttachStrategy if (toAttach == null) return null; - if (manager.getBroker().isPersistent(toAttach)) { + if (manager.getBroker().isNew(toAttach) + || manager.getBroker().isPersistent(toAttach)) { return toAttach; } else if (manager.getBroker().isDetached(toAttach)) { Object oid = manager.getDetachedObjectId(toAttach); diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/TestNoCascadeOneToManyMerge.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/TestNoCascadeOneToManyMerge.java new file mode 100644 index 000000000..3f1492a5f --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/TestNoCascadeOneToManyMerge.java @@ -0,0 +1,126 @@ +/* + * 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.detachment; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; +import org.apache.openjpa.persistence.detachment.model.*; + +import javax.persistence.EntityManager; + +import junit.textui.TestRunner; + +/** + * Test merge case for 3 level one to many relation entities. + * SimpleA -> *SimpleB -> *SimpleC. SimpleC has no CascadeType.MERGE annotation. + * + */ +public class TestNoCascadeOneToManyMerge extends SingleEMFTestCase { + private int a_id; + + public void setUp() { + setUp(SimpleA.class, SimpleRef.class, SimpleB.class, SimpleC.class, CLEAR_TABLES); + createEntities(); + } + + private void createEntities() { + SimpleA a = new SimpleA(); + a.setName("a1"); + + SimpleB b = new SimpleB(); + b.setName("b1"); + a.addB(b); + + SimpleC c1 = new SimpleC(); + c1.setName("c1"); + b.addC(c1); + + SimpleC c2 = new SimpleC(); + c2.setName("c2"); + b.addC(c2); + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + em.persist(a); + em.getTransaction().commit(); + em.close(); + a_id = a.getId(); + } + + public void testMergeAttached () { + EntityManager em = emf.createEntityManager(); + SimpleA a = em.find(SimpleA.class, a_id); + assertNotNull(a); + + SimpleB b = new SimpleB(); + b.setName("b2"); + a.addB(b); + + SimpleC c = new SimpleC(); + c.setName("c3"); + b.addC(c); + + c = new SimpleC(); + c.setName("c4"); + b.addC(c); + + em.getTransaction().begin(); + a = em.merge (a); + em.getTransaction().commit (); + em.close(); + + assertEquals(2, a.getBs().size()); + } + + /** + * This is the case for openjpa-231. + * When "B" and "C" are both newly added to a detached "A" and then merge "A", + * it couldn't find "B" because previous code assume B was detached. + */ + public void testMergeDetached () { + EntityManager em = emf.createEntityManager(); + SimpleA a = em.find(SimpleA.class, a_id); + assertNotNull(a); + assertEquals(1, a.getBs().size()); + em.close(); //detach a + + SimpleB b = new SimpleB(); + b.setName("b2"); + a.addB(b); + + SimpleC c = new SimpleC(); + c.setName("c3"); + b.addC(c); + + c = new SimpleC(); + c.setName("c4"); + b.addC(c); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + a = em.merge(a); + em.getTransaction().commit(); + em.close(); + + assertEquals(2, a.getBs().size()); + } + + public static void main(String[] args) { + TestRunner.run(TestNoCascadeOneToManyMerge.class); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/TestNoCascadeOneToOneMerge.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/TestNoCascadeOneToOneMerge.java new file mode 100644 index 000000000..a50fd0985 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/TestNoCascadeOneToOneMerge.java @@ -0,0 +1,147 @@ +/* + * 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.detachment; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; +import org.apache.openjpa.persistence.detachment.model.*; + +import javax.persistence.EntityManager; + +import junit.textui.TestRunner; + +/** + * Tests merging an entity having a unidirectional one-to-one relation and + * cascade=none. + * + * @author Gokhan Ergul + */ +public class TestNoCascadeOneToOneMerge + extends SingleEMFTestCase { + + private int a_id; + private int b1_id; + private int b2_id; + + public void setUp() { + setUp(SimpleA.class, SimpleRef.class, SimpleB.class, SimpleC.class, CLEAR_TABLES); + createEntities(); + } + + private void createEntities() { + SimpleRef b1 = new SimpleRef(); + b1.setName("b1-name"); + + SimpleRef b2 = new SimpleRef(); + b2.setName("b2-name"); + + SimpleA a = new SimpleA(); + a.setName("a-name"); + a.setRef(b1); + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + em.persist(b1); + em.persist(b2); + em.persist(a); + em.getTransaction().commit(); + em.close(); + a_id = a.getId(); + b1_id = b1.getId(); + b2_id = b2.getId(); + } + + public void testMergeAllAttached () { + EntityManager em = emf.createEntityManager(); + SimpleA a = em.find(SimpleA.class, a_id); + SimpleRef b2 = em.find(SimpleRef.class, b2_id); + assertNotNull(a); + assertNotNull(b2); + + // change a.b from b1 to b2 and merge + a.setRef(b2); + + em.getTransaction().begin(); + em.merge(a); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + a = em.find(SimpleA.class, a_id); + em.close(); + assertNotNull(a); + assertEquals(b2_id, a.getRef().getId()); + } + + public void testMergeRefAttached () { + EntityManager em = emf.createEntityManager(); + SimpleA a = em.find(SimpleA.class, a_id); + assertNotNull(a); + em.close(); // detach a only + + em = emf.createEntityManager(); + SimpleRef b2 = em.find(SimpleRef.class, b2_id); + assertNotNull(b2); + // do not detach b2 + + // change a.b from b1 to b2 and merge + a.setRef(b2); + + em.getTransaction().begin(); + em.merge(a); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + a = em.find(SimpleA.class, a_id); + em.close(); + assertNotNull(a); + assertEquals(b2_id, a.getRef().getId()); + } + + public void testMergeDetached () { + EntityManager em = emf.createEntityManager(); + SimpleA a = em.find(SimpleA.class, a_id); + SimpleRef b1 = em.find(SimpleRef.class, b1_id); + SimpleRef b2 = em.find(SimpleRef.class, b2_id); + assertNotNull(a); + assertNotNull(b1); + assertNotNull(b2); + em.close(); // detach all + + // change a.b from b1 to b2 and merge + a.setRef(b2); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + em.merge(a); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + a = em.find(SimpleA.class, a_id); + em.close(); + assertNotNull(a); + assertEquals(b2_id, a.getRef().getId()); + } + + public static void main(String[] args) { + TestRunner.run(TestNoCascadeOneToOneMerge.class); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleA.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleA.java new file mode 100644 index 000000000..381971285 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleA.java @@ -0,0 +1,70 @@ +/* + * 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.detachment.model; + +import javax.persistence.*; +import java.util.Set; +import java.util.LinkedHashSet; + +@Entity +public class SimpleA { + + @Id + @GeneratedValue + protected int a_id; + + @Basic + protected String name; + + @OneToOne + protected SimpleRef ref; + + @OneToMany(cascade=CascadeType.ALL, mappedBy="parent") + protected Set b_set = new LinkedHashSet(); + + public int getId() { + return a_id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public SimpleRef getRef() { + return ref; + } + + public void setRef(SimpleRef ref) { + this.ref = ref; + } + + public void addB(SimpleB b) { + b_set.add(b); + b.setParent(this); + } + + public Set getBs() { + return b_set; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleB.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleB.java new file mode 100644 index 000000000..6d6b22d77 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleB.java @@ -0,0 +1,71 @@ +/* + * 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.detachment.model; + +import javax.persistence.*; +import java.util.Set; +import java.util.LinkedHashSet; + +@Entity +public class SimpleB { + + @Id + @GeneratedValue + protected int b_id; + + @Basic + protected String name; + + @ManyToOne + @JoinColumn(name="A_ID", referencedColumnName="A_ID", nullable = false, updatable = false) + protected SimpleA parent; + + @OneToMany(cascade=CascadeType.ALL, mappedBy="parent") + protected Set c_set = new LinkedHashSet(); + + public int getId() { + return b_id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public SimpleA getParent() { + return parent; + } + + public void setParent(SimpleA a) { + this.parent = a; + } + + public void addC (SimpleC c) { + c_set.add (c); + c.setParent(this); + } + + public Set getCs() { + return c_set; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleC.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleC.java new file mode 100644 index 000000000..11ff988b0 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleC.java @@ -0,0 +1,39 @@ +package org.apache.openjpa.persistence.detachment.model; + +import javax.persistence.*; + +@Entity +public class SimpleC { + + @Id + @GeneratedValue + protected int c_id; + + @Basic + protected String name; + + @ManyToOne(cascade=CascadeType.PERSIST) + @JoinColumn(name="B_ID", referencedColumnName="B_ID", nullable = false, updatable = false) + protected SimpleB parent; + + public int getId() { + return c_id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setParent(SimpleB b) { + this.parent = b; + } + + public SimpleB getParent() { + return parent; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleRef.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleRef.java new file mode 100644 index 000000000..70cf53e47 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/detachment/model/SimpleRef.java @@ -0,0 +1,48 @@ +/* + * 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.detachment.model; + +import javax.persistence.*; +import java.util.Set; +import java.util.LinkedHashSet; + +@Entity +public class SimpleRef { + + @Id + @GeneratedValue + protected int id; + + @Basic + protected String name; + + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +}