blob: 81b601d329bc5d60c10a7ce7c1a1ec88807638b9 [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.lock;
import java.sql.Connection;
import java.sql.Date;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.naming.NamingException;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.OptimisticLockException;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.RollbackException;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.testing.framework.wdf.Bugzilla;
import org.eclipse.persistence.testing.framework.wdf.JPAEnvironment;
import org.eclipse.persistence.testing.framework.wdf.Skip;
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Department;
import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Review;
import org.eclipse.persistence.testing.models.wdf.jpa1.node.Node;
import org.eclipse.persistence.testing.tests.wdf.jpa1.JPA1Base;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
// TODO remove restrictions
public class TestOptimistic extends JPA1Base {
@Test
public void testOptimisticLockExceptionUpdateUpdate() {
JPAEnvironment env = getEnvironment();
EntityManager em1 = env.getEntityManagerFactory().createEntityManager();
int id = 10;
Review rev1 = new Review(id, Date.valueOf("2005-12-07"), "one");
try {
env.beginTransaction(em1);
em1.persist(rev1);
env.commitTransactionAndClear(em1);
env.beginTransaction(em1);
rev1 = em1.find(Review.class, id);
verify(rev1 != null, "Review is null");
rev1.getVersion();
EntityManager em2 = env.getEntityManagerFactory().createEntityManager();
try {
env.beginTransaction(em2);
Review rev2 = em2.find(Review.class, id);
rev2.setReviewText("two"); // 1 update
env.commitTransactionAndClear(em2);
} finally {
closeEntityManager(em2);
}
rev1.setReviewText("1"); // 2 update
env.commitTransactionAndClear(em1);
flop("OptimisticLockException not thrown");
} catch (RollbackException rbe) {
assertExceptionWrapsOLE(rev1, rbe);
} catch (OptimisticLockException ole) {
assertFailingEntity(rev1, ole);
} finally {
closeEntityManager(em1);
}
// bug 309681: version is incremented in spite of roll back -> not an
// issue
// verify(version == rev1.getVersion(), "wrong version:" + version +
// " != " + rev1.getVersion());
}
private static void assertFailingEntity(Object entity, OptimisticLockException ole) {
// $JL-EXC$ expected behavior
Object failingEntity = ole.getEntity();
if (failingEntity != null) {
verify(entity.equals(failingEntity), "wrong entity");
}
}
@Test
public void testOptimisticLockExceptionDeleteUpdate() {
JPAEnvironment env = getEnvironment();
EntityManager em1 = env.getEntityManagerFactory().createEntityManager();
EntityManager em2 = env.getEntityManagerFactory().createEntityManager();
int id = 20;
Review rev1 = new Review(id, Date.valueOf("2005-12-07"), "one");
try {
env.beginTransaction(em1);
em1.persist(rev1);
env.commitTransactionAndClear(em1);
env.beginTransaction(em1);
rev1 = em1.find(Review.class, id);
verify(rev1 != null, "Review is null");
env.beginTransaction(em2);
Review rev2 = em2.find(Review.class, id);
em2.remove(rev2); // 1 delete
env.commitTransactionAndClear(em2);
rev1.setReviewText("1"); // 2 update
env.commitTransactionAndClear(em1);
flop("OptimisticLockException not thrown");
} catch (RollbackException rbe) {
assertExceptionWrapsOLE(rev1, rbe);
} catch (OptimisticLockException ole) {
assertFailingEntity(rev1, ole);
} finally {
closeEntityManager(em1);
closeEntityManager(em2);
}
}
private static void assertExceptionWrapsOLE(Object entity, PersistenceException rbe) {
Throwable cause = rbe.getCause();
if (cause instanceof OptimisticLockException) {
assertFailingEntity(entity, (OptimisticLockException) cause);
} else {
Assert.fail("Rollback not caused by OLE");
}
}
@Test
public void testOptimisticLockExceptionUpdateDelete() {
JPAEnvironment env = getEnvironment();
EntityManager em1 = env.getEntityManagerFactory().createEntityManager();
EntityManager em2 = env.getEntityManagerFactory().createEntityManager();
int id = 30;
Review rev1 = new Review(id, Date.valueOf("2005-12-07"), "one");
try {
env.beginTransaction(em1);
em1.persist(rev1);
env.commitTransactionAndClear(em1);
env.beginTransaction(em1);
rev1 = em1.find(Review.class, id);
verify(rev1 != null, "Review is null");
env.beginTransaction(em2);
Review rev2 = em2.find(Review.class, id);
rev2.setReviewDate(Date.valueOf("2005-12-23")); // 1 update
env.commitTransactionAndClear(em2);
em1.remove(rev1); // 2 delete
env.commitTransactionAndClear(em1);
flop("OptimisticLockException not thrown");
} catch (RollbackException rbe) {
assertExceptionWrapsOLE(rev1, rbe);
} catch (OptimisticLockException ole) {
assertFailingEntity(rev1, ole);
try {
env.rollbackTransactionAndClear(em1);
flop("no rollback after OptimisticLockException");
} catch (IllegalStateException ise) {
// $JL-EXC$ expected behavior
}
} finally {
closeEntityManager(em1);
closeEntityManager(em2);
}
}
@Test
public void testOptimisticLockExceptionDeleteDelete() {
JPAEnvironment env = getEnvironment();
EntityManager em1 = env.getEntityManagerFactory().createEntityManager();
EntityManager em2 = env.getEntityManagerFactory().createEntityManager();
int id = 40;
Review rev1 = new Review(id, Date.valueOf("2005-12-07"), "one");
try {
env.beginTransaction(em1);
em1.persist(rev1);
env.commitTransactionAndClear(em1);
env.beginTransaction(em1);
rev1 = em1.find(Review.class, id);
verify(rev1 != null, "Review is null");
env.beginTransaction(em2);
Review rev2 = em2.find(Review.class, id);
em2.remove(rev2); // 1 delete
env.commitTransactionAndClear(em2);
em1.remove(rev1); // 2 delete
env.commitTransactionAndClear(em1);
flop("OptimisticLockException not thrown");
} catch (RollbackException rbe) {
assertExceptionWrapsOLE(rev1, rbe);
} catch (OptimisticLockException ole) {
assertFailingEntity(rev1, ole);
} finally {
closeEntityManager(em1);
closeEntityManager(em2);
}
}
@Test
public void testNoChange() {
JPAEnvironment env = getEnvironment();
EntityManager em = env.getEntityManagerFactory().createEntityManager();
try {
int id = 50;
int version = 0;
Review rev = new Review(id, Date.valueOf("2005-12-07"), "1");
env.beginTransaction(em);
em.persist(rev);
env.commitTransactionAndClear(em);
version = rev.getVersion();
env.beginTransaction(em);
rev = em.find(Review.class, id);
verify(rev != null, "Review is null");
rev.setReviewText(rev.getReviewText()); // no change
env.commitTransactionAndClear(em);
verify(version == rev.getVersion(), "wrong version");
} finally {
closeEntityManager(em);
}
}
@Test
@Bugzilla(bugid = 309681)
public void testIllegalVersionAccessNew() {
JPAEnvironment env = getEnvironment();
EntityManager em = env.getEntityManagerFactory().createEntityManager();
boolean caughtException = false;
try {
int id = 60;
Review rev = new Review(id, Date.valueOf("2005-12-07"), "1");
env.beginTransaction(em);
em.persist(rev);
rev.setVersion(5);
env.commitTransactionAndClear(em);
} catch (PersistenceException e) {
// $JL-EXC$ expected behavior
caughtException = true;
} finally {
closeEntityManager(em);
verify(caughtException, "PersistenceException not thrown for versionModification");
}
}
@Test
public void testIllegalVersionAccessManaged() {
JPAEnvironment env = getEnvironment();
EntityManager em = env.getEntityManagerFactory().createEntityManager();
boolean caughtException = false;
try {
int id = 70;
Review rev = new Review(id, Date.valueOf("2005-12-07"), "1");
env.beginTransaction(em);
em.persist(rev);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
rev = em.merge(rev);
rev.setVersion(7);
env.commitTransactionAndClear(em);
} catch (PersistenceException e) {
// $JL-EXC$ expected behavior
caughtException = true;
} finally {
closeEntityManager(em);
}
verify(caughtException, "PersistenceException not thrown for versionModification");
}
@Test
public void testFlushWithVersion() {
JPAEnvironment env = getEnvironment();
EntityManager em = env.getEntityManagerFactory().createEntityManager();
try {
int id = 80;
Review rev = new Review(id, Date.valueOf("2005-12-07"), "1");
env.beginTransaction(em);
em.persist(rev);
em.flush(); // 1st version
int startVersion = rev.getVersion();
rev.setReviewDate(Date.valueOf("2005-12-23"));
env.commitTransactionAndClear(em); // 2nd version
env.beginTransaction(em);
rev = em.merge(rev);
rev.setReviewText("AAA");
em.flush(); // 3rd version
rev.setReviewText("BBB");
env.commitTransactionAndClear(em); // 4th version
env.beginTransaction(em);
rev = em.merge(rev);
em.flush();
env.commitTransactionAndClear(em); // still 4th version
rev = em.find(Review.class, id);
verify(rev.getReviewText().equals("BBB"), "wrong reviewText");
verify(rev.getReviewDate().equals(Date.valueOf("2005-12-23")), "wrong reviewDate");
verify(rev.getVersion() == startVersion + 3, "wrong version");
} finally {
closeEntityManager(em);
}
}
@Test
public void testMergeWithVersion() {
JPAEnvironment env = getEnvironment();
EntityManager em = env.getEntityManagerFactory().createEntityManager();
int id = 90;
Review rev1 = new Review(id, Date.valueOf("2005-12-07"), "a");
Review rev2 = new Review(id, Date.valueOf("2005-12-07"), "b");
try {
env.beginTransaction(em);
em.persist(rev1);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
rev1 = em.merge(rev2);
flop("OptimisticLockException not thrown for merge with old version");
} catch (OptimisticLockException ole) {
assertFailingEntity(rev2, ole);
verify(env.isTransactionMarkedForRollback(em), "transaction not marked for rollback on OptimisticLockException");
} finally {
closeEntityManager(em);
}
}
@Test
public void testRefreshWithVersion() {
JPAEnvironment env = getEnvironment();
EntityManager em1 = env.getEntityManagerFactory().createEntityManager();
EntityManager em2 = env.getEntityManagerFactory().createEntityManager();
try {
int id = 100;
Review rev1 = new Review(id, Date.valueOf("2005-12-07"), "x");
env.beginTransaction(em1);
em1.persist(rev1);
env.commitTransactionAndClear(em1);
env.beginTransaction(em1);
rev1 = em1.find(Review.class, id);
verify(rev1 != null, "Review is null");
env.beginTransaction(em2);
Review rev2 = em2.find(Review.class, id);
rev2.setReviewDate(Date.valueOf("2005-12-23"));
env.commitTransactionAndClear(em2);
em1.refresh(rev1);
rev1.setReviewText("y");
env.commitTransactionAndClear(em1);
} finally {
closeEntityManager(em1);
closeEntityManager(em2);
}
}
@Test
public void testVersion() {
JPAEnvironment env = getEnvironment();
EntityManager em = env.getEntityManagerFactory().createEntityManager();
try {
int id = 110;
int version = 0;
Review rev = new Review(id, Date.valueOf("2005-12-07"), "1");
env.beginTransaction(em);
em.persist(rev);
env.commitTransactionAndClear(em);
for (int i = 0; i < 3; i++) {
env.beginTransaction(em);
rev = em.find(Review.class, id);
verify(rev != null, "Review is null");
rev.setReviewText(Integer.valueOf(i).toString());
env.commitTransactionAndClear(em);
verify(version < rev.getVersion(), "new version to small");
version = rev.getVersion();
}
} finally {
closeEntityManager(em);
}
}
@Test
public void testPersistAgain() {
JPAEnvironment env = getEnvironment();
EntityManager em = env.getEntityManagerFactory().createEntityManager();
try {
int id = 120;
Review rev1 = new Review(id, Date.valueOf("2005-12-07"), "x");
env.beginTransaction(em);
em.persist(rev1);
env.commitTransactionAndClear(em);
env.beginTransaction(em);
rev1 = em.find(Review.class, id);
verify(rev1 != null, "Review is null");
em.persist(rev1);
verify(rev1.getVersion() > 0, "Version reset");
env.commitTransactionAndClear(em);
} finally {
closeEntityManager(em);
}
}
@Ignore
// this only works on oracle
public void testIsoLevel() throws SQLException {
JPAEnvironment env = getEnvironment();
EntityManager em1 = env.getEntityManagerFactory().createEntityManager();
EntityManager em2 = env.getEntityManagerFactory().createEntityManager();
boolean caughtException = false;
try {
Connection con = env.getDataSource().getConnection();
verify(con.getTransactionIsolation() == Connection.TRANSACTION_READ_COMMITTED, "wrong isoLevel");
int id = 130;
Review rev1 = new Review(id, Date.valueOf("2005-12-07"), "1");
env.beginTransaction(em1); // create entity
em1.persist(rev1);
env.commitTransactionAndClear(em1);
env.beginTransaction(em1); // Tx1 read and flush change
rev1 = em1.find(Review.class, id);
verify(rev1 != null, "Review is null");
rev1.setReviewText("2");
em1.flush();
env.beginTransaction(em2); // Tx2 read flushed entity ?
Review rev2 = em2.find(Review.class, id);
verify(rev2 != null, "Review is null");
env.rollbackTransactionAndClear(em1); // Tx1 rollback first change
// and commit second
env.beginTransaction(em1);
rev1 = em1.find(Review.class, id);
verify(rev1 != null, "Review is null");
rev1.setReviewText("3");
env.commitTransactionAndClear(em1);
rev2.setReviewText("4"); // Tx2 commit based on rollback version
env.commitTransactionAndClear(em2);
} catch (OptimisticLockException ole) {
// $JL-EXC$ expected behavior
caughtException = true;
} finally {
closeEntityManager(em1);
closeEntityManager(em2);
verify(caughtException, "OptimisticLockException not thrown");
}
}
@Test
@Bugzilla(bugid = 309681)
public void testNode() {
JPAEnvironment env = getEnvironment();
EntityManager em = env.getEntityManagerFactory().createEntityManager();
int NUMBER_OF_NODES = 7;
int NUMBER_OF_UPDATES = 3;
try {
Node first = new Node(0, null);
first.setName("!0");
Node parent = first;
env.beginTransaction(em);
em.persist(first);
for (int i = 1; i < NUMBER_OF_NODES; i++) {
Node tmp = new Node(i, parent);
tmp.setName(Integer.valueOf(i).toString());
em.persist(tmp);
parent = tmp;
}
env.commitTransactionAndClear(em);
for (int i = 0; i < NUMBER_OF_UPDATES; i++) {
updateNodes(env, em, Integer.toString(i));
}
} finally {
closeEntityManager(em);
}
}
private void updateNodes(JPAEnvironment env, EntityManager em, String newName) {
env.beginTransaction(em);
Node tmp = em.find(Node.class, 0);
verify(tmp.getVersion() != 0, "wrong version");
while (tmp != null) {
tmp.setName(newName);
Set<Node> children = tmp.getChildren();
if (!children.isEmpty()) {
tmp = children.iterator().next(); // linkedList
verify(tmp.getVersion() != 0, "wrong version");
} else {
tmp = null;
}
}
env.commitTransactionAndClear(em);
}
private abstract class BatchTester {
private final boolean throwsRollbackException;
BatchTester(boolean rollback) {
throwsRollbackException = rollback;
}
abstract void trigger(EntityManager em);
}
@Test
@Skip(server = true)
// RESOURCE_LOCAL only
public void testBatchOLECommit() throws SQLException, NamingException {
BatchTester tester = new BatchTester(true) {
@Override
void trigger(EntityManager em) {
em.getTransaction().commit();
}
};
verifyBatchOLE(tester, true);
clearAllTables();
verifyBatchOLE(tester, false);
}
@Test
@Skip(server = true)
// RESOURCE_LOCAL only
public void testBatchOLEFlush() throws SQLException, NamingException {
BatchTester tester = new BatchTester(true) {
@Override
void trigger(EntityManager em) {
em.flush();
}
};
verifyBatchOLE(tester, true);
clearAllTables();
verifyBatchOLE(tester, false);
}
@Test
@Skip(server = true)
// RESOURCE_LOCAL only
public void testBatchOLEQuery() throws SQLException, NamingException {
BatchTester tester = new BatchTester(true) {
@Override
void trigger(EntityManager em) {
em.createQuery("select d from Department d").setFlushMode(FlushModeType.AUTO).getResultList();
em.flush();
}
};
verifyBatchOLE(tester, true);
clearAllTables();
verifyBatchOLE(tester, false);
}
private void verifyBatchOLE(BatchTester tester, boolean batch) throws NamingException {
JPAEnvironment env = getEnvironment();
final Map map = new HashMap();
map.put(PersistenceUnitProperties.DDL_GENERATION, "none");
map.put(PersistenceUnitProperties.BATCH_WRITING, batch ? "JDBC" : "None");
EntityManagerFactory emf = env.createNewEntityManagerFactory(map);
try {
EntityManager em1 = emf.createEntityManager(map);
try {
em1.getTransaction().begin();
em1.persist(new Department(1, "1"));
em1.persist(new Department(2, "2"));
em1.persist(new Department(3, "3"));
em1.persist(new Department(4, "4"));
em1.persist(new Department(5, "5"));
em1.getTransaction().commit();
em1.clear();
em1.getTransaction().begin();
Department i13 = em1.find(Department.class, 3);
Department i14 = em1.find(Department.class, 4);
Department i15 = em1.find(Department.class, 5);
i13.setName("x");
i14.setName("y");
i15.setName("z");
EntityManager em2 = emf.createEntityManager(map);
try {
em2.getTransaction().begin();
Department i21 = em2.find(Department.class, 1);
Department i22 = em2.find(Department.class, 2);
Department i23 = em2.find(Department.class, 3);
i21.setName("a");
i22.setName("b");
i23.setName("c");
em2.getTransaction().commit();
} finally {
em2.close();
}
try {
tester.trigger(em1);
flop("OptimisticLockException not thrown");
} catch (RollbackException rbe) {
if (tester.throwsRollbackException) {
assertExceptionWrapsOLE(i13, rbe);
} else {
// no RollbackException expected
throw rbe;
}
} catch (OptimisticLockException ole) {
assertFailingEntity(i13, ole);
} catch (PersistenceException pe) {
if (!tester.throwsRollbackException) {
assertExceptionWrapsOLE(i13, pe);
} else {
// RollbackException expected
throw pe;
}
} finally {
if (em1.getTransaction().isActive()) {
em1.getTransaction().rollback();
}
}
} finally {
em1.close();
}
} finally {
emf.close();
}
}
}