/* * Copyright (C) 2015 Red Hat, Inc. and/or its affiliates. * * Licensed 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.jboss.errai.jpa.test.client; import java.util.ArrayList; import java.util.List; import javax.persistence.EntityManager; import org.jboss.errai.databinding.client.api.DataBinder; import org.jboss.errai.ioc.client.Container; import org.jboss.errai.ioc.client.container.IOC; import org.jboss.errai.jpa.client.local.ErraiEntityManager; import org.jboss.errai.jpa.rebind.ErraiEntityManagerGenerator; import org.jboss.errai.jpa.test.client.res.JpaClientTestCase; import org.jboss.errai.jpa.test.entity.CascadeFrom; import org.jboss.errai.jpa.test.entity.CascadeThirdGeneration; import org.jboss.errai.jpa.test.entity.CascadeTo; /** * Tests the cascade behaviour for entity state changes under Errai's JPA EntityManager. * <p> * Note that there is a {@link HibernateCascadeTest subclass of this test} that runs * all the same checks against Hibernate, as a sanity check that we're testing * for actual JPA-sanctioned and JPA-compatible behaviour. * * @author Jonathan Fuerth <jfuerth@gmail.com> */ public class ErraiCascadeTest extends JpaClientTestCase { @Override public String getModuleName() { return "org.jboss.errai.jpa.test.JpaTest"; } protected EntityManager getEntityManagerAndClearStorageBackend() { final JpaTestClient testClient = JpaTestClient.INSTANCE; assertNotNull(testClient); assertNotNull(testClient.entityManager); ((ErraiEntityManager) testClient.entityManager).removeAll(); return testClient.entityManager; } @Override protected void gwtSetUp() throws Exception { super.gwtSetUp(); // We need to bootstrap the IoC container manually because GWTTestCase // doesn't call onModuleLoad() for us. new Container().bootstrapContainer(); } @Override protected void gwtTearDown() throws Exception { Container.reset(); IOC.reset(); } /** * Creates a CascadeFrom object, fills in all its related CascadeTo * attributes, persists everything, and flushes the entity manager. Useful as * a first step in most tests of cascade behaviour. * * @param em * The entity manager to persist with * @return a newly-created CascadeFrom, persistent and managed by em, with all * related CascadeTo attributes likewise persisted and managed by em. */ private static CascadeFrom createFullyPersistedObject(final EntityManager em) { final CascadeFrom from = new CascadeFrom(); from.setAll(new CascadeTo()); from.setDetach(new CascadeTo()); from.setMerge(new CascadeTo()); from.setNone(new CascadeTo()); from.setPersist(new CascadeTo()); from.setRefresh(new CascadeTo()); from.setRemove(new CascadeTo()); em.persist(from.getAll()); em.persist(from.getDetach()); em.persist(from.getMerge()); em.persist(from.getNone()); em.persist(from.getPersist()); em.persist(from.getRefresh()); em.persist(from.getRemove()); em.persist(from); em.flush(); assertTrue(em.contains(from)); assertTrue(em.contains(from.getAll())); assertTrue(em.contains(from.getDetach())); assertTrue(em.contains(from.getMerge())); assertTrue(em.contains(from.getNone())); assertTrue(em.contains(from.getPersist())); assertTrue(em.contains(from.getRefresh())); assertTrue(em.contains(from.getRemove())); return from; } /** * Tests that the entity manager was injected into the testing class. If this * test fails, the likely cause is that the * {@link ErraiEntityManagerGenerator} failed to output a compilable class. In * that case, try re-running this test with * {@code -Derrai.codegen.permissive=true} and */ public void testEntityManagerInjection() throws Exception { getEntityManagerAndClearStorageBackend(); // has its own assertions } public void testCascadePersist() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom from = new CascadeFrom(); from.setAll(new CascadeTo()); from.setPersist(new CascadeTo()); em.persist(from); em.flush(); assertTrue(em.contains(from)); assertTrue(em.contains(from.getAll())); assertTrue(em.contains(from.getPersist())); } public void testCascadePersistCollection() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom from = new CascadeFrom(); from.setAllCollection(listOfCascadeTo(3)); from.setPersistCollection(listOfCascadeTo(3)); em.persist(from); em.flush(); assertTrue(em.contains(from)); for (final CascadeTo child : from.getAllCollection()) { assertTrue(em.contains(child)); } for (final CascadeTo child : from.getPersistCollection()) { assertTrue(em.contains(child)); } } public void testCascadePersistFailsWithNonCascadedNewEntity() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom from = new CascadeFrom(); from.setAll(new CascadeTo()); from.setPersist(new CascadeTo()); from.setNone(new CascadeTo()); // this should lead to an error try { em.persist(from); em.flush(); fail("Expected IllegalStateException"); } catch (final IllegalStateException ex) { // check for name of offending relationship assertTrue("Exception message doesn't mention bad relationship: " + ex.getMessage(), ex.getMessage().contains("none")); } } public void testCascadePersistFailsWithNonCascadedNewEntityInCollection() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom from = new CascadeFrom(); from.setAllCollection(listOfCascadeTo(3)); from.setPersistCollection(listOfCascadeTo(3)); from.setNoneCollection(listOfCascadeTo(3)); // this should lead to an error try { em.persist(from); em.flush(); fail("Expected IllegalStateException"); } catch (final IllegalStateException ex) { // this is what Errai throws. We check for the name of the offending relationship // with Hibernate, we're not picky about the exact message :) assertTrue("Exception message doesn't mention bad relationship: " + ex.getMessage(), ex.getMessage().contains("noneCollection") || ex.getMessage().contains("org.hibernate.TransientObjectException")); } } // It's not 100% clear from the spec that this should fail in the same way // as persist, but this test is consistent with the behaviour of Hibernate. public void testCascadeMergeFailsWithNonCascadedNewEntity() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom from = new CascadeFrom(); from.setAll(new CascadeTo()); from.setMerge(new CascadeTo()); from.setNone(new CascadeTo()); // this should lead to an error try { em.merge(from); em.flush(); fail("Expected IllegalStateException"); } catch (final IllegalStateException ex) { // check for name of offending relationship assertTrue("Exception message doesn't mention bad relationship: " + ex.getMessage(), ex.getMessage().contains("none")); } } public void testCascadeMergeFailsWithNonCascadedNewEntityInCollection() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom from = new CascadeFrom(); from.setAllCollection(listOfCascadeTo(3)); from.setMergeCollection(listOfCascadeTo(3)); from.setNoneCollection(listOfCascadeTo(3)); // this should lead to an error try { em.merge(from); em.flush(); fail("Expected IllegalStateException"); } catch (final IllegalStateException ex) { // this is what Errai throws. We check for the name of the offending relationship // with hibernate, we're not picky about the exact message :) assertTrue("Exception message doesn't mention bad relationship: " + ex.getMessage(), ex.getMessage().contains("noneCollection") || ex.getMessage().contains("org.hibernate.TransientObjectException")); } } /** * Tests this rule from the JPA spec 3.2.7.1 Merging Detached Entity State: * <blockquote> * If X is a detached entity, the state of X is copied onto a pre-existing managed * entity instance X' of the same identity or a new managed copy X' of X is created. * </blockquote> */ public void testCascadeMergeRule1WhenPreExistingManagedEntityIsInPersistenceContext() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom preExistingEntity = new CascadeFrom(); em.persist(preExistingEntity); em.flush(); assertTrue(em.contains(preExistingEntity)); final CascadeFrom x = new CascadeFrom(); x.setId(preExistingEntity.getId()); final CascadeFrom xPrime = em.merge(x); em.flush(); assertSame(preExistingEntity, xPrime); assertNotSame(x, xPrime); assertTrue(em.contains(xPrime)); assertFalse(em.contains(x)); } /** * Tests this rule from the JPA spec 3.2.7.1 Merging Detached Entity State: * <blockquote> * If X is a detached entity, the state of X is copied onto a pre-existing managed * entity instance X' of the same identity or a new managed copy X' of X is created. * </blockquote> */ public void testCascadeMergeRule1WhenPreExistingManagedEntityIsNotInPersistenceContext() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom preExistingEntity = new CascadeFrom(); em.persist(preExistingEntity); em.flush(); em.clear(); assertFalse(em.contains(preExistingEntity)); final CascadeFrom x = new CascadeFrom(); x.setId(preExistingEntity.getId()); final CascadeFrom xPrime = em.merge(x); em.flush(); assertNotSame(preExistingEntity, xPrime); assertNotSame(x, xPrime); assertTrue(em.contains(xPrime)); assertFalse(em.contains(x)); assertFalse(em.contains(preExistingEntity)); } /** * Tests this rule from the JPA spec 3.2.7.1 Merging Detached Entity State: * <blockquote> * If X is a new entity instance, a new managed entity instance X' is created * and the state of X is copied into the new managed entity instance X'. * </blockquote> */ public void testCascadeMergeRule2() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom x = new CascadeFrom(); final CascadeFrom xPrime = em.merge(x); em.flush(); assertNotSame(x, xPrime); assertTrue(em.contains(xPrime)); assertFalse(em.contains(x)); } /** * Tests this rule from the JPA spec 3.2.7.1 Merging Detached Entity State: * <blockquote> * If X is a removed entity instance, an IllegalArgumentException will be * thrown by the merge operation (or the transaction commit will fail). * </blockquote> */ public void testCascadeMergeRule3() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom x = new CascadeFrom(); em.persist(x); em.flush(); em.remove(x); try { em.merge(x); em.flush(); fail("Expected IllegalArgumentException"); } catch (final IllegalArgumentException e) { // expected } } /** * Tests this rule from the JPA spec 3.2.7.1 Merging Detached Entity State: * <blockquote> * If X is a managed entity, it is ignored by the merge operation, however, * the merge operation is cascaded to entities referenced by relationships * from X if these relationships have been annotated with the cascade * element value cascade=MERGE or cascade=ALL annotation. * </blockquote> */ public void testCascadeMergeRule4WithSingularAssociations() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom x = new CascadeFrom(); em.persist(x); em.flush(); x.setAll(new CascadeTo()); x.setDetach(new CascadeTo()); x.setMerge(new CascadeTo()); x.setNone(new CascadeTo()); x.setPersist(new CascadeTo()); x.setRefresh(new CascadeTo()); x.setRemove(new CascadeTo()); // have to persist the ones that won't cascade automatically em.persist(x.getDetach()); em.persist(x.getNone()); em.persist(x.getRefresh()); em.persist(x.getRemove()); em.persist(x.getPersist()); final CascadeFrom xPrime = em.merge(x); em.flush(); assertSame(x, xPrime); em.clear(); final CascadeFrom xFetched = em.find(CascadeFrom.class, x.getId()); assertNotSame(x.getAll(), xFetched.getAll()); assertNotSame(x.getDetach(), xFetched.getDetach()); assertNotSame(x.getMerge(), xFetched.getMerge()); assertNotSame(x.getNone(), xFetched.getNone()); assertNotSame(x.getPersist(), xFetched.getPersist()); assertNotSame(x.getRefresh(), xFetched.getRefresh()); assertNotSame(x.getRemove(), xFetched.getRemove()); assertEquals(x.getAll(), xFetched.getAll()); assertEquals(x.getDetach(), xFetched.getDetach()); assertEquals(x.getMerge(), xFetched.getMerge()); assertEquals(x.getNone(), xFetched.getNone()); assertEquals(x.getPersist(), xFetched.getPersist()); assertEquals(x.getRefresh(), xFetched.getRefresh()); assertEquals(x.getRemove(), xFetched.getRemove()); } /** * Tests this rule from the JPA spec 3.2.7.1 Merging Detached Entity State: * <blockquote> * If X is a managed entity, it is ignored by the merge operation, however, * the merge operation is cascaded to entities referenced by relationships * from X if these relationships have been annotated with the cascade * element value cascade=MERGE or cascade=ALL annotation. * </blockquote> */ public void testCascadeMergeRule4WithPluralAssociations() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom x = new CascadeFrom(); em.persist(x); em.flush(); x.setAllCollection(listOfCascadeTo(3)); x.setDetachCollection(listOfCascadeTo(3)); x.setMergeCollection(listOfCascadeTo(3)); x.setNoneCollection(listOfCascadeTo(3)); x.setPersistCollection(listOfCascadeTo(3)); x.setRefreshCollection(listOfCascadeTo(3)); x.setRemoveCollection(listOfCascadeTo(3)); // have to persist the ones that won't cascade automatically for (int i = 0; i < 3; i++) { em.persist(x.getDetachCollection().get(i)); em.persist(x.getNoneCollection().get(i)); em.persist(x.getRefreshCollection().get(i)); em.persist(x.getRemoveCollection().get(i)); em.persist(x.getPersistCollection().get(i)); } final CascadeFrom xPrime = em.merge(x); em.flush(); assertSame(x, xPrime); em.clear(); final CascadeFrom xFetched = em.find(CascadeFrom.class, x.getId()); for (int i = 0; i < 3; i++) { assertNotSame(x.getAllCollection().get(i), xFetched.getAllCollection().get(i)); assertNotSame(x.getDetachCollection().get(i), xFetched.getDetachCollection().get(i)); assertNotSame(x.getMergeCollection().get(i), xFetched.getMergeCollection().get(i)); assertNotSame(x.getNoneCollection().get(i), xFetched.getNoneCollection().get(i)); assertNotSame(x.getPersistCollection().get(i), xFetched.getPersistCollection().get(i)); assertNotSame(x.getRefreshCollection().get(i), xFetched.getRefreshCollection().get(i)); assertNotSame(x.getRemoveCollection().get(i), xFetched.getRemoveCollection().get(i)); assertEquals(x.getAllCollection().get(i), xFetched.getAllCollection().get(i)); assertEquals(x.getDetachCollection().get(i), xFetched.getDetachCollection().get(i)); assertEquals(x.getMergeCollection().get(i), xFetched.getMergeCollection().get(i)); assertEquals(x.getNoneCollection().get(i), xFetched.getNoneCollection().get(i)); assertEquals(x.getPersistCollection().get(i), xFetched.getPersistCollection().get(i)); assertEquals(x.getRefreshCollection().get(i), xFetched.getRefreshCollection().get(i)); assertEquals(x.getRemoveCollection().get(i), xFetched.getRemoveCollection().get(i)); } } /** * Tests these rules from the JPA spec 3.2.7.1 Merging Detached Entity State: * <p> * Rule 5: * <blockquote> * For all entities Y referenced by relationships from X having the cascade * element value cascade=MERGE or cascade=ALL, Y is merged recursively as * Y'. For all such Y referenced by X, X' is set to reference Y'. (Note * that if X is managed then X is the same object as X'.) * </blockquote> * * And Rule 6: * <blockquote> * If X is an entity merged to X', with a reference to another entity Y, * where cascade=MERGE or cascade=ALL is not specified, then navigation of * the same association from X' yields a reference to a managed object Y' * with the same persistent identity as Y. * </blockquote> * <p> * Note that we're not entirely clear on what this means, or how it differs * from rule 5. We're going with "do what Hibernate does." */ public void testCascadeMergeRules5And6() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom x = new CascadeFrom(); x.setAll(new CascadeTo()); x.setDetach(new CascadeTo()); x.setMerge(new CascadeTo()); x.setNone(new CascadeTo()); x.setPersist(new CascadeTo()); x.setRefresh(new CascadeTo()); x.setRemove(new CascadeTo()); // have to persist the ones that won't cascade automatically em.persist(x.getDetach()); em.persist(x.getNone()); em.persist(x.getRefresh()); em.persist(x.getRemove()); em.persist(x.getPersist()); final CascadeFrom xPrime = em.merge(x); em.flush(); // x, x.all, and x.merge were not in managed state (they were new) so the merged copies should be different instances assertNotSame(x, xPrime); assertNotSame(x.getAll(), xPrime.getAll()); assertNotSame(x.getMerge(), xPrime.getMerge()); assertEquals(x.getAll().getString(), xPrime.getAll().getString()); assertEquals(x.getMerge().getString(), xPrime.getMerge().getString()); // the previously managed instances should have been adopted by the merge // ("Note that if X is managed then X is the same object as X'") assertSame(x.getDetach(), xPrime.getDetach()); assertSame(x.getNone(), xPrime.getNone()); assertSame(x.getRefresh(), xPrime.getRefresh()); assertSame(x.getRemove(), xPrime.getRemove()); assertSame(x.getPersist(), xPrime.getPersist()); // ensure they were actually saved to the database em.clear(); final CascadeFrom xFetched = em.find(CascadeFrom.class, xPrime.getId()); assertNotSame(x.getAll(), xFetched.getAll()); assertNotSame(x.getDetach(), xFetched.getDetach()); assertNotSame(x.getMerge(), xFetched.getMerge()); assertNotSame(x.getNone(), xFetched.getNone()); assertNotSame(x.getPersist(), xFetched.getPersist()); assertNotSame(x.getRefresh(), xFetched.getRefresh()); assertNotSame(x.getRemove(), xFetched.getRemove()); } /** * Tests these rules from the JPA spec 3.2.7.1 Merging Detached Entity State: * <p> * Rule 5: * <blockquote> * For all entities Y referenced by relationships from X having the cascade * element value cascade=MERGE or cascade=ALL, Y is merged recursively as * Y'. For all such Y referenced by X, X' is set to reference Y'. (Note * that if X is managed then X is the same object as X'.) * </blockquote> * * And Rule 6: * <blockquote> * If X is an entity merged to X', with a reference to another entity Y, * where cascade=MERGE or cascade=ALL is not specified, then navigation of * the same association from X' yields a reference to a managed object Y' * with the same persistent identity as Y. * </blockquote> * <p> * Note that we're not entirely clear on what this means, or how it differs * from rule 5. We're going with "do what Hibernate does." */ public void testCascadeMergeRules5And6WithCollections() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom x = new CascadeFrom(); x.setAllCollection(listOfCascadeTo(3)); x.setDetachCollection(listOfCascadeTo(3)); x.setMergeCollection(listOfCascadeTo(3)); x.setNoneCollection(listOfCascadeTo(3)); x.setPersistCollection(listOfCascadeTo(3)); x.setRefreshCollection(listOfCascadeTo(3)); x.setRemoveCollection(listOfCascadeTo(3)); // have to persist the ones that won't cascade automatically for (int i = 0; i < 3; i++) { em.persist(x.getDetachCollection().get(i)); em.persist(x.getNoneCollection().get(i)); em.persist(x.getRefreshCollection().get(i)); em.persist(x.getRemoveCollection().get(i)); em.persist(x.getPersistCollection().get(i)); } final CascadeFrom xPrime = em.merge(x); em.flush(); assertNotSame(x, xPrime); for (int i = 0; i < 3; i++) { // these ones were not already managed, so merge should have made new copies assertNotSame(x.getAllCollection().get(i), xPrime.getAllCollection().get(i)); assertNotSame(x.getMergeCollection().get(i), xPrime.getMergeCollection().get(i)); assertEquals(x.getAllCollection().get(i).getString(), xPrime.getAllCollection().get(i).getString()); assertEquals(x.getMergeCollection().get(i).getString(), xPrime.getMergeCollection().get(i).getString()); // the previously managed instances should have been adopted by the merge // ("Note that if X is managed then X is the same object as X'") assertSame(x.getDetachCollection().get(i), xPrime.getDetachCollection().get(i)); assertSame(x.getNoneCollection().get(i), xPrime.getNoneCollection().get(i)); assertSame(x.getPersistCollection().get(i), xPrime.getPersistCollection().get(i)); assertSame(x.getRefreshCollection().get(i), xPrime.getRefreshCollection().get(i)); assertSame(x.getRemoveCollection().get(i), xPrime.getRemoveCollection().get(i)); } } public void testCascadedMergeCopiesEntityState() { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom x = new CascadeFrom(); x.setAll(new CascadeTo()); x.setMerge(new CascadeTo()); x.setPersist(new CascadeTo()); em.persist(x.getMerge()); em.persist(x); em.flush(); em.clear(); x.getAll().setString("updated string"); x.getMerge().setString("updated merge"); final CascadeThirdGeneration cascadeAgain = new CascadeThirdGeneration(); cascadeAgain.setString("3rd gen"); x.getMerge().setCascadeAgain(cascadeAgain); x.getPersist().setString("updated persist"); final CascadeFrom xPrime = em.merge(x); em.flush(); assertNotSame(x, xPrime); assertNotSame(x.getAll(), xPrime.getAll()); assertEquals("updated string", xPrime.getAll().getString()); assertNotSame(x.getMerge(), xPrime.getMerge()); assertEquals("updated merge", xPrime.getMerge().getString()); assertNotSame(x.getMerge().getCascadeAgain(), xPrime.getMerge().getCascadeAgain()); assertEquals("3rd gen", xPrime.getMerge().getCascadeAgain().getString()); // this one should not have the new state because the cascade rule doesn't include merge assertNotSame(x.getPersist(), xPrime.getPersist()); assertNull(xPrime.getPersist().getString()); } public void testCascadedMergeCopiesEntityStateInCollections() { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom x = new CascadeFrom(); x.setAllCollection(listOfCascadeTo(3)); x.setMergeCollection(listOfCascadeTo(3)); x.setPersistCollection(listOfCascadeTo(3)); for (final CascadeTo child : x.getMergeCollection()) { em.persist(child); } em.persist(x); em.flush(); em.clear(); for (int i = 0; i < 3; i++) { x.getAllCollection().get(i).setString("updated string " + i); x.getMergeCollection().get(i).setString("updated merge " + i); final CascadeThirdGeneration cascadeAgain = new CascadeThirdGeneration(); cascadeAgain.setString("3rd gen " + i); x.getMergeCollection().get(i).setCascadeAgain(cascadeAgain); x.getPersistCollection().get(i).setString("updated persist " + i); } final CascadeFrom xPrime = em.merge(x); em.flush(); assertNotSame(x, xPrime); for (int i = 0; i < 3; i++) { assertNotSame(x.getAllCollection().get(i), xPrime.getAllCollection().get(i)); assertEquals("updated string " + i, xPrime.getAllCollection().get(i).getString()); assertNotSame(x.getMergeCollection().get(i), xPrime.getMergeCollection().get(i)); assertEquals("updated merge " + i, xPrime.getMergeCollection().get(i).getString()); assertNotSame(x.getMergeCollection().get(i).getCascadeAgain(), xPrime.getMergeCollection().get(i).getCascadeAgain()); assertEquals("3rd gen " + i, xPrime.getMergeCollection().get(i).getCascadeAgain().getString()); // this one should not have the new state because the cascade rule doesn't include merge assertNotSame(x.getPersistCollection().get(i), xPrime.getPersistCollection().get(i)); assertEquals("string " + i, xPrime.getPersistCollection().get(i).getString()); } } public void testCascadeRemove() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom from = createFullyPersistedObject(em); em.remove(from); assertFalse(em.contains(from)); assertFalse(em.contains(from.getAll())); assertTrue(em.contains(from.getDetach())); assertTrue(em.contains(from.getMerge())); assertTrue(em.contains(from.getNone())); assertTrue(em.contains(from.getPersist())); assertTrue(em.contains(from.getRefresh())); assertFalse(em.contains(from.getRemove())); } public void testCascadeDetach() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom from = createFullyPersistedObject(em); em.detach(from); assertFalse(em.contains(from)); assertFalse(em.contains(from.getAll())); assertFalse(em.contains(from.getDetach())); assertTrue(em.contains(from.getMerge())); assertTrue(em.contains(from.getNone())); assertTrue(em.contains(from.getPersist())); assertTrue(em.contains(from.getRefresh())); assertTrue(em.contains(from.getRemove())); } /** * Regression test for ERRAI-629. */ public void testCascadeMergeBindableProxyIntoItsOwnTarget() throws Exception { final EntityManager em = getEntityManagerAndClearStorageBackend(); final CascadeFrom from = new CascadeFrom(); em.persist(from); final DataBinder<CascadeFrom> dataBinder = DataBinder.forModel(from); dataBinder.getModel().setMergeCollection(listOfCascadeTo(3)); assertNotNull(dataBinder.getModel().getMergeCollection().get(0).getString()); em.merge(dataBinder.getModel()); assertNotNull(dataBinder.getModel().getMergeCollection().get(0).getString()); em.flush(); em.clear(); final CascadeFrom xFetched = em.find(CascadeFrom.class, from.getId()); assertNotNull(xFetched.getMergeCollection()); assertEquals(3, xFetched.getMergeCollection().size()); assertFalse(xFetched.getMergeCollection().get(0).getId() == 0); assertEquals("string 0", xFetched.getMergeCollection().get(0).getString()); } /** * Makes a list of new CascadeTo objects. * * @param size The number of objects that should be in the returned list. Must be 0 or more. */ private static List<CascadeTo> listOfCascadeTo(final int size) { final List<CascadeTo> l = new ArrayList<>(size); for (int i = 0; i < size; i++) { final CascadeTo cascadeTo = new CascadeTo(); cascadeTo.setString("string " + i); l.add(cascadeTo); } return l; } }