blob: 6962fc97953eb30cf73a768fa2f32ccf612fc0fd [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.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.TransactionRequiredException;
import org.eclipse.persistence.testing.framework.wdf.Bugzilla;
import org.eclipse.persistence.testing.framework.wdf.JPAEnvironment;
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Cubicle;
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.CubiclePrimaryKeyClass;
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Department;
import org.eclipse.persistence.testing.tests.wdf.jpa1.JPA1Base;
import org.junit.Ignore;
import org.junit.Test;
public class TestRemove extends JPA1Base {
private void verifyAbsenceOnDatabase(int departmentId) throws SQLException {
Connection conn = getEnvironment().getDataSource().getConnection();
try {
PreparedStatement stmt = conn.prepareStatement("select ID, NAME from TMP_DEP where ID = ?");
try {
stmt.setInt(1, departmentId);
ResultSet rs = stmt.executeQuery();
try {
verify(!rs.next(), "department with id " + departmentId + " found using JDBC.");
} finally {
rs.close();
}
} finally {
stmt.close();
}
} finally {
conn.close();
}
}
@Test
public void testRemoveNew() throws SQLException {
/*
* If X is a new entity, it is ignored by the remove operation. However, the remove operation is cascaded to entities
* referenced by X, if the relationships from X to these other entities is annotated with the cascade=REMOVE or
* cascade=ALL annotation element value.
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
env.evictAll(em);
try {
Department dep = new Department(1, "HUGO");
env.beginTransaction(em);
em.remove(dep);
// TODO verify remove is ignored on this entity using lifecycle callbacks
// TODO verify
env.commitTransactionAndClear(em);
verifyAbsenceOnDatabase(1);
} finally {
closeEntityManager(em);
}
}
private void verifyRemoveManaged(boolean flushBeforeRemove) throws SQLException {
/*
* If X is a managed entity, the remove operation causes it to become removed. The remove operation is cascaded to
* entities referenced by X, if the relationships from X to these other entities is annotated with the cascade=REMOVE or
* cascade=ALL annotation element value.
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
// 1. managed and in state MANAGED
final int id1 = (flushBeforeRemove ? 100 : 0) + 11;
Department dep = new Department(id1, "OLD");
env.beginTransaction(em);
em.persist(dep);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
// find a department in the state MANAGED
dep = em.find(Department.class, id1);
verify(dep != null, "department not found");
dep.setName("NEW");
if (flushBeforeRemove) {
em.flush();
}
em.remove(dep);
env.commitTransactionAndClear(em);
verifyAbsenceOnDatabase(id1);
// 2. managed and in state NEW
final int id2 = (flushBeforeRemove ? 100 : 0) + 12;
dep = new Department(id2, "OLD");
env.beginTransaction(em);
em.persist(dep);
dep.setName("NEW");
if (flushBeforeRemove) {
em.flush();
}
em.remove(dep);
env.commitTransactionAndClear(em);
verifyAbsenceOnDatabase(id2);
} finally {
closeEntityManager(em);
}
}
@Test
public void testRemoveManaged() throws SQLException {
verifyRemoveManaged(false);
}
@Test
public void testRemoveManagedFlushed() throws SQLException {
verifyRemoveManaged(true);
}
private void verifyRemoveRemoved(boolean flushBeforePersist) throws SQLException {
/*
* If X is a removed entity, it is ignored by the remove operation.
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
env.evictAll(em);
try {
// 1. find an existing department, remove it and remove it again
final int id1 = (flushBeforePersist ? 100 : 0) + 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);
// no, the entity should be REMOVED
if (flushBeforePersist) {
em.flush();
}
em.remove(dep);
env.commitTransactionAndClear(em);
// now, the entity should be managed again
verifyAbsenceOnDatabase(id1);
// 2. persist a new depatrment, remove it and call remove
env.beginTransaction(em);
final int id2 = (flushBeforePersist ? 100 : 0) + 22;
dep = new Department(id2, "REMOVE");
em.persist(dep);
em.remove(dep);
if (flushBeforePersist) {
em.flush();
}
em.remove(dep);
env.commitTransactionAndClear(em);
verifyAbsenceOnDatabase(id2);
} finally {
closeEntityManager(em);
}
}
@Test
public void testRemoveRemoved() throws SQLException {
verifyRemoveRemoved(false);
}
@Test
public void testRemoveRemovedFlushed() throws SQLException {
verifyRemoveRemoved(true);
}
@Test
public void testRemoveDetached() {
/*
* If X is a detached entity, an IllegalArgumentException will be thrown by the remove operation (or the transaction
* commit will fail).
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
int id;
Department dep;
Department detachedDep;
try {
// case 1: detached because entity exists on db but is not known by persistence context
id = 31;
dep = new Department(id, "DETACHED");
// firstly, we create a department on the database
env.beginTransaction(em);
em.persist(dep);
env.commitTransactionAndClear(em);
// now the department is detached, we try to remove it
env.beginTransaction(em);
boolean failed = false;
try {
em.remove(dep);
} catch (IllegalArgumentException e) {
failed = true;
env.rollbackTransactionAndClear(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (PersistenceException e) {
failed = true;
}
verify(failed, "remove succeeded on a detached instance.");
// case 2: detached because an object with same pk but different object identity is known by persistence context
// case 2a: state of known object: FOR_INSERT
id = 32;
dep = new Department(id, "MANAGED_NEW");
detachedDep = new Department(id, "DETACHED");
failed = false;
env.beginTransaction(em);
em.persist(dep); // object is now in state MANAGED_NEW
try {
em.remove(detachedDep);
} catch (IllegalArgumentException e) {
failed = true;
env.rollbackTransaction(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (PersistenceException e) {
failed = true;
}
verify(failed, "remove succeeded on a detached instance.");
// case 2b: state of known object: FOR_UPDATE
id = 33;
dep = new Department(id, "MANAGED");
detachedDep = new Department(id, "DETACHED");
failed = false;
env.beginTransaction(em);
em.persist(dep);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
dep = em.find(Department.class, id); // object is now in state MANAGED
try {
em.remove(detachedDep);
} catch (IllegalArgumentException e) {
failed = true;
env.rollbackTransactionAndClear(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (PersistenceException e) {
failed = true;
}
verify(failed, "remove succeeded on a detached instance.");
// case 2c: state of known object: FOR_DELETE
id = 34;
dep = new Department(id, "DELETED");
detachedDep = new Department(id, "DETACHED");
failed = false;
env.beginTransaction(em);
em.persist(dep);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
dep = em.find(Department.class, id);
em.remove(dep); // object is now in state DELETED
try {
em.remove(detachedDep);
} catch (IllegalArgumentException e) {
failed = true;
env.rollbackTransactionAndClear(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (PersistenceException e) {
failed = true;
}
verify(failed, "remove succeeded on a detached instance.");
} finally {
closeEntityManager(em);
}
}
@Test
@Bugzilla(bugid=309681) // INTERPRETATION: SAP JPA has special state DELETED_EXECUTED, which EclipseLink hasn't
public void testRemoveFlushRemoveAgain() {
/*
* If X is a detached entity, an IllegalArgumentException will be thrown by the remove operation (or the transaction
* commit will fail).
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
int id;
Department dep;
Department detachedDep;
try {
// case 2d: state of known object: DELETE_EXECUTED
id = 35;
dep = new Department(id, "DELETED");
detachedDep = new Department(id, "DETACHED");
boolean failed = false;
env.beginTransaction(em);
em.persist(dep);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
dep = em.find(Department.class, id);
em.remove(dep);
em.flush();
try {
em.remove(detachedDep);
} catch (IllegalArgumentException e) {
failed = true;
env.rollbackTransactionAndClear(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (PersistenceException e) {
failed = true;
}
verify(failed, "remove succeeded on a detached instance.");
} finally {
closeEntityManager(em);
}
}
@Test
public void testNotAnEntity() {
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
env.beginTransaction(em);
try {
em.remove("Hutzliputz");
flop("no IllegalArgumentException ");
} catch (IllegalArgumentException e) {
verify(true, "");
} finally {
env.rollbackTransactionAndClear(em);
}
env.beginTransaction(em);
try {
em.remove(null);
flop("no IllegalArgumentException ");
} catch (IllegalArgumentException e) {
verify(true, "");
} finally {
env.rollbackTransactionAndClear(em);
}
} finally {
closeEntityManager(em);
}
}
@Test
public void testRemoveWithCompositeKey() {
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
Cubicle cub = new Cubicle(30, 31, "green", null /* employee */);
CubiclePrimaryKeyClass cubKey = new CubiclePrimaryKeyClass(30, 31);
env.beginTransaction(em);
em.persist(cub);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
cub = em.find(Cubicle.class, cubKey);
em.remove(cub);
env.commitTransactionAndClear(em);
cub = em.find(Cubicle.class, cubKey);
verify(cub == null, "cubicle not removed");
} finally {
closeEntityManager(em);
}
}
@Ignore // @TestProperties(unsupportedEnvironments={JTANonSharedPCEnvironment.class, ResourceLocalEnvironment.class})
public void testNoTransaction() {
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
Department dep = new Department(40, "dep40");
try {
em.remove(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();
Department dep = new Department(41, "dep41");
try {
env.beginTransaction(em);
em.persist(dep);
env.commitTransaction(em);
em.clear();
env.beginTransaction(em);
dep = em.find(Department.class, dep.getId());
env.markTransactionForRollback(em);
em.remove(dep);
verify(!em.contains(dep), "entity still contained in persistence context");
env.rollbackTransaction(em);
} finally {
closeEntityManager(em);
}
}
}