| /* |
| * 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 static org.junit.Assert.fail; |
| |
| import java.sql.Connection; |
| import java.sql.Date; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import jakarta.persistence.EntityManager; |
| import jakarta.persistence.EntityNotFoundException; |
| import jakarta.persistence.FlushModeType; |
| import jakarta.persistence.Query; |
| 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.Department; |
| import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Employee; |
| import org.eclipse.persistence.testing.models.wdf.jpa1.employee.Review; |
| import org.eclipse.persistence.testing.tests.wdf.jpa1.JPA1Base; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| |
| public class TestRefresh extends JPA1Base { |
| |
| @Test |
| public void testRefreshNew() { |
| /* |
| * Refresh on an entity that is not under control of the entity manager |
| * should throw an IllegalArgumentException. |
| */ |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| int id; |
| Department dep; |
| try { |
| id = 1; |
| dep = new Department(id, "NEW"); |
| env.beginTransaction(em); |
| try { |
| em.refresh(dep); |
| flop("refresh did not throw IllegalArgumentException"); |
| } catch (IllegalArgumentException e) { |
| verify(true, ""); |
| } |
| env.rollbackTransactionAndClear(em); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| @Bugzilla(bugid = 309681) |
| public void testRefreshManagedNew() throws SQLException { |
| /* |
| * Note: The specification doesn't state explicitly how to behave in |
| * this case, so we test our interpretation: - If the entity doesn't |
| * exist on the database, nothing is changed - If the entity exists on |
| * the database, the data is loaded and the state changes from |
| * FOR_INSERT to FOR_UPDATE (in order to avoid errors at flush time) |
| */ |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| int id; |
| Department dep; |
| try { |
| // case 2: state MANAGED_NEW, but exists on DB (inserted in |
| // different tx) |
| id = 12; |
| dep = new Department(id, "MANAGED_NEW"); |
| Department depInserted = new Department(id, "INSERTED"); |
| env.beginTransaction(em); |
| em.persist(dep); |
| insertDepartmentIntoDatabase(depInserted); |
| verifyExistenceOnDatabase(id); |
| // entity is now in state MANAGED_NEW, but record exists on db |
| em.refresh(dep); |
| checkDepartment(dep, id, "INSERTED"); // this should now be in state |
| // MANAGED |
| verify(em.contains(dep), "Department is not managed"); |
| dep.setName("UPDATED"); |
| env.commitTransactionAndClear(em); |
| // verify that updated name present on db |
| dep = em.find(Department.class, id); |
| checkDepartment(dep, id, "UPDATED"); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| @Bugzilla(bugid = 309681) |
| public void testRefreshManagedNewNotOnDB() throws SQLException { |
| /* |
| * Note: The specification doesn't state explicitly how to behave in |
| * this case, so we test our interpretation: - If the entity doesn't |
| * exist on the database, nothing is changed - If the entity exists on |
| * the database, the data is loaded and the state changes from |
| * FOR_INSERT to FOR_UPDATE (in order to avoid errors at flush time) |
| */ |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| int id; |
| Department dep; |
| try { |
| // case 1: state MANAGED_NEW and does not exist on DB |
| id = 11; |
| dep = new Department(id, "MANAGED_NEW"); |
| env.beginTransaction(em); |
| em.persist(dep); // this is now in state MANAGED_NEW, but not on db |
| em.refresh(dep); // nothing should happen |
| verify(em.contains(dep), "Department is not managed"); |
| env.commitTransactionAndClear(em); |
| verifyExistenceOnDatabase(id); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void testRefreshManaged() throws SQLException { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| int id; |
| Department dep; |
| Department updatedDep; |
| try { |
| // case 1: undo own changes |
| id = 21; |
| dep = new Department(id, "MANAGED"); |
| env.beginTransaction(em); |
| em.persist(dep); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| dep = em.find(Department.class, id); // this is now in |
| // state MANAGED |
| dep.setName("UPDATED"); |
| em.refresh(dep); |
| checkDepartment(dep, id, "MANAGED"); |
| verify(em.contains(dep), "Department is not managed"); |
| env.commitTransactionAndClear(em); |
| // verify that original name present on db |
| dep = em.find(Department.class, id); |
| checkDepartment(dep, id, "MANAGED"); |
| // case 2: refresh with data changed on db in a different tx |
| id = 22; |
| dep = new Department(id, "MANAGED"); |
| updatedDep = new Department(id, "UPDATED"); |
| env.beginTransaction(em); |
| em.persist(dep); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| dep = em.find(Department.class, id); // this is now in |
| // state MANAGED |
| updateDepartmentOnDatabase(updatedDep); |
| em.refresh(dep); |
| checkDepartment(dep, id, "UPDATED"); |
| verify(em.contains(dep), "Department is not managed"); |
| dep.setName("MANAGED"); |
| env.commitTransactionAndClear(em); |
| // verify that original name present on db |
| dep = em.find(Department.class, id); |
| checkDepartment(dep, id, "MANAGED"); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| @Bugzilla(bugid = 309681) |
| public void testRefreshManagedCheckContains() throws SQLException { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| int id; |
| Department dep; |
| try { |
| // case 3: try to refresh, but record has been deleted on db in a |
| // different tx |
| /* |
| * We expect an EntityNotFoundException. However, the specification |
| * does not state explicitly in which state the managed entity |
| * should be after the exception. We are going to remove the entity |
| * from the persistence context, so it is detached afterwards. |
| */ |
| id = 23; |
| dep = new Department(id, "MANAGED"); |
| env.beginTransaction(em); |
| em.persist(dep); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| dep = em.find(Department.class, id); // this is now in |
| // state MANAGED |
| deleteDepartmentFromDatabase(id); |
| verifyAbsenceFromDatabase(em, id); |
| try { |
| em.refresh(dep); |
| flop("refresh did not throw EntityNotFoundException"); |
| } catch (EntityNotFoundException e) { |
| verify(true, ""); |
| } |
| verify(!em.contains(dep), "entity still managed after EntityNotFoundException"); |
| env.rollbackTransactionAndClear(em); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| private void doRefreshDeleted(int id, boolean flush) throws SQLException { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| try { |
| Department dep = new Department(id, "DELETED"); |
| env.beginTransaction(em); |
| em.persist(dep); |
| env.commitTransactionAndClear(em); |
| |
| env.beginTransaction(em); |
| dep = em.find(Department.class, id); |
| em.remove(dep); |
| if (flush) { |
| em.flush(); // this is now in state DELETE_EXECUTED |
| verifyAbsenceFromDatabase(em, id); |
| } |
| try { |
| em.refresh(dep); |
| fail("refresh did not throw IllegalArgumentException"); |
| } catch (IllegalArgumentException e) { |
| // expected |
| } |
| if (env.isTransactionActive(em)) { |
| env.rollbackTransactionAndClear(em); |
| } |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| /* |
| * Refreshing an entity in state "removed" should raise an |
| * IllegalArgumentException |
| */ |
| @Test |
| public void testRefreshDeleted() throws SQLException { |
| doRefreshDeleted(31, false); |
| doRefreshDeleted(32, true); |
| } |
| |
| @Test |
| public void testRefreshDetached() { |
| /* |
| * Refresh on a detached entity should throw an |
| * IllegalArgumentException. |
| */ |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| int id; |
| Department dep; |
| Department detachedDep; |
| try { |
| // case 1: entity exists on DB, but not contained in persistence |
| // context |
| id = 41; |
| dep = new Department(id, "DETACHED"); |
| // firstly, we create a department on the database |
| env.beginTransaction(em); |
| em.persist(dep); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| try { |
| em.refresh(dep); |
| flop("refresh did not throw IllegalArgumentException"); |
| } catch (IllegalArgumentException e) { |
| verify(true, ""); |
| } |
| env.rollbackTransactionAndClear(em); |
| // case 2: entity is contained in persistence context, but object to |
| // be merged has different object identity |
| // case 2a: state of known object: MANAGED_NEW |
| id = 42; |
| dep = new Department(id, "MANAGED_NEW"); |
| detachedDep = new Department(id, "DETACHED"); |
| env.beginTransaction(em); |
| em.persist(dep); // this is now in state new |
| try { |
| em.refresh(detachedDep); // this object is detached |
| flop("refresh did not throw IllegalArgumentException"); |
| } catch (IllegalArgumentException e) { |
| verify(true, ""); |
| } |
| env.rollbackTransactionAndClear(em); |
| // case 2b: state of known object: MANAGED |
| id = 43; |
| dep = new Department(id, "MANAGED"); |
| detachedDep = new Department(id, "DETACHED"); |
| env.beginTransaction(em); |
| em.persist(dep); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| dep = em.find(Department.class, id); // this is now in |
| // state MANAGED |
| try { |
| em.refresh(detachedDep); // this object is detached |
| flop("refresh did not throw IllegalArgumentException"); |
| } catch (IllegalArgumentException e) { |
| verify(true, ""); |
| } |
| env.rollbackTransactionAndClear(em); |
| // case 2c: state of known object: DELETED |
| id = 44; |
| dep = new Department(id, "DELETED"); |
| detachedDep = new Department(id, "DETACHED"); |
| env.beginTransaction(em); |
| em.persist(dep); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| dep = em.find(Department.class, id); |
| em.remove(dep); // this is now in state DELETED |
| try { |
| em.refresh(detachedDep); // this object is detached |
| flop("refresh did not throw IllegalArgumentException"); |
| } catch (IllegalArgumentException e) { |
| verify(true, ""); |
| } |
| env.rollbackTransactionAndClear(em); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void testNotAnEntity() { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| try { |
| env.beginTransaction(em); |
| try { |
| em.refresh("Hutzliputz"); |
| flop("no IllegalArgumentException "); |
| } catch (IllegalArgumentException e) { |
| verify(true, ""); |
| } finally { |
| env.rollbackTransactionAndClear(em); |
| } |
| env.beginTransaction(em); |
| try { |
| em.refresh(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(); |
| int id; |
| Department dep; |
| try { |
| id = 61; |
| dep = new Department(id, "NO_TX"); |
| verify(!env.isTransactionActive(em), "transaction is active, can't execute test"); |
| try { |
| em.refresh(dep); |
| flop("refresh did not throw TransactionRequiredException"); |
| } catch (TransactionRequiredException e) { |
| verify(true, ""); |
| } |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void testRefreshManagedWithRelationships() { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| try { |
| // case 1: undo own changes |
| Department dep = new Department(101, "Evangelists"); |
| Employee emp = new Employee(102, "First", "Last", dep); |
| Review rev1 = new Review(103, Date.valueOf("2006-02-03"), "Code inspection"); |
| Review rev2 = new Review(104, Date.valueOf("2006-02-04"), "Design review"); |
| emp.addReview(rev1); |
| emp.addReview(rev2); |
| env.beginTransaction(em); |
| em.persist(dep); |
| em.persist(emp); |
| em.persist(rev1); |
| em.persist(rev2); |
| env.commitTransactionAndClear(em); |
| env.beginTransaction(em); |
| emp = em.find(Employee.class, emp.getId()); |
| rev1 = em.find(Review.class, rev1.getId()); |
| Review rev3 = new Review(105, Date.valueOf("2006-02-05"), "Test coverage"); |
| Set<Review> reviews = new HashSet<Review>(); |
| reviews.add(rev1); |
| reviews.add(rev3); |
| emp.setReviews(reviews); |
| rev1.setReviewText("UPDATED"); |
| em.refresh(emp); |
| verify(em.contains(emp), "Employee is not managed"); |
| Set<Review> reviewsAfterRefresh = emp.getReviews(); |
| verify(reviewsAfterRefresh.size() == 2, "Employee contains wrong number of reviews: " + reviewsAfterRefresh.size()); |
| for (Review rev : reviewsAfterRefresh) { |
| int id = rev.getId(); |
| verify(id == rev1.getId() || id == rev2.getId(), "Employee has wrong review: " + id); |
| verify(em.contains(rev), "Review " + id + " is not managed"); |
| } |
| env.commitTransactionAndClear(em); |
| // verify that original name present on db |
| rev1 = em.find(Review.class, rev1.getId()); |
| verify("UPDATED".equals(rev1.getReviewText()), "Rev1 has wrong text: " + rev1.getReviewText()); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void testTransactionMarkedForRollback() { |
| final JPAEnvironment env = getEnvironment(); |
| final EntityManager em = env.getEntityManager(); |
| Department dep = new Department(111, "dep111"); |
| try { |
| env.beginTransaction(em); |
| em.persist(dep); |
| env.commitTransaction(em); |
| em.clear(); |
| env.beginTransaction(em); |
| dep = em.find(Department.class, dep.getId()); |
| dep.setName("updated"); |
| env.markTransactionForRollback(em); |
| em.refresh(dep); |
| checkDepartment(dep, dep.getId(), "dep111"); |
| verify(em.contains(dep), "entity not contained in persistence context"); |
| env.rollbackTransaction(em); |
| } finally { |
| closeEntityManager(em); |
| } |
| } |
| |
| private void verifyExistenceOnDatabase(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(), "no department with id " + departmentId + " found using JDBC."); |
| } finally { |
| rs.close(); |
| } |
| } finally { |
| stmt.close(); |
| } |
| } finally { |
| conn.close(); |
| } |
| } |
| |
| private void verifyAbsenceFromDatabase(EntityManager em, int id) { |
| Query query = em.createQuery("SELECT d.id from Department d where d.id = ?1"); |
| query.setFlushMode(FlushModeType.COMMIT); |
| query.setParameter(1, id); |
| verify(query.getResultList().size() == 0, "wrong result list size"); |
| } |
| |
| private void deleteDepartmentFromDatabase(int departmentId) throws SQLException { |
| Connection conn = getEnvironment().getDataSource().getConnection(); |
| try { |
| PreparedStatement stmt = conn.prepareStatement("delete from TMP_DEP where ID = ?"); |
| try { |
| stmt.setInt(1, departmentId); |
| stmt.executeUpdate(); |
| } finally { |
| stmt.close(); |
| } |
| } finally { |
| conn.close(); |
| } |
| } |
| |
| private void insertDepartmentIntoDatabase(Department department) throws SQLException { |
| Connection conn = getEnvironment().getDataSource().getConnection(); |
| try { |
| PreparedStatement stmt = conn.prepareStatement("insert into TMP_DEP (ID, NAME, VERSION) values (?, ?, ?)"); |
| try { |
| stmt.setInt(1, department.getId()); |
| stmt.setString(2, department.getName()); |
| stmt.setShort(3, (short) 0); |
| stmt.executeUpdate(); |
| } finally { |
| stmt.close(); |
| } |
| } finally { |
| conn.close(); |
| } |
| } |
| |
| private void updateDepartmentOnDatabase(Department department) throws SQLException { |
| Connection conn = getEnvironment().getDataSource().getConnection(); |
| try { |
| PreparedStatement stmt = conn.prepareStatement("update TMP_DEP set NAME = ? where ID = ?"); |
| try { |
| stmt.setString(1, department.getName()); |
| stmt.setInt(2, department.getId()); |
| stmt.executeUpdate(); |
| } finally { |
| stmt.close(); |
| } |
| } finally { |
| conn.close(); |
| } |
| } |
| |
| private void checkDepartment(Department department, int id, String name) { |
| verify(department != null, "department is null"); |
| verify(id == department.getId(), "department has wrong id: " + department.getId()); |
| verify(name.equals(department.getName()), "department has wrong name: " + department.getName()); |
| } |
| } |