| /* |
| * 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); |
| } |
| } |
| } |