| /* |
| * 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 java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.apache.openjpa.conf.Compatibility; |
| import org.apache.openjpa.meta.ClassMetaData; |
| import org.apache.openjpa.meta.MetaDataRepository; |
| import org.apache.openjpa.meta.ValueMetaData; |
| import org.apache.openjpa.persistence.OpenJPAEntityManager; |
| import org.apache.openjpa.persistence.detachment.model.DMCustomer; |
| import org.apache.openjpa.persistence.detachment.model.DMCustomerInventory; |
| import org.apache.openjpa.persistence.detachment.model.DMItem; |
| import org.apache.openjpa.persistence.test.SingleEMFTestCase; |
| |
| /** |
| * Tests detachment behavior according to JPA 2.0 Specification. The primary |
| * changes in detachment behavior from the existing OpenJPA behavior are |
| * i. detach(x) does not flush if x is dirty |
| * ii. detach(x) removes x from persistence context |
| * iii. detach(x) propagates via CascadeType.DETACH. It is not clear how that |
| * impacts the detach graph. So currently, detach graph is same as 'loaded'. |
| * |
| * The test uses a 'domain model' with following cascade relation |
| * |
| * ALL PERSIST, MERGE, REFRESH |
| * Customer --------> Inventory ----------------------> Item |
| * <------- |
| * MERGE |
| * |
| * @author Pinaki Poddar |
| * |
| */ |
| public class TestDetach extends SingleEMFTestCase { |
| OpenJPAEntityManager em; |
| DMCustomer root; |
| |
| @Override |
| public void setUp() { |
| super.setUp(DMCustomer.class, DMCustomerInventory.class, DMItem.class, |
| CLEAR_TABLES); |
| |
| Compatibility compat = |
| emf.getConfiguration().getCompatibilityInstance(); |
| compat.setCopyOnDetach(false); |
| compat.setFlushBeforeDetach(false); |
| |
| em = emf.createEntityManager(); |
| root = createData(); |
| } |
| |
| public void testDetachCascadeIsSet() { |
| MetaDataRepository repos = emf.getConfiguration() |
| .getMetaDataRepositoryInstance(); |
| ClassMetaData meta = repos.getCachedMetaData(DMCustomer.class); |
| assertEquals(ValueMetaData.CASCADE_NONE, |
| meta.getField("firstName").getCascadeDetach()); |
| assertEquals(ValueMetaData.CASCADE_IMMEDIATE, meta.getField( |
| "customerInventories").getElement().getCascadeDetach()); |
| |
| meta = repos.getCachedMetaData(DMCustomerInventory.class); |
| assertEquals(ValueMetaData.CASCADE_NONE, |
| meta.getField("customer").getCascadeDetach()); |
| assertEquals(ValueMetaData.CASCADE_NONE, |
| meta.getField("item").getCascadeDetach()); |
| |
| } |
| |
| public void testDetachRemovesEntityAndCascadedRelationFromContext() { |
| em.getTransaction().begin(); |
| |
| DMCustomer pc = em.find(DMCustomer.class, root.getId()); |
| List<DMCustomerInventory> inventories = pc.getCustomerInventories(); |
| DMItem item = inventories.get(0).getItem(); |
| |
| assertNotDetached(pc); |
| for (DMCustomerInventory i : inventories) assertNotDetached(i); |
| assertNotDetached(item); |
| |
| em.detach(pc); |
| |
| assertDetached(pc); |
| for (DMCustomerInventory i : inventories) assertDetached(i); |
| |
| em.getTransaction().rollback(); |
| |
| assertNotNull(pc.getFirstName()); |
| } |
| |
| public void testDetachingDirtyEntityDoesNotImplicitlyFlush() { |
| em.getTransaction().begin(); |
| DMCustomer pc = em.find(DMCustomer.class, root.getId()); |
| String original = pc.getLastName(); |
| pc.setLastName("Changed That Should not be Saved"); |
| |
| em.detach(pc); |
| em.getTransaction().commit(); |
| |
| DMCustomer pc2 = em.find(DMCustomer.class, root.getId()); |
| assertNotNull(pc2); |
| assertEquals(original, pc2.getLastName()); |
| } |
| |
| public void testDetachingNewEntityIsIgnored() { |
| em.getTransaction().begin(); |
| DMCustomer pc = em.find(DMCustomer.class, root.getId()); |
| List<DMCustomerInventory> inventories = pc.getCustomerInventories(); |
| |
| DMCustomer newPC = new DMCustomer(); |
| newPC.setCustomerInventories(inventories); |
| for (DMCustomerInventory inventory : inventories) |
| inventory.setCustomer(newPC); |
| |
| em.detach(newPC); |
| for (DMCustomerInventory inventory : inventories) { |
| assertNotDetached(inventory); |
| } |
| em.getTransaction().rollback(); |
| } |
| |
| public void testDetachingDetachedEntityIsIgnored() { |
| em.getTransaction().begin(); |
| DMCustomer pc = em.find(DMCustomer.class, root.getId()); |
| List<DMCustomerInventory> inventories = pc.getCustomerInventories(); |
| |
| em.detach(pc); |
| DMCustomer detached = pc; |
| assertDetached(detached); |
| for (DMCustomerInventory inventory : inventories) { |
| assertDetached(inventory); |
| } |
| |
| List<DMCustomerInventory> newInventories = |
| new ArrayList<>(inventories); |
| DMCustomerInventory newInventory = new DMCustomerInventory(); |
| newInventory.setCustomer(detached); |
| newInventories.add(newInventory); |
| detached.setCustomerInventories(newInventories); |
| em.persist(newInventory); |
| assertNotDetached(newInventory); |
| |
| em.detach(detached); |
| assertDetached(detached); |
| assertEquals(inventories.size()+1, newInventories.size()); |
| for (DMCustomerInventory inventory : newInventories) { |
| if (inventory == newInventory) |
| assertNotDetached(inventory); |
| else |
| assertDetached(inventory); |
| } |
| em.getTransaction().rollback(); |
| } |
| |
| |
| public void testFlushingBeforeDetachingSavesChange() { |
| |
| } |
| |
| public void testManagedEntityContinuesToReferDetachedEntities() { |
| em.getTransaction().begin(); |
| |
| DMCustomer pc = em.find(DMCustomer.class, root.getId()); |
| List<DMCustomerInventory> inventories = pc.getCustomerInventories(); |
| DMItem item = inventories.get(1).getItem(); |
| |
| em.detach(inventories.get(0)); |
| |
| DMCustomerInventory attached0 = inventories.get(0); |
| DMCustomerInventory attached1 = inventories.get(1); |
| |
| assertSame(pc.getCustomerInventories().get(0), attached0); |
| assertSame(pc.getCustomerInventories().get(1), attached1); |
| |
| em.getTransaction().rollback(); |
| } |
| |
| DMCustomer createData() { |
| DMItem item1 = new DMItem(); |
| DMItem item2 = new DMItem(); |
| item1.setName("item-1"); item1.setPrice(100.0); |
| item2.setName("item-2"); item2.setPrice(200.0); |
| |
| DMCustomerInventory inventory1 = new DMCustomerInventory(); |
| DMCustomerInventory inventory2 = new DMCustomerInventory(); |
| inventory1.setItem(item1); inventory1.setQuantity(10); |
| inventory2.setItem(item2); inventory2.setQuantity(20); |
| DMCustomer customer = new DMCustomer(); |
| customer.setFirstName("Detached"); customer.setLastName("Customer"); |
| customer.setCustomerInventories(Arrays.asList( |
| new DMCustomerInventory[]{inventory1,inventory2})); |
| inventory1.setCustomer(customer); |
| inventory2.setCustomer(customer); |
| |
| em.getTransaction().begin(); |
| em.persist(customer); |
| em.getTransaction().commit(); |
| em.clear(); |
| |
| return customer; |
| } |
| |
| void assertDetached(Object pc) { |
| assertTrue(pc + " should be detached", em.isDetached(pc)); |
| assertFalse(pc + " should not be in cache", em.contains(pc)); |
| } |
| |
| void assertNotDetached(Object pc) { |
| assertFalse(pc + " should not be detached", em.isDetached(pc)); |
| assertTrue(pc + " should be in cache", em.contains(pc)); |
| } |
| } |