Enterprise Java Development@TOPIC@

Chapter 55. Server-side Persistence Unit

55.1. EJB Persistence Unit
55.2. Imported EJB - WAR Deployment
55.3. Embedded EJB - WAR Deployment
55.4. Summary

In this chapter we will form a persistence unit to be deployed to the server-side. The data tier has been implemented for you and is located in the "impl" module. We will use the persistence unit from that module as a starting point.

Warning

This exercise relies on an EJB to reset the DB in between IT tests. Something may not be getting closed out because after ~10 re-deploys of the application, the re-deploy is stalled by a failure to get a DB connection. When this occurred, the only way I could resolve was to kill the application server (kill -9 on Linux). It did not occur often -- but enough to place this warning and work-around here.

In this section we will deploy a persistence unit within a EAR-based EJB deployment. The exercise starts off with a missing persistence unit in the EJB, an injection of the persistence context into the EJBs commented out, and the test of the EJB @Ignored.

  1. Remove the @Ignore from the venueEAR() @Test in VenueMgmtIT.java. This will activate the test and request the server-side to create a Venue.


  2. Attempt to build the application from the parent pom.


    There is a NullPointerException on the server-side because the @PersistenceContext is not being injected and the EntityManager passed to the DAO is null. Please fix this in the next step.

  3. Inject a persistence context (i.e., EntityManager) for the "jpatickets-labex" persistence unit for VenueMgmtEJB.

    There are two types of JPA injections that can be done in JavaEE; @PersistenceUnit and @PersistenceContext. The @PersistenceUnit defines an injection of an EntityManagerFactory and is usually only done in EJBs using BEAN-managed transactions. The TicketsInitTxEJB uses that construct. @PersistenceContext defines the injection of an EntityManager. Both express the persistence unit name with the "unitName" attribute. The "name" attribute is used for JNDI ENC injection. We will be defining our injections direct without going through the indirection of the ENC.

    Since VenueMgmtEJB and EventMgmtEJB are both @Stateless EJBs, the persistence contexts are defined to be transaction-scoped. That means there will be a single transaction for each sequence of interactions with the persistence context. You have no option to commit and begin a new transaction when it is transaction-scoped. To do otherwise you would need either a @Stateful or @Singleton EJB.


  4. Attempt to build and deploy application from parent pom. This will fail since we do not yet have the persistence unit defined. We have a persistence.xml file with "a" persistence unit defined, but it is not the one VenueMgmtEJB is trying to use.


  5. Open the existing META-INF/persistence.xml in the EJB. When you are done this file will have two (2) persistence units defined. The second one is already defined and is used by TicketsInitTxEJB to reset the database in between IT tests. The first one can be built using parts of the "impl" module (I am choosing to ignore what the schema-gen persistence unit can provide you and focusing on what we can/can't carry forward from the unit test)


  6. Open the existing META-INF/persistence.xml in the impl module src/test/resource directory. We will use that as a starting point and discuss the differences. Note that we placed the unit test's persistence.xml within the src/test tree so that it would be available for unit testing but would not get placed in the JAR and deployed to the server.

    Figure 55.6. 

    jpatickets-labex-impl/src/
    `-- test
        `-- resources
            `-- META-INF
                `-- persistence.xml
    
    <persistence-unit name="jpatickets-test">
        <!-- used on the server-side 
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        -->
        <!-- jarfile shortcut can be used when deploying EJB within EAR 
        <jar-file>lib/jpatickets-labsol-impl-${project.version}.jar</jar-file>
        -->

        <!-- classes must be enumerated when deploying outside of EAR -->
        <class>org.myorg.jpatickets.bo.Venue</class>
        <class>org.myorg.jpatickets.bo.Address</class>
        <class>org.myorg.jpatickets.bo.Seat</class>
        <class>org.myorg.jpatickets.bo.Event</class>
        <class>org.myorg.jpatickets.bo.Ticket</class>
        
        <properties>
            <!-- this applies to both unit-test and server-side environments -->
            <property name="hibernate.dialect" value="${hibernate.dialect}"/>

            <!-- these can be helpful in both environments when debugging
            <property name="hibernate.jdbc.batch_size" value="0"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
             -->            
            
            <!-- this applies to only the ***demo***-nature of this example -->
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
            
            <!-- these apply only to the unit test environment -->
            <property name="javax.persistence.jdbc.driver" value="${jdbc.driver}"/>
            <property name="javax.persistence.jdbc.url" value="${jdbc.url}"/>
            <property name="javax.persistence.jdbc.user" value="${jdbc.user}"/>
            <property name="javax.persistence.jdbc.password" value="${jdbc.password}"/>
        </properties>
    </persistence-unit>

  7. Create the outer element of the persistence unit in the EJB. Note that we have picked a different name than the persistence unit used during the unit tests. This helps keep things straight if they ever were deployed to the server.


  8. Add a jta-data-source for obtaining connections. The jta-data-source references a JNDI name for a javax.sql.DataSource defined at the application server. The components will borrow physical connections from the DataSource during transactions, form lightweight logical connections, close them complete, which frees of the physical connection for other clients.

    The jta-data-source takes the place of the "javax.persistence.jdbc" connection properties. These properties are only necessary when creating direct connections to the database and the server will have already done this for us.


    The JNDI name from the server's configuration (standalone.xml) must match the JNDI name of the jta-data-source element of the peristence unit.


  9. Attempt the build, deploy, and test the application with the persistence unit defined with only a jta-data-source. The build will fail because of an unknown entity.


    The entity is unknown because the entity class is part of a separate JAR file and must be brought into the persistence unit using one of several techniques.

  10. Since the EJB is being deployed to the server using an EAR - lets take advantage of a shorthand way of referring to every @Entity in the impl.jar. Notice the impl.jar is in the lib directory of the EAR so we can reference it there. We can also take advantage of Maven filtering to take care of the version#.


    Explicitly name Entity classes if still unknown

    While doing a quick dry-run of this exercise, I noticed that I could not get this step to work as planned and am not currently sure why broken. If this is not working for you -- please update to the following. There will be a later step that asks you to that exact edit for a separate reason when using WARs.

    
    <persistence-unit name="jpatickets-labex">
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        <!-- 
        <jar-file>lib/jpatickets-labex-impl-${project.version}.jar</jar-file>
         -->
        <class>org.myorg.jpatickets.bo.Venue</class>
        <class>org.myorg.jpatickets.bo.Address</class>
        <class>org.myorg.jpatickets.bo.Seat</class>
        <class>org.myorg.jpatickets.bo.Event</class>
        <class>org.myorg.jpatickets.bo.Ticket</class>
    </persistence-unit>
  11. Add some debug output from the persistence unit.


  12. Build, deploy, and test the application by building from the parent pom. This should be successful. If you look in the server.log you should see the show_sql we enabled above creating and getting a venue.


    Notice with the @Stateless EJB, we get an init/destroy pair for every business method call because we have not placed this EJB in a pool. Notice too that the destroy for the createVenue() is printed prior to the database INSERTs. That is because the persistence context was injected by the container and the end-of-transaction flush() did not occur until after the business method exited. The init/destroy pair for the getVenue() occur around the SELECT because queries are performed immediately and do not wait until the end of a transaction (if one is active).

You have completed this section of the exercise. You now have an initial persistence unit defined and deployed to the server. The persistence unit is injected into the EJB and has required entities registered. The EJB is able to expose business methods that use the persistence unit to create and get a Venue.

In this section we will deploy the EJB from the previous section using a WAR instead of an EAR. We will do this to demonstrate that the jar-file shortcut only works for EAR-based deployments.

  1. Activate the venueImportedEJB() @Test within VenueMgmtIT.java of the test module.


  2. Attempt to build the application from the root. Notice the EAR-based EJB test still passes but the imported WAR-based cannot resolve the entities (like before).


    The jar-file element unfortunately will not resolve a JAR file within anything besides an EAR - even if we adjusted the path to match the WAR locations. We will have to use one of the other techniques to register the entity classes with the persistence unit that is more portable.

  3. Register the entity classes using class elements. This is more verbose but will pull the entities from the classpath formed by either the EAR or WAR and we will not have to worry about the location of the impl.jar containing the @Entities. Remove the jar-file reference


  4. Build, Deploy, and Re-run the IT test for the WAR-based imported EJB. This should now complete successfully.


You have completed two of the three deployment alternatives. The first and second were actually the same EJB artifact. One was deployed as an EAR-based EJB. The other deployed as a WAR-based EJB. We learned that jar-file references only work in EAR-based deployments but naming each entity using the class element works in both environments.

In this section we will make a simple copy of the VenueMgmtEJB and implement it as an EJB embedded within the WAR. That means it will not be a part of any EJB.jar, will not be in WEB-INF/lib, but will be located in WEB-INF/classes.

  1. Activate the venueWAR() @Test within the VenueMgmtIT.java IT test in the test module.


  2. Attempt the build the application from the parent pom. This should fail for a familiar error -- Unknown entity.


    Note in this case we have an entirely new persistence.xml. It is embedded within the WAR and deployed to WEB-INF/classes/META-INF. The source location within the WAR module is src/main/resources.


    We are currently missing all of the internals of this persistence unit.

  3. Add jta-data-source and entity class references.


  4. Build, deploy, and test the WAR-based embedded persistence unit. This should now pass and you should notice that three (3) IT tests were actually executed and the rest are still ignored.


You have finished the third of three(3) different deployment types for a persistence unit. In this last option you deployed a persistence unit for the explicit use by beans embedded within the WAR. Note the beans in the WAR also had access to the persistence unit within the imported EJB if they wanted to share a persistence context at runtime (quite likely).

In this exercise you deployed a persistence unit within a EAR-deployed and WAR-deployed EJB and embedded a persistence unit within a WAR for use by embedded EJBs and other beans. From this point you should be able to begin layering your implementation around the injected @PersistenceContext/EntityManager and addressing issues that are of concern to the remote access to the EJB.