/* * JBoss, Home of Professional Open Source. * Copyright 2010, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ejb3.examples.employeeregistry; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.concurrent.Callable; import java.util.logging.Logger; import javax.ejb.EJB; import javax.persistence.EmbeddedId; import javax.persistence.EntityManager; import javax.persistence.GeneratedValue; import javax.persistence.IdClass; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.ejb3.examples.employeeregistry.ch09.entitymanager.SimpleEmployee; import org.jboss.ejb3.examples.employeeregistry.ch10.mapping.EmbeddedEmployeePK; import org.jboss.ejb3.examples.employeeregistry.ch10.mapping.EmployeeType; import org.jboss.ejb3.examples.employeeregistry.ch10.mapping.EmployeeWithEmbeddedPK; import org.jboss.ejb3.examples.employeeregistry.ch10.mapping.EmployeeWithExternalCompositePK; import org.jboss.ejb3.examples.employeeregistry.ch10.mapping.EmployeeWithMappedSuperClassId; import org.jboss.ejb3.examples.employeeregistry.ch10.mapping.EmployeeWithProperties; import org.jboss.ejb3.examples.employeeregistry.ch10.mapping.ExternalEmployeePK; import org.jboss.ejb3.examples.employeeregistry.ch11.relationships.Address; import org.jboss.ejb3.examples.employeeregistry.ch11.relationships.Computer; import org.jboss.ejb3.examples.employeeregistry.ch11.relationships.Customer; import org.jboss.ejb3.examples.employeeregistry.ch11.relationships.Employee; import org.jboss.ejb3.examples.employeeregistry.ch11.relationships.Phone; import org.jboss.ejb3.examples.employeeregistry.ch11.relationships.PhoneType; import org.jboss.ejb3.examples.employeeregistry.ch11.relationships.Task; import org.jboss.ejb3.examples.employeeregistry.ch11.relationships.Team; import org.jboss.ejb3.examples.employeeregistry.ch14.listener.EntityListenerEmployee; import org.jboss.ejb3.examples.employeeregistry.ch14.listener.EventTracker; import org.jboss.ejb3.examples.testsupport.dbquery.EntityManagerExposingBean; import org.jboss.ejb3.examples.testsupport.dbquery.EntityManagerExposingLocalBusiness; import org.jboss.ejb3.examples.testsupport.entity.IdentityBase; import org.jboss.ejb3.examples.testsupport.txwrap.TaskExecutionException; import org.jboss.ejb3.examples.testsupport.txwrap.TxWrappingLocalBusiness; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; /** * Tests to ensure that we can do simple CRUD operations * upon an object view (Entity beans), and see our changes persisted * across transactions. * * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a> * @version $Revision: $ */ @RunWith(Arquillian.class) public class EmployeeIntegrationTest { //-------------------------------------------------------------------------------------|| // Class Members ----------------------------------------------------------------------|| //-------------------------------------------------------------------------------------|| /** * Logger */ private static final Logger log = Logger.getLogger(EmployeeIntegrationTest.class.getName()); /** * The Deployment into the EJB Container */ @Deployment public static JavaArchive getDeployment() { final JavaArchive archive = ShrinkWrap.create(JavaArchive.class, "entities.jar").addPackages(false, SimpleEmployee.class.getPackage(), EmployeeWithMappedSuperClassId.class.getPackage(), Employee.class.getPackage(), TxWrappingLocalBusiness.class.getPackage(), IdentityBase.class.getPackage(), EntityListenerEmployee.class.getPackage(), EntityManagerExposingBean.class.getPackage(), org.jboss.ejb3.examples.employeeregistry.ch12.inheritance.singleclass.Employee.class.getPackage(), org.jboss.ejb3.examples.employeeregistry.ch12.inheritance.tableperclass.Employee.class.getPackage(), org.jboss.ejb3.examples.employeeregistry.ch12.inheritance.joined.Employee.class.getPackage()) .addAsManifestResource("persistence.xml"); log.info(archive.toString(true)); return archive; } /* * Data for our tests */ private static final long ID_DAVE = 1L; private static final long ID_JOSH = 2L; private static final long ID_RICK = 3L; private static final String NAME_DAVE = "Dave"; private static final String NAME_DAVE_NEW = "Dave - The Good Doctor"; private static final String NAME_JOSH = "Josh"; private static final String NAME_RICK = "Rick, Jr."; //-------------------------------------------------------------------------------------|| // Instance Members -------------------------------------------------------------------|| //-------------------------------------------------------------------------------------|| /** * EJB which wraps supplied {@link Callable} instances inside of a new Tx */ @EJB(mappedName="java:global/entities/TxWrappingBean!org.jboss.ejb3.examples.testsupport.txwrap.TxWrappingLocalBusiness") private TxWrappingLocalBusiness txWrapper; /** * EJB which provides direct access to an {@link EntityManager}'s method for use in testing. * Must be called inside an existing Tx so that returned entities are not detached. */ @EJB(mappedName="java:global/entities/EntityManagerExposingBean!org.jboss.ejb3.examples.testsupport.dbquery.EntityManagerExposingLocalBusiness") private EntityManagerExposingLocalBusiness emHook; //-------------------------------------------------------------------------------------|| // Lifecycle --------------------------------------------------------------------------|| //-------------------------------------------------------------------------------------|| /** * Manually looks up EJBs in JNDI and assigns them */ @Before public void injectEjbsAndClearDB() throws Throwable { // Clear all employees before running, just in case this.clearAllEmployees(); } /** * Resets all entity callbacks */ @Before public void clearEntityCallbacks() { EventTracker.reset(); } /** * Issues a deletion to remove all employees from persistent storage * @throws Throwable */ @After public void clearAllEmployees() throws Throwable { // Clear the DB of all Employees try { txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { final EntityManager em = emHook.getEntityManager(); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(SimpleEmployee.class, em); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(EmployeeWithMappedSuperClassId.class, em); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(EmployeeWithExternalCompositePK.class, em); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(EmployeeWithProperties.class, em); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(Computer.class, em); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(Phone.class, em); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(Customer.class, em); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(Task.class, em); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(Team.class, em); EmployeeIntegrationTest.this.deleteAllEntitiesOfType(Employee.class, em); return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } //-------------------------------------------------------------------------------------|| // Tests ------------------------------------------------------------------------------|| //-------------------------------------------------------------------------------------|| /** * Tests that we can use the {@link EntityManager} to perform simple * CRUD (Create, remove, update, delete) operations on an object view, * and these changes will be persisted as expected. */ @Test public void persistAndModifyEmployees() throws Throwable { try { // Execute the addition of the employees, and conditional checks, in the context of a Transaction txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Create a few plain instances final SimpleEmployee josh = new SimpleEmployee(ID_DAVE, NAME_DAVE); final SimpleEmployee dave = new SimpleEmployee(ID_JOSH, NAME_JOSH); final SimpleEmployee rick = new SimpleEmployee(ID_RICK, NAME_RICK); // Get the EntityManager from our test hook final EntityManager em = emHook.getEntityManager(); // Now first check if any employees are found in the underlying persistent // storage (shouldn't be) Assert.assertNull("Employees should not have been added to the EM yet", em.find(SimpleEmployee.class, ID_DAVE)); // Check if the object is managed (shouldn't be) Assert.assertFalse("Employee should not be managed yet", em.contains(josh)); // Now persist the employees em.persist(dave); em.persist(josh); em.persist(rick); log.info("Added: " + rick + dave + josh); // The employees should be managed now Assert.assertTrue("Employee should be managed now, after call to persist", em.contains(josh)); // Return return null; } }); // Now change Employee Dave's name in a Tx; we'll verify the changes were flushed to the DB later txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get an EM final EntityManager em = emHook.getEntityManager(); // Look up "Dave" by ID from the EM final SimpleEmployee dave = em.find(SimpleEmployee.class, ID_DAVE); // Change Dave's name dave.setName(NAME_DAVE_NEW); log.info("Changing Dave's name: " + dave); // That's it - the new name should be flushed to the DB when the Tx completes return null; } }); // Since we've changed Dave's name in the last transaction, ensure that we see the changes // have been flushed and we can see them from a new Tx. txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get an EM final EntityManager em = emHook.getEntityManager(); // Make a new "Dave" as a detached object with same primary key, but a different name final SimpleEmployee dave = new SimpleEmployee(ID_DAVE, NAME_DAVE_NEW); // Merge these changes on the detached instance with the DB em.merge(dave); // Ensure we see the name change Assert.assertEquals("Employee Dave's name should have been changed", NAME_DAVE_NEW, dave.getName()); // Now we'll detach Dave from the EM, this makes the object no longer managed em.detach(dave); // Change Dave's name again to some dummy value. Because the object is // detached and no longer managed, we should not see this new value // synchronized with the DB dave.setName("A name we shouldn't see flushed to persistence"); log.info("Changing Dave's name after detached: " + dave); // Return return null; } }); // Another check. We changed Dave's name when the entity was no longer // managed and attached to an EM, so ensure that any changes we made // were not flushed out txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get an EM final EntityManager em = emHook.getEntityManager(); // Make a new "Dave" instance final SimpleEmployee dave = em.find(SimpleEmployee.class, ID_DAVE); log.info("Lookup of Dave after we changed his name on a detached instance: " + dave); // Ensure that the last name change we gave to Dave did not take affect Assert .assertEquals("Detached object values should not have been flushed", NAME_DAVE_NEW, dave.getName()); // Return return null; } }); // Uh oh, Rick has decided to leave the company. Let's delete his record. txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get an EM final EntityManager em = emHook.getEntityManager(); // Look up Rick final SimpleEmployee rick = em.find(SimpleEmployee.class, ID_RICK); // Remove em.remove(rick); log.info("Deleted: " + rick); // Return return null; } }); // Ensure we can no longer find Rick in the DB txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get an EM final EntityManager em = emHook.getEntityManager(); // Look up Rick final SimpleEmployee rick = em.find(SimpleEmployee.class, ID_RICK); // Assert Assert.assertNull("Rick should have been removed from the DB", rick); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of JPA autogeneration of primary keys, using * {@link EmployeeWithMappedSuperClassId} which inherits PK support from * {@link IdentityBase#getId()}. * @throws Throwable */ @Test public void autogenPrimaryKeyFromMappedSuperClass() throws Throwable { try { // Create a new Employee, and let JPA give us the PK value final Long id = txWrapper.wrapInTx(new Callable<Long>() { @Override public Long call() throws Exception { // Make a new Employee final EmployeeWithMappedSuperClassId alrubinger = new EmployeeWithMappedSuperClassId( "Andrew Lee Rubinger"); // Ensure we have no ID now Assert.assertNull("Primary key should not be set yet", alrubinger.getId()); // Persist emHook.getEntityManager().persist(alrubinger); // Now show that JPA gave us a primary key as generated final Long id = alrubinger.getId(); Assert.assertNotNull("Persisting an entity with PK " + GeneratedValue.class.getName() + " should be created", id); log.info("Persisted: " + alrubinger); // Return return id; } }); // Ensure we can look up this new entity by the PK we've been given txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Look up the Employee by the ID we just gave final EmployeeWithMappedSuperClassId employee = emHook.getEntityManager().find( EmployeeWithMappedSuperClassId.class, id); // Ensure found Assert.assertNotNull("Employee should be able to be looked up by PK", employee); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of an entity which gets its identity via an * {@link IdClass} - {@link ExternalEmployeePK}. * @throws Throwable */ @Test public void externalCompositePrimaryKey() throws Throwable { try { txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Define the values to compose a primary key identity final String lastName = "Rubinger"; final Long ssn = 100L; // Not real ;) // Create a new Employee which uses a custom @IdClass final EmployeeWithExternalCompositePK employee = new EmployeeWithExternalCompositePK(); employee.setLastName(lastName); employee.setSsn(ssn); // Persist final EntityManager em = emHook.getEntityManager(); em.persist(employee); log.info("Persisted: " + employee); // Now look up using our custom composite PK value class final ExternalEmployeePK pk = new ExternalEmployeePK(); pk.setLastName(lastName); pk.setSsn(ssn); final EmployeeWithExternalCompositePK roundtrip = em.find(EmployeeWithExternalCompositePK.class, pk); // Ensure found Assert.assertNotNull("Should have been able to look up record via a custom PK composite class", roundtrip); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of an entity which gets its identity via an * {@link EmbeddedId} - {@link EmployeeWithEmbeddedPK} * @throws Throwable */ @Test public void embeddedCompositePrimaryKey() throws Throwable { try { txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Define the values to compose a primary key identity final String lastName = "Rubinger"; final Long ssn = 100L; // Not real ;) // Create a new Employee which uses an Embedded PK Class final EmployeeWithEmbeddedPK employee = new EmployeeWithEmbeddedPK(); final EmbeddedEmployeePK pk = new EmbeddedEmployeePK(); pk.setLastName(lastName); pk.setSsn(ssn); employee.setId(pk); // Persist final EntityManager em = emHook.getEntityManager(); em.persist(employee); log.info("Persisted: " + employee); // Now look up using our custom composite PK value class final EmployeeWithEmbeddedPK roundtrip = em.find(EmployeeWithEmbeddedPK.class, pk); // Ensure found Assert .assertNotNull("Should have been able to look up record via a custom embedded PK class", roundtrip); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of an entity with a series of nonstandard * mappings which require additional JPA metadata to show * the ORM layer how things should be represented in the DB. */ @Test public void propertyMappings() throws Throwable { // Define the values for our employee final byte[] image = new byte[] {0x00}; final Date since = new Date(0L); // Employed since the epoch final EmployeeType type = EmployeeType.PEON; final String currentAssignment = "Learn JPA and EJB!"; try { final Long id = txWrapper.wrapInTx(new Callable<Long>() { @Override public Long call() throws Exception { // Create a new Employee final EmployeeWithProperties employee = new EmployeeWithProperties(); employee.setImage(image); employee.setSince(since); employee.setType(type); employee.setCurrentAssignment(currentAssignment); // Persist final EntityManager em = emHook.getEntityManager(); em.persist(employee); log.info("Persisted: " + employee); // Get the ID, now that one's been assigned final Long id = employee.getId(); // Return return id; } }); // Now execute in another Tx, to ensure we get a real DB load from the EM, // and not just a direct reference back to the object we persisted. txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Roundtrip lookup final EmployeeWithProperties roundtrip = emHook.getEntityManager() .find(EmployeeWithProperties.class, id); log.info("Roundtrip: " + roundtrip); final Calendar suppliedSince = Calendar.getInstance(); suppliedSince.setTime(since); final Calendar obtainedSince = Calendar.getInstance(); obtainedSince.setTime(roundtrip.getSince()); // Assert all values are as expected Assert.assertEquals("Binary object was not mapped properly", image[0], roundtrip.getImage()[0]); Assert.assertEquals("Temporal value was not mapped properly", suppliedSince.get(Calendar.YEAR), obtainedSince.get(Calendar.YEAR)); Assert.assertEquals("Temporal value was not mapped properly", suppliedSince.get(Calendar.MONTH), obtainedSince.get(Calendar.MONTH)); Assert.assertEquals("Temporal value was not mapped properly", suppliedSince.get(Calendar.DATE), obtainedSince.get(Calendar.DATE)); Assert.assertEquals("Enumerated value was not as expected", type, roundtrip.getType()); Assert.assertNull("Transient property should not have been persisted", roundtrip.getCurrentAssignment()); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of the 1:1 Unidirectional Mapping Between * {@link Employee} and {@link Address} * @throws Throwable */ @Test public void oneToOneUnidirectionalMapping() throws Throwable { // Create a new Employee final Employee alrubinger = new Employee("Andrew Lee Rubinger"); // Create a new Address final Address address = new Address("1 JBoss Way", "Boston", "MA"); try { // Persist and associate an Employee and Address final Long employeeId = txWrapper.wrapInTx(new Callable<Long>() { @Override public Long call() throws Exception { // Get the EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(alrubinger); em.persist(address); // Associate alrubinger.setAddress(address); // Return return alrubinger.getId(); } }); // Now ensure when we look up the Address again by Employee after Tx has completed, // all's as expected txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get the EM final EntityManager em = emHook.getEntityManager(); // Look up the employee final Employee roundtripEmployee = em.find(Employee.class, employeeId); // Get the address final Address persistedAddress = roundtripEmployee.getAddress(); // Ensure equal Assert.assertEquals("Persisted address association was not as expected", address, persistedAddress); // Clean up the association so we can remove roundtripEmployee.setAddress(null); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of the 1:1 Bidirectional Mapping Between * {@link Employee} and {@link Computer} * @throws Throwable */ @Test public void oneToOneBidirectionalMapping() throws Throwable { // Create a new Computer final Computer computer = new Computer(); computer.setMake("Computicorp"); computer.setModel("ZoomFast 100"); // Create a new Employee final Employee carloDeWolf = new Employee("Carlo de Wolf"); try { /* * We don't associate yet; our cascade policy will prohibit * persisting entities with relationships that are not themselves * yet persisted */ // Persist and associate final Long employeeId = txWrapper.wrapInTx(new Callable<Long>() { @Override public Long call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(carloDeWolf); em.persist(computer); // Associate *both* sides of a bidirectional relationship carloDeWolf.setComputer(computer); computer.setOwner(carloDeWolf); // Return return carloDeWolf.getId(); } }); // Now check all was associated correctly txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get the EM final EntityManager em = emHook.getEntityManager(); // Get the Employee final Employee carloRoundtrip = em.find(Employee.class, employeeId); // Get the Computer via the Employee final Computer computerRoundtrip = carloRoundtrip.getComputer(); // Get the Employee via the Computer final Employee ownerOfComputer = computer.getOwner(); log.info("Employee " + carloRoundtrip + " has computer " + computerRoundtrip); log.info("Computer " + computerRoundtrip + " has owner " + ownerOfComputer); // Assert all's as expected Assert.assertEquals("Computer of employee was not as expected ", computer, computerRoundtrip); Assert.assertEquals("Owner of computer was not as expected ", carloDeWolf, ownerOfComputer); // Clean up the associations so we can remove ownerOfComputer.setComputer(null); computerRoundtrip.setOwner(null); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of the 1:N Unidirectional Mapping Between * {@link Employee} and {@link Phone} * @throws Throwable */ @Test public void oneToManyUnidirectionalMapping() throws Throwable { // Create an Employee final Employee jaikiranPai = new Employee("Jaikiran Pai"); // Create a couple Phones final Phone phone1 = new Phone(); phone1.setNumber("800-USE-JBOSS"); phone1.setType(PhoneType.WORK); final Phone phone2 = new Phone(); phone2.setNumber("800-EJB-TIME"); phone2.setType(PhoneType.MOBILE); try { // Persist and associate final Long employeeId = txWrapper.wrapInTx(new Callable<Long>() { @Override public Long call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(jaikiranPai); em.persist(phone1); em.persist(phone2); // Associate jaikiranPai.getPhones().add(phone1); jaikiranPai.getPhones().add(phone2); // Return return jaikiranPai.getId(); } }); // Now check all was associated correctly txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get the EM final EntityManager em = emHook.getEntityManager(); // Get the Employee final Employee jaikiranRoundtrip = em.find(Employee.class, employeeId); // Get Phones via the Employee final Collection<Phone> phones = jaikiranRoundtrip.getPhones(); log.info("Phones for " + jaikiranRoundtrip + ": " + phones); // Assert all's as expected final String assertionError = "Phones were not associated with the employee as expected"; Assert.assertEquals(assertionError, 2, phones.size()); Assert.assertTrue(assertionError, phones.contains(phone1)); Assert.assertTrue(assertionError, phones.contains(phone2)); // Clean up the associations so we can remove things jaikiranRoundtrip.getPhones().clear(); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of the 1:N Bidirectional Mapping Between * {@link Employee} and his/her reports {@link Employee}. Also * shows the Manager of an {@link Employee}. * @throws Throwable */ @Test public void oneToManyBidirectionalMapping() throws Throwable { // Create a few Employees final Employee alrubinger = new Employee("Andrew Lee Rubinger"); final Employee carloDeWolf = new Employee("Carlo de Wolf"); final Employee jaikiranPai = new Employee("Jaikiran Pai"); final Employee bigD = new Employee("Big D"); try { // Persist and associate final Long managerId = txWrapper.wrapInTx(new Callable<Long>() { @Override public Long call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(jaikiranPai); em.persist(alrubinger); em.persist(carloDeWolf); em.persist(bigD); // Associate *both* sides of the bidirectional relationship final Collection<Employee> peonsOfD = bigD.getPeons(); peonsOfD.add(alrubinger); peonsOfD.add(carloDeWolf); peonsOfD.add(jaikiranPai); alrubinger.setManager(bigD); carloDeWolf.setManager(bigD); jaikiranPai.setManager(bigD); // Return return bigD.getId(); } }); // Let the last Tx flush everything out, so lookup again // and perform assertions txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get the EM final EntityManager em = emHook.getEntityManager(); // Get the Employee/Manager final Employee managerRoundtrip = em.find(Employee.class, managerId); // Get the reports to the manager final Collection<Employee> peonsForManager = managerRoundtrip.getPeons(); log.info("Reports of " + managerRoundtrip + ": " + peonsForManager); // Assert all's as expected final String assertionMessage = "The Employee Manager/Reports relationship was not as expected"; Assert.assertEquals(assertionMessage, 3, peonsForManager.size()); Assert.assertTrue(assertionMessage, peonsForManager.contains(alrubinger)); Assert.assertTrue(assertionMessage, peonsForManager.contains(carloDeWolf)); Assert.assertTrue(assertionMessage, peonsForManager.contains(jaikiranPai)); Assert.assertEquals(assertionMessage, bigD, alrubinger.getManager()); Assert.assertEquals(assertionMessage, bigD, carloDeWolf.getManager()); Assert.assertEquals(assertionMessage, bigD, jaikiranPai.getManager()); // Clean up the associations so we can remove things for (final Employee peon : peonsForManager) { peon.setManager(null); } peonsForManager.clear(); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of the N:1 Unidirectional Mapping Between * {@link Customer} and his/her primary {@link Employee} contact. * @throws Throwable */ @Test public void manyToOneUnidirectionalMapping() throws Throwable { // Create an Employee final Employee bstansberry = new Employee("Brian Stansberry"); // Create a couple of Customers final Customer jgreene = new Customer("Jason T. Greene"); final Customer bobmcw = new Customer("Bob McWhirter"); try { // Persist and associate txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(bstansberry); em.persist(jgreene); em.persist(bobmcw); // Associate jgreene.setPrimaryContact(bstansberry); bobmcw.setPrimaryContact(bstansberry); // Return return null; } }); // Lookup and perform assertions txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Get the customers final Customer jgreeneRoundtrip = em.find(Customer.class, jgreene.getId()); final Customer bobmcwRoundtrip = em.find(Customer.class, bobmcw.getId()); // Ensure all's as expected final String assertionMessage = "Primary contact was not assigned as expected"; Assert.assertEquals(assertionMessage, bstansberry, jgreeneRoundtrip.getPrimaryContact()); Assert.assertEquals(assertionMessage, bstansberry, bobmcwRoundtrip.getPrimaryContact()); // Clean up the associations so we can remove things jgreeneRoundtrip.setPrimaryContact(null); bobmcwRoundtrip.setPrimaryContact(null); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of the N:N Unidirectional Mapping Between * {@link Customer} and his/her assigned {@link Task}s * @throws Throwable */ @Test public void manyToManyUnidirectionalMapping() throws Throwable { // Create a couple of employees final Employee smarlow = new Employee("Scott Marlow"); final Employee jpederse = new Employee("Jesper Pedersen"); // Create a couple of tasks final Task task1 = new Task("Go to the JBoss User's Group - Boston"); final Task task2 = new Task("Pick up flowers for Shelly McGowan"); try { // Persist and associate txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(smarlow); em.persist(jpederse); em.persist(task1); em.persist(task2); // Associate task1.getOwners().add(smarlow); task1.getOwners().add(jpederse); task2.getOwners().add(smarlow); task2.getOwners().add(jpederse); // Return return null; } }); // Lookup and perform assertions txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Get the tasks final Task task1Roundtrip = em.find(Task.class, task1.getId()); final Task task2Roundtrip = em.find(Task.class, task2.getId()); // Ensure all's as expected final String assertionMessage = "Task owners were not assigned as expected"; Assert.assertTrue(assertionMessage, task1Roundtrip.getOwners().contains(smarlow)); Assert.assertTrue(assertionMessage, task1Roundtrip.getOwners().contains(jpederse)); Assert.assertTrue(assertionMessage, task2Roundtrip.getOwners().contains(smarlow)); Assert.assertTrue(assertionMessage, task2Roundtrip.getOwners().contains(jpederse)); // Clean up the associations so we can remove things task1Roundtrip.getOwners().clear(); task2Roundtrip.getOwners().clear(); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Shows usage of the N:N Unidirectional Mapping Between * {@link Employee} and his/her team members. * @throws Throwable */ @Test public void manyToManyBidirectionalMapping() throws Throwable { // Create a few employees final Employee pmuir = new Employee("Pete Muir"); final Employee dallen = new Employee("Dan Allen"); final Employee aslak = new Employee("Aslak Knutsen"); // Create some teams final Team seam = new Team("Seam"); final Team arquillian = new Team("Arquillian"); try { // Persist and associate txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(pmuir); em.persist(dallen); em.persist(aslak); em.persist(seam); em.persist(arquillian); // Associate *both* directions seam.getMembers().add(dallen); seam.getMembers().add(pmuir); seam.getMembers().add(aslak); arquillian.getMembers().add(dallen); arquillian.getMembers().add(pmuir); arquillian.getMembers().add(aslak); aslak.getTeams().add(seam); aslak.getTeams().add(arquillian); dallen.getTeams().add(seam); dallen.getTeams().add(arquillian); pmuir.getTeams().add(seam); pmuir.getTeams().add(arquillian); // Return return null; } }); // Lookup and perform assertions txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Get the teams and employees back out as managed objects final Team seamRoundtrip = em.find(Team.class, seam.getId()); final Team arquillianRoundtrip = em.find(Team.class, arquillian.getId()); final Employee dallenRoundtrip = em.find(Employee.class, dallen.getId()); final Employee pmuirRoundtrip = em.find(Employee.class, pmuir.getId()); final Employee aslakRoundtrip = em.find(Employee.class, aslak.getId()); // Ensure all's as expected final String assertionMessage = "Team members were not assigned as expected"; Assert.assertTrue(assertionMessage, seamRoundtrip.getMembers().contains(pmuir)); Assert.assertTrue(assertionMessage, seamRoundtrip.getMembers().contains(aslak)); Assert.assertTrue(assertionMessage, seamRoundtrip.getMembers().contains(dallen)); Assert.assertTrue(assertionMessage, arquillianRoundtrip.getMembers().contains(pmuir)); Assert.assertTrue(assertionMessage, arquillianRoundtrip.getMembers().contains(aslak)); Assert.assertTrue(assertionMessage, arquillianRoundtrip.getMembers().contains(dallen)); Assert.assertTrue(assertionMessage, dallenRoundtrip.getTeams().contains(seamRoundtrip)); Assert.assertTrue(assertionMessage, dallenRoundtrip.getTeams().contains(arquillianRoundtrip)); Assert.assertTrue(assertionMessage, pmuirRoundtrip.getTeams().contains(seamRoundtrip)); Assert.assertTrue(assertionMessage, pmuirRoundtrip.getTeams().contains(arquillianRoundtrip)); Assert.assertTrue(assertionMessage, aslakRoundtrip.getTeams().contains(seamRoundtrip)); Assert.assertTrue(assertionMessage, aslakRoundtrip.getTeams().contains(arquillianRoundtrip)); // Clean up the associations so we can remove things aslakRoundtrip.getTeams().clear(); dallenRoundtrip.getTeams().clear(); pmuirRoundtrip.getTeams().clear(); seamRoundtrip.getMembers().clear(); arquillianRoundtrip.getMembers().clear(); // Return return null; } }); } catch (final TaskExecutionException tee) { // Unwrap throw tee.getCause(); } } /** * Ensures that JPA Entity Callbacks are received * @throws Exception */ @Test public void entityCallbacks() throws Exception { // Precondition checks final String preconditionMessage = "Test setup is in error"; Assert.assertFalse(preconditionMessage, EventTracker.postLoad); Assert.assertFalse(preconditionMessage, EventTracker.postPersist); Assert.assertFalse(preconditionMessage, EventTracker.postRemove); Assert.assertFalse(preconditionMessage, EventTracker.postUpdate); Assert.assertFalse(preconditionMessage, EventTracker.prePersist); Assert.assertFalse(preconditionMessage, EventTracker.preRemove); Assert.assertFalse(preconditionMessage, EventTracker.preUpdate); // Create a new employee final EntityListenerEmployee employee = new EntityListenerEmployee(); // Put through the full lifecycle txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(employee); // Update employee.setName("New Name"); em.flush(); // Lookup final EntityListenerEmployee employee2 = em.find(EntityListenerEmployee.class, employee.getId()); em.refresh(employee2); // Remove em.remove(employee); // Return return null; } }); // Assert events fired final String postconditionMessage = "Missing event fired"; Assert.assertTrue(postconditionMessage, EventTracker.postLoad); Assert.assertTrue(postconditionMessage, EventTracker.postPersist); Assert.assertTrue(postconditionMessage, EventTracker.postRemove); Assert.assertTrue(postconditionMessage, EventTracker.postUpdate); Assert.assertTrue(postconditionMessage, EventTracker.prePersist); Assert.assertTrue(postconditionMessage, EventTracker.preRemove); Assert.assertTrue(postconditionMessage, EventTracker.preUpdate); } /** * Ensures we may look up an entity by a JPA QL Query * @throws Exception */ @Test public void jpaQlFind() throws Exception { // Create an employee final SimpleEmployee employee = new SimpleEmployee(ID_DAVE, NAME_DAVE); // Persist, then lookup txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(employee); // Lookup final String jpaQlQuery = "FROM " + SimpleEmployee.class.getSimpleName() + " e WHERE e.name=?1"; final SimpleEmployee roundtrip = (SimpleEmployee) em.createQuery(jpaQlQuery).setParameter(1, NAME_DAVE) .getSingleResult(); // Test obtained as expected Assert.assertEquals("Employee from JPA QL Query should equal the record added", employee, roundtrip); // Return return null; } }); } /** * Ensures we may look up an entity by a Criteria API Query * @throws Exception */ @Test public void criertiaAPIFind() throws Exception { // Create an employee final SimpleEmployee employee = new SimpleEmployee(ID_DAVE, NAME_DAVE); // Persist, then lookup txWrapper.wrapInTx(new Callable<Void>() { @Override public Void call() throws Exception { // Get EM final EntityManager em = emHook.getEntityManager(); // Persist em.persist(employee); // Lookup final CriteriaBuilder builder = em.getCriteriaBuilder(); final CriteriaQuery<SimpleEmployee> query = builder.createQuery(SimpleEmployee.class); Root<SimpleEmployee> root = query.from(SimpleEmployee.class); query.select(root).where(builder.equal(root.get("name"), NAME_DAVE)); final SimpleEmployee roundtrip = (SimpleEmployee) em.createQuery(query).getSingleResult(); // Test obtained as expected Assert.assertEquals("Employee from Criteria API Query should equal the record added", employee, roundtrip); // Return return null; } }); } //-------------------------------------------------------------------------------------|| // Internal Helper Methods -----------------------------------------------------------|| //-------------------------------------------------------------------------------------|| /** * Issues a JPA QL Update to remove all entities of the specified type * @param type * @param em */ private void deleteAllEntitiesOfType(final Class<?> type, final EntityManager em) { assert em != null : EntityManager.class.getSimpleName() + " must be specified"; assert type != null : "type to be removed must be specified"; // JPA QL String to remove all of the specified type log.info("Removed: " + em.createQuery("DELETE FROM " + type.getSimpleName() + " o").executeUpdate() + " entities of type " + type); } }