/* * 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 { 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>(); 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())); } } } }