/******************************************************************************* * Copyright (c) 2005, 2015 SAP. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * SAP - initial API and implementation ******************************************************************************/ package org.eclipse.persistence.testing.tests.wdf.jpa1.entitymanager; import java.io.IOException; import java.sql.Date; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.PersistenceException; import javax.persistence.TransactionRequiredException; import junit.framework.Assert; import org.eclipse.persistence.testing.framework.wdf.AbstractBaseTest; import org.eclipse.persistence.testing.framework.wdf.JPAEnvironment; import org.eclipse.persistence.testing.framework.wdf.ToBeInvestigated; import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Department; import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Employee; import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Hobby; import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Review; import org.eclipse.persistence.testing.models.wdf.jpa1.timestamp.Nasty; import org.eclipse.persistence.testing.models.wdf.jpa1.timestamp.Timestamp; import org.eclipse.persistence.testing.tests.wdf.jpa1.JPA1Base; import org.junit.Ignore; import org.junit.Test; public class TestMerge extends JPA1Base { @Test public void testMergeNew() { /* * 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'. */ final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); env.evictAll(em); try { Department dep = new Department(1, "NEW"); env.beginTransaction(em); Department dep2 = em.merge(dep); checkDepartment(dep2, 1, "NEW"); env.commitTransactionAndClear(em); verify(!em.contains(dep), "entity manager contains department -> it cannot be detached"); verify(!em.contains(dep2), "entity manager contains department -> it cannot be detached"); verifyExistence(em, 1, "NEW"); } finally { closeEntityManager(em); } } private void checkDepartment(Department department, int id, String name) { verify(department != null, "department is null"); verify(id == department.getId(), "department has wrong id: " + department.getId()); verify(name.equals(department.getName()), "department has wrong name: " + department.getName()); } @Test @ToBeInvestigated public void testMergeDetachedWithRelation() { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { Department dep = new Department(101, "Aztec gods"); Employee empDetached = new Employee(102, "Huitzil", "Opochtli", dep); Review revManaged = new Review(103, Date.valueOf("2006-01-01"), "managed"); Review revDetached = new Review(104, Date.valueOf("2006-01-02"), "detached/in PC"); Review revDetachedNotInPC = new Review(105, Date.valueOf("2006-01-03"), "detached/not in PC"); Review revNew = new Review(106, Date.valueOf("2006-01-04"), "new"); Review revAddedInManagedEmp = new Review(107, Date.valueOf("2006-01-05"), "added in managed emp"); Hobby hobbyManaged = new Hobby("managed"); Hobby hobbyDetached = new Hobby("detached/in PC"); Hobby hobbyDetachedNotInPC = new Hobby("detached/not in PC"); Hobby hobbyNew = new Hobby("new"); Hobby hobbyAddedInManagedEmp = new Hobby("added in managed emp"); env.beginTransaction(em); em.persist(dep); em.persist(empDetached); em.persist(revManaged); em.persist(revDetached); em.persist(revDetachedNotInPC); em.persist(revAddedInManagedEmp); em.persist(hobbyManaged); em.persist(hobbyDetached); em.persist(hobbyDetachedNotInPC); em.persist(hobbyAddedInManagedEmp); env.commitTransactionAndClear(em); env.beginTransaction(em); // relations of the managed employee Employee empManaged = em.find(Employee.class, new Integer(empDetached.getId())); empManaged.setFirstName("Adrian"); revManaged = em.find(Review.class, new Integer(revManaged.getId())); Review revSamePKAsDetached = em.find(Review.class, new Integer(revDetached.getId())); revSamePKAsDetached.setReviewText("same PK as detached review"); revAddedInManagedEmp = em.find(Review.class, new Integer(revAddedInManagedEmp.getId())); em.persist(revAddedInManagedEmp); empManaged.addReview(revManaged); empManaged.addReview(revSamePKAsDetached); empManaged.addReview(revAddedInManagedEmp); hobbyManaged = em.find(Hobby.class, hobbyManaged.getId()); Hobby hobbySamePKAsDetached = em.find(Hobby.class, hobbyDetached.getId()); hobbySamePKAsDetached.setDescription("same PK as detached hobby"); hobbyAddedInManagedEmp = em.find(Hobby.class, hobbyAddedInManagedEmp.getId()); em.persist(hobbyAddedInManagedEmp); empManaged.addHobby(hobbyManaged); empManaged.addHobby(hobbySamePKAsDetached); empManaged.addHobby(hobbyAddedInManagedEmp); // relations of the detached employee empDetached.addReview(revManaged); empDetached.addReview(revDetached); empDetached.addReview(revDetachedNotInPC); empDetached.addReview(revNew); empDetached.addHobby(hobbyManaged); empDetached.addHobby(hobbyDetached); empDetached.addHobby(hobbyDetachedNotInPC); empDetached.addHobby(hobbyNew); Employee empAfterMerge = em.merge(empDetached); Set<Review> reviews = empAfterMerge.getReviews(); List<Hobby> hobbies = empAfterMerge.getHobbies(); verify(empAfterMerge == empManaged, "entity manager changed identity of managed object"); verify(reviews.size() == 4, "Merged employee has " + reviews.size() + " reviews, expected 4"); verify(hobbies.size() == 4, "Merged employee has " + hobbies.size() + " hobbies, expected 4"); verify(empAfterMerge.getFirstName().equals(empDetached.getFirstName()), "First name not merged correctly"); verify(containsIdentical(reviews, revManaged), "Merged employee does not contain revManaged"); verify(containsIdentical(hobbies, hobbyManaged), "Merged employee does not contain hobbyManaged"); verify(containsIdentical(reviews, revSamePKAsDetached), "Merged employee does not contain revSamePKAsDetached"); verify(containsIdentical(hobbies, hobbySamePKAsDetached), "Merged employee does not contain hobbySamePKAsDetached"); verify("same PK as detached review".equals(revSamePKAsDetached.getReviewText()), "Text of revSamePKAsDetached changed"); verify("same PK as detached hobby".equals(hobbySamePKAsDetached.getDescription()), "Text of hobbySamePKAsDetached changed"); verify(containsIdentical(reviews, revNew), "Merged employee does not contain revNew"); verify(containsIdentical(hobbies, hobbyNew), "Merged employee does not contain hobbyNew"); verify(!em.contains(revNew), "revNew is managed"); verify(!em.contains(hobbyNew), "hobbyNew is managed"); verify(!containsIdentical(reviews, revAddedInManagedEmp), "Merged employee contains revAddedInManagedEmp"); verify(!containsIdentical(hobbies, hobbyAddedInManagedEmp), "Merged employee contains hobbyAddedInManagedEmp"); verify(em.contains(revAddedInManagedEmp), "revAddedInManagedEmp is not managed"); verify(em.contains(hobbyAddedInManagedEmp), "hobbyAddedInManagedEmp is not managed"); int pk = revDetachedNotInPC.getId(); Review revSamePK = null; for (Review rev : reviews) { if (rev.getId() == pk) { revSamePK = rev; break; } } verify(revSamePK != null, "Merged employee does not contain review with id " + pk); verify(em.contains(revSamePK), "Review " + pk + " is not managed"); String hobbyId = hobbyDetachedNotInPC.getId(); Hobby hobbySamePK = null; for (Hobby hobby : hobbies) { if (hobby.getId().equals(hobbyId)) { hobbySamePK = hobby; break; } } verify(hobbySamePK != null, "Merged employee does not contain hobby with id " + pk); verify(em.contains(hobbySamePK), "Hobby " + pk + " is not managed"); // commit should fail because empAfterMerge has a relation to a new review/hobby boolean commitFailed = false; try { env.commitTransactionAndClear(em); } catch (RuntimeException e) { if (!checkForIllegalStateException(e)) { throw e; } commitFailed = true; } verify(commitFailed, "Commit succeeded although employee had a relation to a new review"); } finally { closeEntityManager(em); } } @Test public void testMergeManaged() { /* * 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. */ final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { // case 1: object is managed and in state NEW int id = 11; Department dep = new Department(id, "MANAGED_NEW"); env.beginTransaction(em); em.persist(dep); // object is now in state NEW verify(em.contains(dep), "entity manager does not contain object"); Department mergeResult = em.merge(dep); // this should be ignored checkDepartment(mergeResult, id, "MANAGED_NEW"); verify(mergeResult == dep, "merge operation has changed identity of a managed object"); env.commitTransactionAndClear(em); // case 2: object is managed and in state MANAGED id = 12; dep = new Department(id, "MANAGED"); env.beginTransaction(em); em.persist(dep); env.commitTransactionAndClear(em); env.beginTransaction(em); dep = em.find(Department.class, new Integer(id)); // object is now in state MANAGED checkDepartment(dep, id, "MANAGED"); verify(em.contains(dep), "entity manager does not contain object"); mergeResult = em.merge(dep); // this should be ignored checkDepartment(mergeResult, id, "MANAGED"); verify(mergeResult == dep, "merge operation has changed identity of a managed object"); env.commitTransactionAndClear(em); } finally { closeEntityManager(em); } } @Test @ToBeInvestigated public void testMergeRemoved() { /* * If X is a removed entity instance, an IllegalArgumentException will be thrown by the merge operation (or the * transaction commit will fail). */ final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { // 1. find an existing department, remove it and call merge int id1 = 21; Department dep = new Department(id1, "REMOVE"); env.beginTransaction(em); em.persist(dep); env.commitTransactionAndClear(em); env.beginTransaction(em); dep = em.find(Department.class, new Integer(id1)); em.remove(dep); // now the entity should be REMOVED boolean failed = false; try { em.merge(dep); } catch (IllegalArgumentException ex) { failed = true; env.rollbackTransactionAndClear(em); } try { if (env.isTransactionActive(em)) { env.commitTransactionAndClear(em); } } catch (PersistenceException ex) { failed = true; } verify(failed, "merge did succeed on a removed instance."); // 2. remove managed-new object // what happens is undefined by the spec. Consequently, we don't check anything. // 3. find an existing department, remove it and call merge on a different object with same primary key // (so the object we pass in is actually detached, but the object under control of entity manager // is in state REMOVED) int id = 23; Department depEM = new Department(id, "REMOVE"); Department depClient = new Department(id, "NEW_NAME"); env.beginTransaction(em); em.persist(depEM); env.commitTransactionAndClear(em); env.beginTransaction(em); depEM = em.find(Department.class, new Integer(id)); checkDepartment(depEM, id, "REMOVE"); em.remove(depEM); // this is now in state REMOVED failed = false; try { em.merge(depClient); } catch (IllegalArgumentException ex) { failed = true; env.rollbackTransactionAndClear(em); } try { if (env.isTransactionActive(em)) { env.commitTransactionAndClear(em); } } catch (PersistenceException ex) { failed = true; } verify(failed, "merge did succeed on a removed instance."); // 4. find an existing department, remove it, flush, and call merge id1 = 24; dep = new Department(id1, "REMOVE"); env.beginTransaction(em); em.persist(dep); env.commitTransactionAndClear(em); env.beginTransaction(em); dep = em.find(Department.class, new Integer(id1)); em.remove(dep); em.flush(); // now the entity should be in state DELETE_EXECUTED failed = false; try { em.merge(dep); } catch (IllegalArgumentException ex) { failed = true; env.rollbackTransactionAndClear(em); } try { if (env.isTransactionActive(em)) { env.commitTransactionAndClear(em); } } catch (PersistenceException ex) { failed = true; } verify(failed, "merge did succeed on a removed instance."); } finally { closeEntityManager(em); } } @Test public void testMergeDetached() { /* * If X is a detached entity, it is copied onto a pre-existing managed entity instance X' of the same identity or a new * managed copy of X is created. */ final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); int id; try { // case 1: entity exists on DB, but not contained in persistence context id = 31; Department dep = new Department(id, "DETACHED"); // firstly, we create a department on the database env.beginTransaction(em); em.persist(dep); env.commitTransactionAndClear(em); verify(!em.contains(dep), "entity manager contains department -> it cannot be detached"); dep.setName("NEW_NAME"); env.beginTransaction(em); Department dep2 = em.merge(dep); checkDepartment(dep2, id, "NEW_NAME"); verify(em.contains(dep2), "entity manager does not contain department -> it cannot be merged"); env.commitTransactionAndClear(em); verifyExistence(em, id, "NEW_NAME"); // case 2: entity is contained in persistence context, but object to be merged has different object identity Department depEM; Department depClient; Department mergeResult; // case 2a: state of known object: FOR_INSERT id = 32; depEM = new Department(id, "ORIGINAL"); depClient = new Department(id, "NEW_NAME"); env.beginTransaction(em); em.persist(depEM); // this is now in state new checkDepartment(depEM, id, "ORIGINAL"); verify(em.contains(depEM), "entity manager does not contain department -> it cannot be merged"); mergeResult = em.merge(depClient); checkDepartment(mergeResult, id, "NEW_NAME"); verify(em.contains(mergeResult), "entity manager does not contain merged department"); verify(mergeResult == depEM, "entity manager changed identity of managed object"); env.commitTransactionAndClear(em); verifyExistence(em, id, "NEW_NAME"); // case 2b: state of known object: FOR_UPDATE id = 33; depEM = new Department(id, "ORIGINAL"); env.beginTransaction(em); em.persist(depEM); env.commitTransactionAndClear(em); env.beginTransaction(em); depClient = em.find(Department.class, new Integer(id)); env.commitTransactionAndClear(em); env.beginTransaction(em); depClient.setName(("NEW_NAME")); depEM = em.find(Department.class, new Integer(id)); // this is now in state managed checkDepartment(depEM, id, "ORIGINAL"); verify(em.contains(depEM), "entity manager does not contain department -> it cannot be merged"); mergeResult = em.merge(depClient); checkDepartment(mergeResult, id, "NEW_NAME"); verify(em.contains(mergeResult), "entity manager does not contain merged department"); verify(mergeResult == depEM, "entity manager changed identity of managed object"); env.commitTransactionAndClear(em); verifyExistence(em, id, "NEW_NAME"); } finally { closeEntityManager(em); } } private void verifyExistence(final EntityManager em, int id, String name) { Department dep; dep = em.find(Department.class, new Integer(id)); verify(dep != null, "department not found"); verify(name.equals(dep.getName()), "department has wrong name: " + dep.getName()); } /* * throws IllegalArgumentException, if instance is not an entity */ @Test public void testNotAnEntity() { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { env.beginTransaction(em); try { em.merge("Hutzliputz"); flop("no IllegalArgumentException "); } catch (IllegalArgumentException e) { verify(true, ""); } finally { env.rollbackTransactionAndClear(em); } env.beginTransaction(em); try { em.merge(null); flop("no IllegalArgumentException "); } catch (IllegalArgumentException e) { verify(true, ""); } finally { env.rollbackTransactionAndClear(em); } } finally { closeEntityManager(em); } } @Test @ToBeInvestigated public void testMergeNewWithRelation() { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { Department dep = new Department(201, "Aztec gods"); Employee empNew = new Employee(202, "Huitzil", "Opochtli", dep); Review revManaged = new Review(203, Date.valueOf("2006-01-01"), "managed"); Review revDetached = new Review(204, Date.valueOf("2006-01-02"), "detached/in PC"); Review revDetachedNotInPC = new Review(205, Date.valueOf("2006-01-03"), "detached/not in PC"); Review revNew = new Review(206, Date.valueOf("2006-01-04"), "new"); Hobby hobbyManaged = new Hobby("managed"); Hobby hobbyDetached = new Hobby("detached/in PC"); Hobby hobbyDetachedNotInPC = new Hobby("detached/not in PC"); Hobby hobbyNew = new Hobby("new"); env.beginTransaction(em); em.persist(dep); em.persist(revManaged); em.persist(revDetached); em.persist(revDetachedNotInPC); em.persist(hobbyManaged); em.persist(hobbyDetached); em.persist(hobbyDetachedNotInPC); env.commitTransactionAndClear(em); env.beginTransaction(em); revManaged = em.find(Review.class, new Integer(revManaged.getId())); Review revSamePKAsDetached = em.find(Review.class, new Integer(revDetached.getId())); revSamePKAsDetached.setReviewText("same PK as detached review"); empNew.addReview(revManaged); empNew.addReview(revDetached); empNew.addReview(revDetachedNotInPC); empNew.addReview(revNew); hobbyManaged = em.find(Hobby.class, hobbyManaged.getId()); Hobby hobbySamePKAsDetached = em.find(Hobby.class, hobbyDetached.getId()); hobbySamePKAsDetached.setDescription("same PK as detached hobby"); empNew.addHobby(hobbyManaged); empNew.addHobby(hobbyDetached); empNew.addHobby(hobbyDetachedNotInPC); empNew.addHobby(hobbyNew); Employee empAfterMerge = em.merge(empNew); Set<Review> reviews = empAfterMerge.getReviews(); List<Hobby> hobbies = empAfterMerge.getHobbies(); verify(em.contains(empAfterMerge), "Merged employee not managed"); verify(empAfterMerge != empNew, "Merge did not copy new entity"); verify(reviews.size() == 4, "Merged employee has " + reviews.size() + " reviews, expected 4"); verify(hobbies.size() == 4, "Merged employee has " + hobbies.size() + " hobbies, expected 4"); verify(containsIdentical(reviews, revManaged), "Merged employee does not contain revManaged"); verify(containsIdentical(hobbies, hobbyManaged), "Merged employee does not contain hobbyManaged"); verify(containsIdentical(reviews, revSamePKAsDetached), "Merged employee does not contain revSamePKAsDetached"); verify(containsIdentical(hobbies, hobbySamePKAsDetached), "Merged employee does not contain hobbySamePKAsDetached"); verify("same PK as detached review".equals(revSamePKAsDetached.getReviewText()), "Text of revSamePKAsDetached changed"); verify("same PK as detached hobby".equals(hobbySamePKAsDetached.getDescription()), "Text of hobbySamePKAsDetached changed"); verify(containsIdentical(reviews, revNew), "Merged employee does not contain revNew"); verify(containsIdentical(hobbies, hobbyNew), "Merged employee does not contain hobbyNew"); verify(!em.contains(revNew), "revNew is managed"); verify(!em.contains(hobbyNew), "hobbyNew is managed"); int pk = revDetachedNotInPC.getId(); Review revSamePK = null; for (Review rev : reviews) { if (rev.getId() == pk) { revSamePK = rev; break; } } verify(revSamePK != null, "Merged employee does not contain review with id " + pk); verify(em.contains(revSamePK), "Review " + pk + " is not managed"); String hobbyId = hobbyDetachedNotInPC.getId(); Hobby hobbySamePK = null; for (Hobby hobby : hobbies) { if (hobby.getId().equals(hobbyId)) { hobbySamePK = hobby; break; } } verify(hobbySamePK != null, "Merged employee does not contain hobby with id " + pk); verify(em.contains(hobbySamePK), "Hobby " + pk + " is not managed"); // commit should fail because empAfterMerge has a relation to a new review/hobby boolean commitFailed = false; try { env.commitTransactionAndClear(em); } catch (RuntimeException e) { if (!checkForIllegalStateException(e)) { throw e; } commitFailed = true; } verify(commitFailed, "Commit succeeded although employee had a relation to a new review"); } finally { closeEntityManager(em); } } @Test public void testWithLazyRelationPending() { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { // Case 1: Change observer pending in both detached and managed object Department dep = new Department(301, "Merge test department"); Employee emp = new Employee(302, "Detached", "Employee", dep); Review rev1 = new Review(303, Date.valueOf("2006-01-01"), "one"); Review rev2 = new Review(304, Date.valueOf("2006-01-02"), "two"); Hobby hobby1 = new Hobby("counting beans"); Hobby hobby2 = new Hobby("writing SDDs"); emp.addReview(rev1); emp.addReview(rev2); emp.addHobby(hobby1); emp.addHobby(hobby2); env.beginTransaction(em); em.persist(dep); em.persist(emp); em.persist(rev1); em.persist(rev2); em.persist(hobby1); em.persist(hobby2); env.commitTransactionAndClear(em); env.beginTransaction(em); Employee empDetached = em.find(Employee.class, new Integer(emp.getId())); env.commitTransactionAndClear(em); env.beginTransaction(em); emp = em.find(Employee.class, new Integer(emp.getId())); emp.setFirstName("Managed"); emp = em.merge(empDetached); verify(empDetached.getFirstName().equals(emp.getFirstName()), "Merged employee has wrong first name"); verify(emp.getReviews().size() == 2, "Merged employee has " + emp.getReviews().size() + " reviews, expected 2"); verify(emp.getHobbies().size() == 2, "Merged employee has " + emp.getHobbies().size() + " hobbies, expected 2"); env.commitTransactionAndClear(em); // Case 2: Change observer pending in detached object and loaded but unchanged in managed object env.beginTransaction(em); empDetached = em.find(Employee.class, new Integer(emp.getId())); emp.setFirstName("Detached"); env.commitTransactionAndClear(em); env.beginTransaction(em); emp = em.find(Employee.class, new Integer(emp.getId())); emp.setFirstName("Managed"); Set<Review> reviews = emp.getReviews(); verify(reviews.size() == 2, "Employee has " + reviews.size() + " reviews, expected 2"); List<Hobby> hobbies = emp.getHobbies(); verify(hobbies.size() == 2, "Employee has " + hobbies.size() + " hobbies, expected 2"); emp = em.merge(empDetached); env.commitTransactionAndClear(em); verify(empDetached.getFirstName().equals(emp.getFirstName()), "Merged employee has wrong first name"); reviews = emp.getReviews(); verify(reviews.size() == 2, "Merged employee has " + reviews.size() + " reviews, expected 2"); hobbies = emp.getHobbies(); verify(hobbies.size() == 2, "Merged employee has " + hobbies.size() + " hobbies, expected 2"); } finally { closeEntityManager(em); } } @SuppressWarnings("boxing") @Test @ToBeInvestigated public void testIlegalArgumentWithLazyRelationPending() { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { // case 1: relationship to be merged has wrong owner Employee e1 = new Employee(401, "Detached", "Employee", null); Employee e2 = new Employee(402, "Detached", "Employee", null); Review review = new Review(403, Date.valueOf("2006-03-15"), "abc"); e1.addReview(review); env.beginTransaction(em); em.persist(e1); em.persist(e2); em.persist(review); env.commitTransaction(em); em.clear(); Employee detached1 = em.find(Employee.class, 401); Employee detached2 = em.find(Employee.class, 402); detached2.setReviews(detached1.getReviews()); em.clear(); env.beginTransaction(em); try { em.merge(detached2); flop("missing illegalArgumentException"); } catch (IllegalArgumentException ex) { Assert.assertTrue(true); } finally { env.rollbackTransaction(em); em.clear(); } // case 2: target relationship is pending but has wrong owner e1 = new Employee(411, "Detached", "Employee", null); e2 = new Employee(412, "Detached", "Employee", null); review = new Review(413, Date.valueOf("2006-03-15"), "abc"); e1.addReview(review); env.beginTransaction(em); em.persist(e1); em.persist(e2); em.persist(review); env.commitTransaction(em); em.clear(); detached1 = em.find(Employee.class, 411); detached2 = em.find(Employee.class, 412); em.clear(); env.beginTransaction(em); try { e1 = em.find(Employee.class, 411); e2 = em.find(Employee.class, 412); e2.setReviews(e1.getReviews()); em.merge(detached2); flop("missing illegalArgumentException"); } catch (IllegalArgumentException ex) { Assert.assertTrue(true); } finally { env.rollbackTransaction(em); em.clear(); } // case 3: target relationship is no change observer e1 = new Employee(421, "Detached", "Employee", null); env.beginTransaction(em); em.persist(e1); env.commitTransaction(em); em.clear(); detached1 = em.find(Employee.class, 421); em.clear(); env.beginTransaction(em); try { e1 = em.find(Employee.class, 421); e1.setReviews(new HashSet<Review>()); em.merge(detached1); flop("missing illegalArgumentException"); } catch (IllegalArgumentException ex) { Assert.assertTrue(true); } finally { env.rollbackTransaction(em); em.clear(); } } finally { closeEntityManager(em); } } @SuppressWarnings("boxing") @Test @ToBeInvestigated public void testIlegalArgumentWithDeserializedEntity() throws IOException, ClassNotFoundException { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { // case 1: relationship to be merged has wrong owner Employee e1 = new Employee(501, "Detached", "Employee", null); Employee e2 = new Employee(502, "Detached", "Employee", null); Review review = new Review(503, Date.valueOf("2006-03-15"), "abc"); e1.addReview(review); env.beginTransaction(em); em.persist(e1); em.persist(e2); em.persist(review); env.commitTransaction(em); em.clear(); Employee detached1 = AbstractBaseTest.serializeDeserialize(em.find(Employee.class, 501)); Employee detached2 = AbstractBaseTest.serializeDeserialize(em.find(Employee.class, 502)); detached2.setReviews(detached1.getReviews()); em.clear(); env.beginTransaction(em); try { em.merge(detached2); flop("missing illegalArgumentException"); } catch (IllegalArgumentException ex) { Assert.assertTrue(true); } finally { env.rollbackTransaction(em); em.clear(); } // case 2: target relationship is pending but has wrong owner e1 = new Employee(511, "Detached", "Employee", null); e2 = new Employee(512, "Detached", "Employee", null); review = new Review(513, Date.valueOf("2006-03-15"), "abc"); e1.addReview(review); env.beginTransaction(em); em.persist(e1); em.persist(e2); em.persist(review); env.commitTransaction(em); em.clear(); detached1 = AbstractBaseTest.serializeDeserialize(em.find(Employee.class, 511)); detached2 = AbstractBaseTest.serializeDeserialize(em.find(Employee.class, 512)); em.clear(); env.beginTransaction(em); try { e1 = em.find(Employee.class, 511); e2 = em.find(Employee.class, 512); e2.setReviews(e1.getReviews()); em.merge(detached2); flop("missing illegalArgumentException"); } catch (IllegalArgumentException ex) { Assert.assertTrue(true); } finally { env.rollbackTransaction(em); em.clear(); } // case 3: target relationship is no change observer e1 = new Employee(521, "Detached", "Employee", null); env.beginTransaction(em); em.persist(e1); env.commitTransaction(em); em.clear(); detached1 = AbstractBaseTest.serializeDeserialize(em.find(Employee.class, 521)); em.clear(); env.beginTransaction(em); try { e1 = em.find(Employee.class, 521); e1.setReviews(new HashSet<Review>()); em.merge(detached1); flop("missing illegalArgumentException"); } catch (IllegalArgumentException ex) { Assert.assertTrue(true); } finally { env.rollbackTransaction(em); em.clear(); } } finally { closeEntityManager(em); } } /** * Checks whether the collection contains an entry identical to the given object. The method uses object identity for * comparison (not the object's <code>equals</code> method * * @param collection * the collection * @param o * the object to search for in the set * @return <code>true</code> if the set contains o (and o is not <code>null</code>), <code>false</code> otherwise */ private boolean containsIdentical(Collection<?> collection, Object o) { boolean contains = false; if (o != null) { for (Object entry : collection) { if (entry == o) { contains = true; break; } } } return contains; } @Ignore // @TestProperties(unsupportedEnvironments={JTANonSharedPCEnvironment.class, ResourceLocalEnvironment.class}) public void testNoTransaction() { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); Department dep = new Department(600, "dep600"); try { em.merge(dep); flop("exception not thrown as expected"); } catch (TransactionRequiredException e) { // $JL-EXC$ expected behavior } finally { closeEntityManager(em); } } @Test public void testTransactionMarkedForRollback() { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { // case 1: persistence context already available when tx marked for rollback Department dep = new Department(601, "dep601"); env.beginTransaction(em); verify(!em.contains(dep), "entity contained in persistence context"); // this ensures that the pc is // available env.markTransactionForRollback(em); dep = em.merge(dep); verify(em.contains(dep), "entity not contained in persistence context"); env.rollbackTransaction(em); // case 2: persistence context not yet available when tx marked for rollback // will throw an exception in the container-managed JTA case // if (!(env instanceof JTASharedPCEnvironment)) { { dep = new Department(602, "dep602"); env.beginTransaction(em); env.markTransactionForRollback(em); dep = em.merge(dep); verify(em.contains(dep), "entity not contained in persistence context"); env.rollbackTransaction(em); } } finally { closeEntityManager(em); } } private void verifyMergeNewEntityWithIdSetInPrePersist(Timestamp t1) { /* * 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'. */ final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { env.beginTransaction(em); Timestamp t2 = em.merge(t1); verify(t2 != null, "merged timestamp is null"); verify(t2.getId() != null && t2.getId() != Long.valueOf(0), "unexpected id: " + t2.getId()); verify(em.contains(t2), "not contained"); env.commitTransactionAndClear(em); verify(!em.contains(t1), "entity manager contains timestamp -> it cannot be detached"); verify(!em.contains(t2), "entity manager contains timestamp -> it cannot be detached"); Timestamp t3 = em.find(Timestamp.class, t2.getId()); verify(t3 != null, "not found"); verify(t3.getId().equals(t2.getId()), "unexpected id: " + t3.getId()); } finally { closeEntityManager(em); } } @Test public void testMergeNewEntityWithIdSetInPrePersist() { // initial object verifyMergeNewEntityWithIdSetInPrePersist(new Timestamp()); try { Thread.sleep(10); // make sure next entity gets new ID } catch (InterruptedException e) { // $JL-EXC$ } Timestamp t = new Timestamp(); t.setId(Long.valueOf(1)); // verifyMergeNewEntityWithIdSetInPrePersist(t); } @Test public void testNastyTimestampTwice() { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { Nasty nasty = new Nasty(); verify(nasty.getId() == null, "id is not null"); env.beginTransaction(em); em.persist(nasty); Long value = nasty.getId(); verify(value != null, "id is null"); try { em.merge(new Nasty()); env.commitTransaction(em); flop("persisting second nasty timestamp succeeded"); } catch (PersistenceException ex) { Assert.assertTrue(true); } if (env.isTransactionActive(em)) { env.rollbackTransactionAndClear(em); } } finally { closeEntityManager(em); } } @Test public void testNastyTimestampTwiceNotInitial() { final JPAEnvironment env = getEnvironment(); final EntityManager em = env.getEntityManager(); try { Nasty nasty = new Nasty(); verify(nasty.getId() == null, "id is not null"); env.beginTransaction(em); em.persist(nasty); Long value = nasty.getId(); verify(value != null, "id is null"); try { Nasty n2 = new Nasty(); n2.setId(Long.valueOf(2000)); em.merge(n2); env.commitTransaction(em); flop("persisting second nasty timestamp succeeded"); } catch (PersistenceException ex) { Assert.assertTrue(true); } if (env.isTransactionActive(em)) { env.rollbackTransactionAndClear(em); } } finally { closeEntityManager(em); } } }