blob: 1a806df55358bf4acb73b49e04bc48c3e3daad0c [file] [log] [blame]
/*
* Copyright (c) 2005, 2021 Oracle and/or its affiliates. All rights reserved.
* 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// 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 jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.TransactionRequiredException;
import org.junit.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, empDetached.getId());
empManaged.setFirstName("Adrian");
revManaged = em.find(Review.class, revManaged.getId());
Review revSamePKAsDetached = em.find(Review.class, revDetached.getId());
revSamePKAsDetached.setReviewText("same PK as detached review");
revAddedInManagedEmp = em.find(Review.class, 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, 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, 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, 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, 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, id);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
depClient.setName(("NEW_NAME"));
depEM = em.find(Department.class, 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, 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, revManaged.getId());
Review revSamePKAsDetached = em.find(Review.class, 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, emp.getId());
env.commitTransactionAndClear(em);
env.beginTransaction(em);
emp = em.find(Employee.class, 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, emp.getId());
emp.setFirstName("Detached");
env.commitTransactionAndClear(em);
env.beginTransaction(em);
emp = em.find(Employee.class, 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(1L);
//
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(2000L);
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);
}
}
}