blob: c9d06dbe151285462554412b5900e558c6aabf9d [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.EntityExistsException;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.TransactionRequiredException;
import org.junit.Assert;
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.Department;
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.models.wdf.jpa1.types.BasicTypesFieldAccess;
import org.eclipse.persistence.testing.tests.wdf.jpa1.JPA1Base;
import org.junit.Ignore;
import org.junit.Test;
public class TestPersist extends JPA1Base {
private void verifyExistenceOnDatabase(int departmentId, String tableName) throws SQLException {
if (tableName == null || !tableName.startsWith("TMP_")) {
throw new IllegalArgumentException(
"None or illegal tableName was submitted to this method. Valid tableNames have to begin with 'TMP_'.");
}
Connection conn = getEnvironment().getDataSource().getConnection();
try {
StringBuilder builder = new StringBuilder();
builder.append("select ID from ").append(tableName).append(" where ID = ?");
PreparedStatement stmt = conn.prepareStatement(builder.toString());
try {
stmt.setInt(1, departmentId);
ResultSet rs = stmt.executeQuery();
try {
verify(rs.next(), "no department with id " + departmentId + " found using JDBC.");
} finally {
rs.close();
}
} finally {
stmt.close();
}
} finally {
conn.close();
}
}
@Test
public void testPersistNewDepartment() throws SQLException {
/*
* If X is a new entity, it becomes managed. The entity X will be entered into the database at or before transaction
* commit or as a result of the flush operation.
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
Department dep = new Department(1, "R&D");
env.beginTransaction(em);
em.persist(dep);
env.commitTransactionAndClear(em);
// verify existence after commit
verifyExistenceOnDatabase(1, "TMP_DEP");
Department dep2 = new Department(2, "QM");
env.beginTransaction(em);
em.persist(dep2);
em.flush();
// will not work on Oracle!
// verify existence after flush
env.commitTransactionAndClear(em);
verifyExistenceOnDatabase(2, "TMP_DEP");
// TODO verify cascading
} finally {
closeEntityManager(em);
}
}
@Test
public void testPersistNewBasicTypesFieldAccess() throws SQLException {
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
BasicTypesFieldAccess btfa = new BasicTypesFieldAccess(1);
btfa.fill();
env.beginTransaction(em);
em.persist(btfa);
env.commitTransactionAndClear(em);
// verify existence after commit
verifyExistenceOnDatabase(1, "TMP_BASIC_TYPES_FA");
BasicTypesFieldAccess btfa2 = new BasicTypesFieldAccess(2);
btfa2.fill();
env.beginTransaction(em);
em.persist(btfa2);
em.flush();
// will not work on Oracle!
// verify existence after flush
env.commitTransactionAndClear(em);
verifyExistenceOnDatabase(2, "TMP_BASIC_TYPES_FA");
// TODO verify cascading
} finally {
closeEntityManager(em);
}
}
private void verifyPersistManaged(boolean flushBeforePersist) {
/*
* If X is a pre-existing managed entity, it is ignored by the persist operation. However, the persist operation is
* cascaded to entities referenced by X, if the relationships from X to these other entities is annotated with the
* cascade=PERSIST or cascade=ALL annotation element value or specified with the equivalent XML descriptor element.
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
// 1. managed via find
final int id1 = (flushBeforePersist ? 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("CHANGED");
if (flushBeforePersist) {
em.flush();
}
// this persist should be ignored as object is already managed
em.persist(dep);
// TODO verify that persist is ignored (use callback methods)
// TODO verify cascading
env.commitTransactionAndClear(em);
// 2. managed via persist
final int id2 = (flushBeforePersist ? 100 : 0) + 12;
dep = new Department(id2, "OLD");
env.beginTransaction(em);
em.persist(dep);
dep.setName("CHANGED");
if (flushBeforePersist) {
em.flush();
}
// this persist should be ignored as object is already managed
em.persist(dep);
// TODO verify that persist is ignored (use callback methods)
// TODO verify cascading
env.commitTransactionAndClear(em);
} finally {
closeEntityManager(em);
}
}
private void verifyPersistRemoved(boolean flushBeforePersist) {
/*
* If X is a removed entity, it becomes managed.
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
// 1. find an existing department, remove it and call persist
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);
// now, the entity should be REMOVED
if (flushBeforePersist) {
em.flush();
}
em.persist(dep);
// now, the entity should be managed again
dep.setName("REINVIGORATED");
env.commitTransactionAndClear(em);
dep = em.find(Department.class, id1);
verify(dep != null, "department not found");
verify("REINVIGORATED".equals(dep.getName()), "department has wrong name: " + dep.getName());
// 2. persist a new department, remove it and call persist
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.persist(dep);
dep.setName("REINVIGORATED");
env.commitTransactionAndClear(em);
dep = em.find(Department.class, id2);
verify(dep != null, "department not found");
verify("REINVIGORATED".equals(dep.getName()), "department has wrong name: " + dep.getName());
} finally {
closeEntityManager(em);
}
}
@Test
public void testPersistManaged() {
verifyPersistManaged(false);
}
@Test
public void testPersistManagedFlushed() {
verifyPersistManaged(true);
}
@Test
public void testPersistRemoved() {
verifyPersistRemoved(false);
}
@Test
public void testPersistRemovedFlushed() {
verifyPersistRemoved(true);
}
@Test
public void testPersistDetached() throws SQLException {
/*
* <li>If X is a detached object, the EntityExistsException may be thrown when the persist operation is invoked,
* or the EntityExistsException or another PersistenceException may be thrown at flush or commit time.</li>
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
int id;
try {
// case 1: detached because entity exists on db but is not known by 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);
verifyExistenceOnDatabase(id, "TMP_DEP");
// now, the department is detached, we try to persist it again
env.beginTransaction(em);
boolean failed = false;
try {
em.persist(dep);
} catch (EntityExistsException e) {
failed = true;
env.rollbackTransactionAndClear(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (RuntimeException e) {
if (!checkForPersistenceException(e)) {
throw e;
}
failed = true;
}
verify(failed, "persist did succeed 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, "ORIGINAL");
Department detachedDep = new Department(id, "DETACHED");
failed = false;
env.beginTransaction(em);
em.persist(dep); // this is now in state new
try {
em.persist(detachedDep);
} catch (EntityExistsException e) {
failed = true;
env.rollbackTransactionAndClear(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (RuntimeException e) {
if (!checkForPersistenceException(e)) {
throw e;
}
failed = true;
}
verify(failed, "persist did succeed on a detached instance.");
// case 2b: state of known object: FOR_UPDATE
id = 33;
dep = new Department(id, "ORIGINAL");
detachedDep = new Department(id, "DETACHED");
failed = false;
env.beginTransaction(em);
em.persist(dep);
env.commitTransactionAndClear(em);
verifyExistenceOnDatabase(id, "TMP_DEP");
env.beginTransaction(em);
dep = em.find(Department.class, id); // this is now in state managed
try {
em.persist(detachedDep);
} catch (EntityExistsException e) {
failed = true;
env.rollbackTransactionAndClear(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (RuntimeException e) {
if (!checkForPersistenceException(e)) {
throw e;
}
failed = true;
}
verify(failed, "persist did succeed on a detached instance.");
// case 2c: state of known object: FOR_DELETE
id = 34;
dep = new Department(id, "ORIGINAL");
detachedDep = new Department(id, "DETACHED");
failed = false;
env.beginTransaction(em);
em.persist(dep);
env.commitTransactionAndClear(em);
verifyExistenceOnDatabase(id, "TMP_DEP");
env.beginTransaction(em);
dep = em.find(Department.class, id);
em.remove(dep); // this is now in state deleted
try {
em.persist(detachedDep);
} catch (EntityExistsException e) {
failed = true;
env.rollbackTransactionAndClear(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (RuntimeException e) {
if (!checkForPersistenceException(e)) {
throw e;
}
failed = true;
}
verify(failed, "persist did succeed on a detached instance.");
} finally {
closeEntityManager(em);
}
}
@Test
@Bugzilla(bugid=309681)
public void testPersistDeleteExecuted() throws SQLException {
/*
* If X is a detached object, an IllegalArgumentException will be thrown by the persist operation (or the transaction
* commit will fail).
*/
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
int id;
try {
// case 2d: state of known object: DELETE_EXECUTED
id = 35;
Department dep = new Department(id, "ORIGINAL");
Department detachedDep = new Department(id, "DETACHED");
boolean failed = false;
env.beginTransaction(em);
em.persist(dep);
env.commitTransactionAndClear(em);
verifyExistenceOnDatabase(id, "TMP_DEP");
env.beginTransaction(em);
dep = em.find(Department.class, id);
em.remove(dep);
em.flush();
try {
em.persist(detachedDep);
} catch (IllegalArgumentException e) {
failed = true;
env.rollbackTransactionAndClear(em);
}
try {
if (env.isTransactionActive(em)) {
env.commitTransactionAndClear(em);
}
} catch (RuntimeException e) {
if (!checkForPersistenceException(e)) {
throw e;
}
failed = true;
}
verify(failed, "persist did succeed on a detached instance.");
} finally {
closeEntityManager(em);
}
}
/*
* 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.persist("Hutzliputz");
flop("no IllegalArgumentException ");
} catch (IllegalArgumentException e) {
verify(true, "");
} finally {
env.rollbackTransactionAndClear(em);
}
env.beginTransaction(em);
try {
em.persist(null);
flop("no IllegalArgumentException ");
} catch (IllegalArgumentException e) {
verify(true, "");
} finally {
env.rollbackTransactionAndClear(em);
}
} 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(41, "dep41");
try {
em.persist(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(42, "dep42");
env.beginTransaction(em);
verify(!em.contains(dep), "entity contained in persistence context"); // this ensures that the pc is available
env.markTransactionForRollback(em);
em.persist(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 */Object)) {
dep = new Department(43, "dep43");
env.beginTransaction(em);
env.markTransactionForRollback(em);
em.persist(dep);
verify(em.contains(dep), "entity not contained in persistence context");
env.rollbackTransaction(em);
}
} finally {
closeEntityManager(em);
}
}
@Test
public void testPersistNewEntityWithIdModifiedInPrePersist() {
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
Timestamp timestamp = new Timestamp();
verify(timestamp.getId() == null, "id is not null");
env.beginTransaction(em);
em.persist(timestamp);
Long value = timestamp.getId();
verify(value != null, "id is null");
env.commitTransactionAndClear(em);
verify(em.find(Timestamp.class, value) != null, "not found");
} finally {
closeEntityManager(em);
}
}
private void verifyPersistRemovedEntityWithIdModifiedInPrePersist(boolean removeExecuted) {
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
Timestamp timestamp = new Timestamp();
verify(timestamp.getId() == null, "id is not null");
env.beginTransaction(em);
em.persist(timestamp);
env.commitTransaction(em);
env.beginTransaction(em);
final Long id1 = timestamp.getId();
timestamp = em.find(Timestamp.class, id1);
em.remove(timestamp);
if (removeExecuted) {
em.flush();
}
em.persist(timestamp);
env.commitTransactionAndClear(em);
final Long id2 = timestamp.getId();
Assert.assertFalse(id2.equals(id1));
Assert.assertNull(em.find(Timestamp.class, id1));
Assert.assertNotNull(em.find(Timestamp.class, id1));
} finally {
closeEntityManager(em);
}
}
@Test
@Bugzilla(bugid=309681)
public void testPersistRemovedEntityWithIdModifiedInPrePersist() {
verifyPersistRemovedEntityWithIdModifiedInPrePersist(false);
}
@Test
@Bugzilla(bugid=309681)
public void testPersistRemovedEntityWithIdModifiedInPrePersistFlushed() {
verifyPersistRemovedEntityWithIdModifiedInPrePersist(true);
}
@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.persist(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 testDuprec() {
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
env.beginTransaction(em);
Department department = new Department(999, "X");
em.persist(department);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
try {
em.persist(department); // exception here
env.commitTransaction(em); // or here
flop("duprec not detected");
} catch (PersistenceException ex) {
// OK
} catch (/* JTARollback */Exception ex) {
// JTA OK
}
} finally {
closeEntityManager(em);
}
}
@Test
public void testDuprecBatch() {
final JPAEnvironment env = getEnvironment();
final EntityManager em = env.getEntityManager();
try {
env.beginTransaction(em);
Department d1 = new Department(998, "X");
em.persist(d1);
Department d2 = new Department(997, "X");
em.persist(d2);
Department d3 = new Department(996, "X");
em.persist(d3);
Department d4 = new Department(995, "X");
em.persist(d4);
Department d5 = new Department(994, "X");
em.persist(d5);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
try {
d1 = new Department(993, "X");
em.persist(d1);
d2 = new Department(992, "X");
em.persist(d2);
d3 = new Department(991, "X");
em.persist(d3);
em.persist(d4); // duprec: 995
d5 = new Department(990, "X");
em.persist(d5);
env.commitTransaction(em); // or here
flop("duprec not detected");
} catch (PersistenceException ex) {
// OK
} catch (/* JTARollback */Exception ex) {
// JTA OK
}
} finally {
closeEntityManager(em);
}
}
}