| /* |
| * Copyright (c) 1998, 2020 Oracle and/or its affiliates. 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: |
| // dminsky - initial API and implementation |
| package org.eclipse.persistence.testing.tests.jpa.advanced; |
| |
| import java.util.List; |
| |
| import jakarta.persistence.EntityManager; |
| import jakarta.persistence.LockModeType; |
| |
| import org.eclipse.persistence.testing.framework.QuerySQLTracker; |
| import org.eclipse.persistence.testing.framework.junit.JUnitTestCase; |
| import org.eclipse.persistence.testing.models.jpa.advanced.Address; |
| import org.eclipse.persistence.testing.models.jpa.advanced.AdvancedTableCreator; |
| import org.eclipse.persistence.testing.models.jpa.advanced.EmployeePopulator; |
| |
| import junit.framework.TestSuite; |
| |
| /** |
| * Bug 389265 - OptimisticLock Force Increment increments version both on flush |
| * and on commit |
| * |
| * - Test LockModeType.OPTIMISTIC_FORCE_INCREMENT with combinations of |
| * flush/commit/changes/no-op |
| */ |
| public class OptimisticLockForceIncrementTestSuite extends JUnitTestCase { |
| |
| public OptimisticLockForceIncrementTestSuite() { |
| super(); |
| } |
| |
| public OptimisticLockForceIncrementTestSuite(String name) { |
| super(name); |
| } |
| |
| public static TestSuite suite() { |
| TestSuite suite = new TestSuite("OptimisticLockForceIncrementTestSuite"); |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testSetup")); |
| |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementNoChanges")); |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementPreFlushChanges")); |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementPostFlushChanges")); |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementPreAndPostFlushChanges")); |
| |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementCommitNoChanges")); |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementPreCommitChanges")); |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementFlushCommitNoChanges")); |
| |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementMultipleEntities")); |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementBasicPromoteLock")); |
| suite.addTest(new OptimisticLockForceIncrementTestSuite("testVersionIncrementPromoteLock")); |
| |
| return suite; |
| } |
| |
| public void testSetup() { |
| new AdvancedTableCreator().replaceTables(JUnitTestCase.getServerSession()); |
| |
| EmployeePopulator employeePopulator = new EmployeePopulator(); |
| employeePopulator.buildExamples(); |
| employeePopulator.persistExample(JUnitTestCase.getServerSession()); |
| |
| clearCache(); |
| } |
| |
| public void testVersionIncrementNoChanges() { |
| if (usesSOP()) { |
| // with SOP it fails with: 1 SQL update statement execution(s) |
| // expected:<1> but was:<2> |
| // Looks like a bug: cascade versioning (used with SOP) adds an |
| // extra version increment. |
| return; |
| } |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity = addresses.get(0); |
| assertNotNull("Entity: Address cannot be null", entity); |
| |
| counter.getSqlStatements().clear(); |
| int startVersion = entity.getVersion(); |
| em.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT); |
| |
| em.flush(); |
| |
| em.getTransaction().commit(); |
| int expectedVersion = (startVersion + 1); |
| int actualVersion = entity.getVersion(); |
| |
| em.close(); |
| |
| assertEquals("Version number incremented incorrectly: ", expectedVersion, actualVersion); |
| // SQL statements expected - 1 x update |
| assertEquals("1 SQL update statement execution(s)", 1, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| public void testVersionIncrementPreFlushChanges() { |
| if (usesSOP()) { |
| // with SOP it fails with: 1 SQL update statement execution(s) |
| // expected:<1> but was:<2> |
| // Looks like a bug: cascade versioning (used with SOP) adds an |
| // extra version increment. |
| return; |
| } |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| counter.getSqlStatements().clear(); |
| |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity = addresses.get(0); |
| assertNotNull("Entity: Address cannot be null", entity); |
| |
| int startVersion = entity.getVersion(); |
| em.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT); |
| entity.setCity("Vancouver"); |
| entity.setProvince("BC"); |
| entity.setCountry("Canada"); |
| |
| em.flush(); |
| |
| em.getTransaction().commit(); |
| int expectedVersion = (startVersion + 1); |
| int actualVersion = entity.getVersion(); |
| |
| em.close(); |
| |
| assertEquals("Version number incremented incorrectly: ", expectedVersion, actualVersion); |
| assertEquals("Entity's name should be changed", "Vancouver", entity.getCity()); |
| // SQL statements expected - 1 x update |
| assertEquals("1 SQL update statement execution(s)", 1, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| public void testVersionIncrementPostFlushChanges() { |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| counter.getSqlStatements().clear(); |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity = addresses.get(0); |
| assertNotNull("Entity: Address cannot be null", entity); |
| |
| int startVersion = entity.getVersion(); |
| em.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT); |
| |
| em.flush(); |
| |
| entity.setCity("Toronto"); |
| entity.setProvince("ON"); |
| entity.setCountry("Canada"); |
| |
| em.getTransaction().commit(); |
| int expectedVersion = (startVersion + 1); |
| int actualVersion = entity.getVersion(); |
| |
| em.close(); |
| |
| assertEquals("Version number incremented incorrectly: ", expectedVersion, actualVersion); |
| assertEquals("Entity's name should be changed", "Toronto", entity.getCity()); |
| // SQL statements expected - 2 x update |
| assertEquals("2 SQL update statement execution(s)", 2, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| public void testVersionIncrementPreAndPostFlushChanges() { |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| counter.getSqlStatements().clear(); |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity = addresses.get(0); |
| assertNotNull("Entity: Address cannot be null", entity); |
| |
| int startVersion = entity.getVersion(); |
| em.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT); |
| |
| entity.setCity("Moncton"); |
| entity.setProvince("NB"); |
| entity.setCountry("Canada"); |
| |
| em.flush(); |
| |
| entity.setCity("Halifax"); |
| entity.setProvince("NS"); |
| entity.setCountry("Canada"); |
| |
| em.getTransaction().commit(); |
| int expectedVersion = (startVersion + 1); |
| int actualVersion = entity.getVersion(); |
| |
| em.close(); |
| |
| assertEquals("Version number incremented incorrectly: ", expectedVersion, actualVersion); |
| assertEquals("Entity's name should be changed", "Halifax", entity.getCity()); |
| // SQL statements expected - 2 x update |
| assertEquals("2 SQL update statement execution(s)", 2, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| public void testVersionIncrementCommitNoChanges() { |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| counter.getSqlStatements().clear(); |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity = addresses.get(0); |
| assertNotNull("Entity: Address cannot be null", entity); |
| |
| int startVersion = entity.getVersion(); |
| em.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT); |
| |
| em.getTransaction().commit(); |
| int expectedVersion = (startVersion + 1); |
| int actualVersion = entity.getVersion(); |
| |
| em.close(); |
| |
| assertEquals("Version number incremented incorrectly: ", expectedVersion, actualVersion); |
| // SQL statements expected - 1 x update |
| assertEquals("1 SQL update statement execution(s)", 1, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| public void testVersionIncrementPreCommitChanges() { |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| counter.getSqlStatements().clear(); |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity = addresses.get(0); |
| assertNotNull("Entity: Address cannot be null", entity); |
| |
| int startVersion = entity.getVersion(); |
| em.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT); |
| |
| entity.setCity("Calgary"); |
| entity.setProvince("AB"); |
| entity.setCountry("Canada"); |
| |
| em.getTransaction().commit(); |
| |
| int expectedVersion = (startVersion + 1); |
| int actualVersion = entity.getVersion(); |
| |
| em.close(); |
| |
| assertEquals("Version number incremented incorrectly: ", expectedVersion, actualVersion); |
| assertEquals("Entity's name should be changed", "Calgary", entity.getCity()); |
| // SQL statements expected - 1 x update |
| assertEquals("1 SQL update statement execution(s)", 1, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| public void testVersionIncrementFlushCommitNoChanges() { |
| if (usesSOP()) { |
| // with SOP it fails with: 1 SQL update statement execution(s) |
| // expected:<1> but was:<2> |
| // Looks like a bug: cascade versioning (used with SOP) adds an |
| // extra version increment. |
| return; |
| } |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| counter.getSqlStatements().clear(); |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity = addresses.get(0); |
| assertNotNull("Entity: Address cannot be null", entity); |
| |
| int startVersion = entity.getVersion(); |
| em.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT); |
| |
| em.flush(); |
| |
| em.getTransaction().commit(); |
| int expectedVersion = (startVersion + 1); |
| int actualVersion = entity.getVersion(); |
| |
| em.close(); |
| |
| assertEquals("Version number incremented incorrectly: ", expectedVersion, actualVersion); |
| // SQL statements expected - 1 x update |
| assertEquals("1 SQL update statement execution(s)", 1, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| public void testVersionIncrementMultipleEntities() { |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| counter.getSqlStatements().clear(); |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity1 = addresses.get(0); |
| Address entity2 = addresses.get(1); |
| |
| assertNotNull("Entity 1: Address cannot be null", entity1); |
| assertNotNull("Entity 2: Address cannot be null", entity2); |
| |
| int startVersion1 = entity1.getVersion(); |
| int startVersion2 = entity2.getVersion(); |
| em.lock(entity1, LockModeType.OPTIMISTIC_FORCE_INCREMENT); |
| |
| // update entity 2 |
| entity2.setCity("Kamloops"); |
| entity2.setProvince("BC"); |
| entity2.setCountry("Canada"); |
| |
| em.flush(); |
| |
| // update entity 1 |
| entity1.setCity("New Glasgow"); |
| entity1.setProvince("NS"); |
| entity1.setCountry("Canada"); |
| |
| em.flush(); |
| |
| int expectedVersion1 = (startVersion1 + 1); |
| int actualVersion1 = entity1.getVersion(); |
| |
| int expectedVersion2 = (startVersion2 + 1); |
| int actualVersion2 = entity2.getVersion(); |
| |
| em.getTransaction().rollback(); |
| em.close(); |
| |
| assertEquals("Entity 1: Version number incremented incorrectly: ", expectedVersion1, actualVersion1); |
| assertEquals("Entity 2: Version number incremented incorrectly: ", expectedVersion2, actualVersion2); |
| |
| // 3 SQL update statements expected |
| assertEquals("3 SQL update statement execution(s)", 3, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| public void testVersionIncrementBasicPromoteLock() { |
| if (usesSOP()) { |
| // with SOP it fails with: 1 SQL update statement execution(s) |
| // expected:<1> but was:<2> |
| // Looks like a bug: cascade versioning (used with SOP) adds an |
| // extra version increment. |
| return; |
| } |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| counter.getSqlStatements().clear(); |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity = addresses.get(0); |
| assertNotNull("Entity: Address cannot be null", entity); |
| |
| int startVersion = entity.getVersion(); |
| em.lock(entity, LockModeType.OPTIMISTIC); |
| |
| entity.setCity("Churchill"); |
| entity.setProvince("MB"); |
| entity.setCountry("Canada"); |
| |
| em.flush(); |
| em.getTransaction().commit(); |
| |
| int expectedVersion = (startVersion + 1); |
| int actualVersion = entity.getVersion(); |
| |
| em.close(); |
| |
| assertEquals("Version number incremented incorrectly: ", expectedVersion, actualVersion); |
| // SQL statements expected |
| assertEquals("1 SQL update statement execution(s)", 1, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| public void testVersionIncrementPromoteLock() { |
| QuerySQLTracker counter = null; |
| try { |
| counter = new QuerySQLTracker(getServerSession()); |
| EntityManager em = getEntityManagerFactory().createEntityManager(); |
| em.getTransaction().begin(); |
| |
| List<Address> addresses = em.createQuery("select a from Address a").getResultList(); |
| counter.getSqlStatements().clear(); |
| assertNotNull("Null query results returned", addresses); |
| assertNotSame("No query results returned", addresses.size(), 0); |
| Address entity = addresses.get(0); |
| assertNotNull("Entity: Address cannot be null", entity); |
| |
| int startVersion = entity.getVersion(); |
| em.lock(entity, LockModeType.OPTIMISTIC); |
| |
| entity.setCity("Banff"); |
| entity.setProvince("AB"); |
| entity.setCountry("Canada"); |
| |
| em.flush(); |
| |
| em.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT); |
| |
| entity.setCity("London"); |
| entity.setProvince("ON"); |
| entity.setCountry("Canada"); |
| |
| em.getTransaction().commit(); |
| |
| int expectedVersion = (startVersion + 1); |
| int actualVersion = entity.getVersion(); |
| |
| em.close(); |
| |
| assertEquals("Version number incremented incorrectly: ", expectedVersion, actualVersion); |
| // SQL statements expected |
| assertEquals("2 SQL update statement execution(s)", 2, countNumberOfUpdateStatements(counter)); |
| } finally { |
| if (counter != null) { |
| counter.remove(); |
| } |
| } |
| } |
| |
| protected int countNumberOfUpdateStatements(QuerySQLTracker counter) { |
| if (counter == null || counter.getSqlStatements().size() == 0) { |
| return 0; |
| } |
| |
| int numberOfStatements = 0; |
| List<String> statements = counter.getSqlStatements(); |
| for (String statement : statements) { |
| if (statement != null && statement.trim().toUpperCase().startsWith("UPDATE")) { |
| numberOfStatements++; |
| } |
| } |
| return numberOfStatements; |
| } |
| |
| } |