| /* |
| * 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.merge; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.persistence.EntityManager; |
| |
| import org.apache.openjpa.persistence.merge.model.Car; |
| import org.apache.openjpa.persistence.merge.model.Label; |
| import org.apache.openjpa.persistence.merge.model.Label2; |
| import org.apache.openjpa.persistence.merge.model.Make; |
| import org.apache.openjpa.persistence.merge.model.Model; |
| import org.apache.openjpa.persistence.merge.model.ShipPackage; |
| import org.apache.openjpa.persistence.merge.model.ShipPackage2; |
| import org.apache.openjpa.persistence.test.SingleEMFTestCase; |
| |
| public class TestMergeDetachStateField extends SingleEMFTestCase { |
| @Override |
| public void setUp() { |
| setUp(Label.class, ShipPackage.class, |
| Label2.class, ShipPackage2.class, |
| Car.class, Model.class, Make.class, |
| CLEAR_TABLES); |
| } |
| |
| /** |
| * Verify a merge graph is correct when an entity uses a detached state field. When |
| * a detached state field is used, the entity does not get populated with a DetachedStateManager |
| * upon detachment. |
| * |
| * ShipPackage: Has DetachedStateManager after detach. |
| * Label: Detached state field - no DSM after detach. |
| * Initial graph: Label <--> ShipPackage |
| * Merge: Label |
| * Verify after merge: Label' <--> ShipPackage' |
| */ |
| public void testCascadeMergeDetachState() { |
| EntityManager em = emf.createEntityManager(); |
| |
| try { |
| // Create simple bi-di graph |
| ShipPackage p = new ShipPackage(); |
| Label l = new Label(p); |
| p.setLabel(l); |
| |
| // Persist |
| em.getTransaction().begin(); |
| em.persist(l); |
| em.getTransaction().commit(); |
| |
| // Detach |
| em.clear(); |
| assertFalse(em.contains(l)); |
| assertFalse(em.contains(p)); |
| assertFalse(em.contains(l.getPackage())); |
| assertFalse(em.contains(p.getLabel())); |
| |
| em.getTransaction().begin(); |
| Label mergedLabel = em.merge(l); |
| |
| assertFalse(mergedLabel == l); |
| assertFalse(p == mergedLabel.getPackage()); |
| // Assert that the bi-directional relationship points to the |
| // newly merged entity |
| assertTrue(mergedLabel == mergedLabel.getPackage().getLabel()); |
| assertFalse(l == mergedLabel.getPackage().getLabel()); |
| em.remove(mergedLabel); |
| em.getTransaction().commit(); |
| } finally { |
| if (em != null) { |
| if (em.getTransaction().isActive()) |
| em.getTransaction().rollback(); |
| em.close(); |
| } |
| } |
| } |
| |
| /** |
| * Verify a merge graph is correct when an entity uses a detached state field. When |
| * a detached state field is used, the entity does not get populated with a DetachedStateManager |
| * upon detachment. Same as testCascadeMergeDetachState, except merge is on ShipPackage2, |
| * which contains the cascade instead of Label2. |
| * |
| * ShipPackage: Has DetachedStateManager after detach. |
| * Label: Detached state field - no DSM after detach. |
| * Initial graph: ShipPackage2 <--> Label2 |
| * Merge: ShipPackage2 |
| * Verify after merge: ShipPackage2' <--> Label2' |
| */ |
| public void testCascadeMergeDetachState2() { |
| EntityManager em = emf.createEntityManager(); |
| |
| try { |
| // Create simple bi-di graph |
| ShipPackage2 p = new ShipPackage2(); |
| Label2 l = new Label2(p); |
| p.setLabel2(l); |
| |
| // Persist |
| em.getTransaction().begin(); |
| em.persist(p); |
| em.getTransaction().commit(); |
| |
| // Detach |
| em.clear(); |
| assertFalse(em.contains(l)); |
| assertFalse(em.contains(p)); |
| assertFalse(em.contains(l.getPackage2())); |
| assertFalse(em.contains(p.getLabel2())); |
| |
| em.getTransaction().begin(); |
| ShipPackage2 mergedPackage = em.merge(p); |
| |
| assertFalse(mergedPackage == p); |
| assertFalse(l == mergedPackage.getLabel2()); |
| // Assert that the bi-directional relationship points to the |
| // newly merged entity |
| assertTrue(mergedPackage == mergedPackage.getLabel2().getPackage2()); |
| assertFalse(p == mergedPackage.getLabel2().getPackage2()); |
| em.remove(mergedPackage); |
| em.getTransaction().commit(); |
| } finally { |
| if (em != null) { |
| if (em.getTransaction().isActive()) |
| em.getTransaction().rollback(); |
| em.close(); |
| } |
| } |
| } |
| |
| /** |
| * Verify a merge graph is correct when multiple entities of a complex |
| * graph use a detached state field. When a detached state field is used, |
| * the entity does not get populated with a DetachedStateManager upon |
| * detachment, but merge should still succeed. |
| */ |
| public void testCascadeMergeDetachStateComplex() { |
| EntityManager em = emf.createEntityManager(); |
| |
| try { |
| Car c1 = new Car(); |
| Car c2 = new Car(); |
| Make mk1 = new Make(); |
| ArrayList<Model> models = new ArrayList<>(); |
| Model md1 = new Model(); |
| models.add(md1); |
| Model md2 = new Model(); |
| models.add(md2); |
| |
| //populate bidirectional relationships |
| c1.setModel(md1); |
| c2.setModel(md2); |
| md1.setCar(c1); |
| md1.setMake(mk1); |
| md2.setCar(c2); |
| md1.setMake(mk1); |
| mk1.setModels(models); |
| |
| // Persist car1 - will cascade |
| em.getTransaction().begin(); |
| em.persist(c1); |
| em.getTransaction().commit(); |
| |
| Object[] entities = new Object[] { c1, c2, mk1, md1, md2 }; |
| // detach all |
| em.clear(); |
| // verify all entities are detached and references to them |
| // are also detached. |
| verifyDetached(em, entities); |
| |
| em.getTransaction().begin(); |
| // Merge model back in and verify all entities are newly merged entities |
| Model mergedModel = em.merge(md1); |
| assertFalse(mergedModel == md1); |
| assertFalse(mergedModel.getMake() == mk1); |
| List<Model> mds = mergedModel.getMake().getModels(); |
| assertTrue(mds.contains(mergedModel)); |
| assertFalse(c1 == mergedModel.getCar()); |
| assertTrue(mergedModel.getCar().getModel() == mergedModel); |
| em.remove(mergedModel); |
| em.getTransaction().commit(); |
| } finally { |
| if (em != null) { |
| if (em.getTransaction().isActive()) |
| em.getTransaction().rollback(); |
| em.close(); |
| } |
| } |
| } |
| |
| private void verifyDetached(EntityManager em, Object[] entities) { |
| for (Object entity : entities) { |
| assertFalse(em.contains(entity)); |
| if (entity instanceof Car) { |
| Car c = (Car)entity; |
| assertFalse(em.contains(c.getModel())); |
| } |
| else if (entity instanceof Make) { |
| Make m = (Make)entity; |
| List<Model> mds = m.getModels(); |
| for (Model md : mds) { |
| assertFalse(em.contains(md)); |
| } |
| } |
| else if (entity instanceof Model) { |
| Model m = (Model)entity; |
| assertFalse(em.contains(m.getCar())); |
| assertFalse(em.contains(m.getMake())); |
| } |
| } |
| } |
| |
| } |