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