/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.testing.tests.jpa.advanced; import org.eclipse.persistence.testing.framework.junit.JUnitTestCase; import org.eclipse.persistence.testing.models.jpa.advanced.Employee; import org.eclipse.persistence.testing.models.jpa.advanced.EmployeeListener; import org.eclipse.persistence.testing.models.jpa.advanced.AdvancedTableCreator; import org.eclipse.persistence.testing.models.jpa.advanced.Project; import java.util.Vector; import javax.persistence.EntityManager; import junit.framework.TestSuite; import junit.framework.Test; /* * Suite used to contain Entity callback tests */ public class CallbackEventJUnitTestSuite extends JUnitTestCase { protected boolean m_reset = false; // reset gets called twice on error protected int m_beforeEvent, m_afterEvent; public CallbackEventJUnitTestSuite() { } public CallbackEventJUnitTestSuite(String name) { super(name); } public static Test suite() { TestSuite suite = new TestSuite(); suite.setName("CallbackEventJUnitTestSuite"); suite.addTest(new CallbackEventJUnitTestSuite("testSetup")); suite.addTest(new CallbackEventJUnitTestSuite("testPersistThenRemoveCalls")); suite.addTest(new CallbackEventJUnitTestSuite("testRemoveUnmanagedNewEntity")); suite.addTest(new CallbackEventJUnitTestSuite("testPersistOnRegisteredObject")); suite.addTest(new CallbackEventJUnitTestSuite("testPreUpdateEvent_UpdateAltered")); suite.addTest(new CallbackEventJUnitTestSuite("testPreUpdateEvent_UpdateReverted")); suite.addTest(new CallbackEventJUnitTestSuite("testMergeCascadeTriggersPrePersist")); return suite; } /** * The setup is done as a test, both to record its failure, and to allow execution in the server. */ public void testSetup() { new AdvancedTableCreator().replaceTables(JUnitTestCase.getServerSession()); clearCache(); } /* * test for bug 4568370:TopLink should perform an unregister on the remove call on a new object * Calls persist/remove on an existing object which will cause a DB exception if the insert * is generated. It also checks that the prepersist/preremove callbacks get issued */ public void testPersistThenRemoveCalls() throws Exception { clearCache(); m_beforeEvent = EmployeeListener.PRE_REMOVE_COUNT; int m_beforePrePersistEvent = EmployeeListener.PRE_PERSIST_COUNT; EntityManager em = createEntityManager(); beginTransaction(em); Employee employee = new Employee(); try { employee = new Employee(); em.persist(employee); em.remove(employee); commitTransaction(em); } catch (RuntimeException ex) { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); throw ex; } m_afterEvent = EmployeeListener.PRE_REMOVE_COUNT; int m_afterPrePersistEvent = EmployeeListener.PRE_PERSIST_COUNT; if (em.find(Employee.class, employee.getId()) != null) { fail("Employee was inserted."); } closeEntityManager(em); assertTrue("The prePersist callback method was not called.", m_beforePrePersistEvent != m_afterPrePersistEvent); assertTrue("The preRemove callback method was not called.", m_beforeEvent != m_afterEvent); } public void testRemoveUnmanagedNewEntity() throws Exception { m_beforeEvent = EmployeeListener.PRE_REMOVE_COUNT; clearCache(); EntityManager em = createEntityManager(); beginTransaction(em); //new_emp should exist only in db try{ Employee newEmp = new Employee(); newEmp.setFirstName("new"); newEmp.setLastName("guy2"); em.remove(newEmp); commitTransaction(em); }catch (RuntimeException ex){ if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); throw ex; } m_afterEvent = EmployeeListener.PRE_REMOVE_COUNT; closeEntityManager(em); this.assertTrue("The preRemove callback method was called, remove should have been ignored.", m_beforeEvent == m_afterEvent); //Employee emp = (Employee)em.find(Employee.class, new_emp.getId()); //this.assertTrue("The remove should have been ignored.", m_beforeEvent == m_afterEvent); } public void testPersistOnRegisteredObject() { clearCache(); EntityManager em = createEntityManager(); beginTransaction(em); //create new employee and persist it try{ Employee newEmp = new Employee(); newEmp.setFirstName("new2"); newEmp.setLastName("guy2"); em.persist(newEmp); m_beforeEvent = EmployeeListener.POST_PERSIST_COUNT; em.persist(newEmp); m_afterEvent = EmployeeListener.POST_PERSIST_COUNT; rollbackTransaction(em); }catch (RuntimeException ex){ if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); throw ex; } this.assertTrue("Calling persist on a managed object should be ignored", m_beforeEvent==m_afterEvent); } public void testPreUpdateEvent_UpdateAltered() { internalTestPreUpdateEvent(false); } public void testPreUpdateEvent_UpdateReverted() { internalTestPreUpdateEvent(true); } // PreUpdate event support must change to allow data modifications in event[PreUpdate] protected void internalTestPreUpdateEvent(boolean shouldUseOriginalName) { EntityManager em = createEntityManager(); //moved from setUp so that it doesn't prevent the testSetup from running when the tables do not exist. Employee new_emp =null; beginTransaction(em); try{ new_emp = new Employee(); new_emp.setFirstName("New"); new_emp.setLastName("Guy"); em.persist(new_emp); commitTransaction(em); }catch (RuntimeException ex){ if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); //rethrow since the test cannot run without the employee persisted throw ex; } clearCache(); em = createEntityManager(); Employee emp = null; int originalVersion = 0; String firstNameExpectedAfterCommit = ""; beginTransaction(em); try{ emp = em.find(Employee.class, new_emp.getId()); originalVersion = getVersion(emp); if(shouldUseOriginalName) { firstNameExpectedAfterCommit = emp.getFirstName(); } else { firstNameExpectedAfterCommit = "Updated" + emp.getFirstName(); } // Assign a new first name to the employee: // it consists of a EmployeeListener.PRE_UPDATE_NAME_PREFIX // and either original first name or an updated one. String firstNameAssigned = EmployeeListener.PRE_UPDATE_NAME_PREFIX + firstNameExpectedAfterCommit; emp.setFirstName(firstNameAssigned); commitTransaction(em); }catch (RuntimeException ex){ if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); throw ex; } // On commit, EmployeeListener will process PreUpdate event removing // EmployeeListener.PRE_UPDATE_NAME_PREFIX from firstName - therefore after commit // employee's first name should be firstNameExpectedAfterCommit. // In case shouldUseOriginalName==true, there should've been no update performed at all - // because by removing the prefix EmployeeListener reverts the object to its original state - // therefore in this case employee's versions before and after commit should be the same. // However change tracking will perform the update... // Verify the employee object from the cache first. emp = em.find(Employee.class, new_emp.getId()); if(!emp.getFirstName().equals(firstNameExpectedAfterCommit)) { fail("In cache: wrong firstName = " + emp.getFirstName() + "; " + firstNameExpectedAfterCommit + " was expected"); } int version = getVersion(emp); // Only check version if it was changed, because if it was reverted back change tracking will still detect change. if(!shouldUseOriginalName) { if(originalVersion >= version) { fail("In cache: wrong version = " + version + "; version > " + originalVersion + " was expected"); } } // Verify the employee object from the db. emp = (Employee) em.createQuery("SELECT OBJECT(e) FROM Employee e WHERE e.id = "+new_emp.getId()).setHint("eclipselink.refresh", Boolean.TRUE).getSingleResult(); if(!emp.getFirstName().equals(firstNameExpectedAfterCommit)) { fail("In DB: wrong firstName = " + emp.getFirstName() + "; " + firstNameExpectedAfterCommit + " was expected"); } version = getVersion(emp); if(!shouldUseOriginalName) { if(originalVersion >= version) { fail("In DB: wrong version = " + version + "; version > " + originalVersion + " was expected"); } } } // helper method, used by internalTestPreUpdate protected int getVersion(Employee emp) { Vector pk = new Vector(); pk.add(emp.getId()); return ((Integer)getServerSession().getDescriptor(Employee.class).getOptimisticLockingPolicy().getWriteLockValue(emp, pk, getServerSession())).intValue(); } // gf 2894: merge does not trigger prePersist callbacks public void testMergeCascadeTriggersPrePersist() { clearCache(); EntityManager em = createEntityManager(); try { beginTransaction(em); // create an employee and persist it Employee emp = new Employee(); emp.setFirstName("James"); emp.setLastName("Dean"); em.persist(emp); // create a new project and associate to persistent employee Project newProj = new Project(); newProj.setName("Rebel Without a Cause"); emp.addProject(newProj); newProj.addTeamMember(emp); // merge should trigger prePersist callback on project em.merge(emp); // merge returns a managed copy of an unmanaged instance Project managedProj = emp.getProjects().iterator().next(); this.assertTrue("Cascading merge to a new object should trigger prePersist callback", managedProj.pre_persist_count == 1); em.merge(emp); // second merge should be ignored this.assertTrue("prePersist callback should only be triggered once", managedProj.pre_persist_count == 1); } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } }