Lab 9: JPA Relationships

In this lab, you will write a program that uses Java Persistence to persist a business object model that features relationships.

Objectives:

Part 1: Creating the Entities

In this part, you will create the entities to represent a business model. The model in question is for an organization that maintains memberships. Each of the several membership lists keeps track of associates, each of which pays dues. A given associate can be part of more than one list. The UML class diagram for your business model is shown in Figure 1:

Steps:

  1. As in earlier labs, create a Dynamic Web project named Lab09Web.

  2. As before, add JPA 2.0 to the project's facets.

  3. Next, you need to edit the persistence.xml file that configures Java Persistence :

    1. In the Project Explorer, expand the META-INF folder and double-click on persistence.xml to open it into the editor.

    2. At the bottom of the editor, click the General tab, then in the General section at the top, change the Name to lab09.

    3. At the bottom of the editor, click the Connections tab. In the General section, for the Transaction Type, select JTA.

      Then for the JTA Data Source Name, enter ExampleDS.

    4. Click the Properties tab at the bottom of the editor window.

      Use the Add button to define the following properties:

      Name                                 
      ----                                 
      openjpa.jdbc.SynchronizeMappings     
      
      Value
      ------
      buildSchema(foreignKeys=true,schemaAction='dropDB,add')                                                                            
      
      
      Name                                 
      ----                                 
      openjpa.jdbc.SchemaFactory     
      
      Value
      ------
      native(foreignKeys=true)                                                                            
      
      
      Name                                 
      ----                                 
      openjpa.jdbc.MappingDefaults     
      
      Value
      ------
      ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict                                                                            
                     

    5. Click the Source tab at the bottom of the window and examine the raw XML and ensure that you understand it.

      Be sure to save the file before continuing.

  4. As in earlier labs, use the New JPA Entity wizard to create the three entity classes in the part1 package, MembershipList, Associate and Dues. As you create them, configure the properties described in Figure 1:

    MembershipList
    --------------
    
    listID        int                       -- key field
    listName      java.lang.String
    
    Associate
    ---------
    
    associateID   int                       -- key field
    name          java.lang.String
    emailAddress  java.lang.String
    
    Dues
    ----
    
    duesID        int                      -- key field
    paymentDate   java.util.Date
    amount        double
               

  5. Edit MembershipList.java and make the following changes and additions:

    1. Annotate the listID field so that it uses an auto-generated value with the GenerationType.AUTO strategy.

    2. As you can see in Figure 1, MembershipList has a ManyToMany bidirectional relationship with Associate. To implement this, MembershipList needs to maintain a collection of Associates. Define the following annotated field:

      @ManyToMany
      private Collection<Associate> associateList =
          new ArrayList<Associate>();
                     
                     

      Use Source - Organize Imports to import java.util.List.

      Note that since we did not use the mappedBy element, MembershipList will be the owner side of the relationship.

    3. Use Source - Generate Getters and Setters to create get/set methods for the associateList.

    4. Write a business logic method that calculates the total dues paid by all associates:

      public double calculateTotalDues()
      {
          double total = 0;
       
          for (Associate a : associateList)
          {
              total += a.calculateTotalDues();
          }
          
          return total;
      }
                     

      Note that you will have an error on the calculateTotalDues call since you haven't written it yet. You will fix this in a moment.

  6. Edit Associate.java and make the following changes and additions:

    1. As you did for MembershipList, annotate the key field in Associate so it uses an auto-generated value.

    2. Since each Associate can be a member of multiple lists, Associate needs to maintain a collection of MembershipLists. Define the following annotated field:

      @ManyToMany(mappedBy="associateList")
      private Collection<MembershipList> membershipList =
          new ArrayList<MembershipList>(); 
                     

      Use Source - Organize Imports to import java.util.List.

      Note that since we used the mappedBy element, Associate will be the inverse (non-owning) side of the relationship.

    3. Use Source - Generate Getters and Setters to create get/set methods for the membershipList.

    4. Associate also has a unidirectional OneToMany relationship with Dues. Using similar code to what you just did, define a field named duesList to implement that relationship, annotating it as required.

      Note since this a unidirectional relationship, Associate is by definition the owner, and thus you don't need a mappedBy element.

      Generate get/set methods for the duesList field.

    5. Write a business logic method that calculates the total dues payments by this associate:

      public double calculateTotalDues()
      {
          double total = 0;
       
          for (Dues d : duesList)
          {
              total += d.getAmount();
          }
          
          return total;
      }
                     

  7. The Dues entity doesn't have any relationships with the other entities, but you still need to work on it a little bit. Edit Dues.java and make the following changes and additions:

    1. As you did for the other entities, annotate the primary key field so it uses an auto-generated value.

    2. Annotate the paymentDate field so that JPA stores a date (not a time) in the database:

       @Temporal(TemporalType.DATE)               
                     

Part 2: Stateless Session EJB

In this part, you will create a stateless session EJB that manipulates the business model entities.

Steps:

  1. As in earlier labs, create a stateless session EJB according to these specifications:

    Java package name:     session
    Class name:            MembershipSessionBean
    No-interview View:     unchecked
    Local interface name:  session.ManageMembership
             

  2. Complete the session/ManageMembership.java interface by writing the following method definitions:

    public MembershipList createMembershipList(String listName);
    public Associate createAssociate(String name, String email,
            int listID);
    public Dues createDues(double amount, int assocID);
    public double calculateTotalDues();
                              
             

  3. Complete the session EJB, MembershipSessionBean.java:

    1. Use dependency injection to obtain a reference to the persistence manager:

      @PersistenceContext(unitName="lab09")
      private EntityManager entityManager;
                   

      Recall that you wrote a persistence.xml file in the project that configured the persistence unit name to be lab09.

    2. Use Source - Override/Implement Methods menu so that Eclipse generates empty implementation methods for the interface methods.

    3. Complete the createMembershipList method:

      • Create a MembershipList entity and call setListName.
      • Use the EntityManager to persist the entity.
      • When you persist the entity, the entity manager will assign the auto-generated key. Modify the return statement to return the entity's reference.
    4. Complete the createAssociate method:

      • Create an Associate entity and call setName and setEmail address.
      • Add the new associate to the specified membership list:

        Query q = entityManager.createQuery(
            "SELECT l FROM MembershipList l WHERE l.listID = :listID");                 
        q.setParameter("listID", listID);
        
        MembershipList list = (MembershipList)q.getSingleResult();
        list.getAssociateList().add(assoc);
                         

      • Add the membership list parameter to the associate's list of membership lists:

        assoc.getMembershipList().add(list);                 
                         

      • Use the EntityManager to persist the entity.
      • When you persist the entity, the entity manager will assign the auto-generated key. Modify the return statement to return the entity's reference.
    5. Complete the createDues method:

      • Create a Dues entity and initialize it by calling the setAmount and setPaymentDate methods (for the date, create a new java.util.Date object).
      • Add the new Dues entity object to the specified associate:

        Query q = entityManager.createQuery(
            "SELECT a FROM Associate a WHERE a.associateID = :associateID");                 
        q.setParameter("associateID", assocID);
        
        Associate assoc = (Associate)q.getSingleResult();
        assoc.getDuesList().add(d);
                         

      • Use the EntityManager to persist the entity.
      • When you persist the entity, the entity manager will assign the auto-generated key. Modify the return statement to return the entity's reference.
    6. Complete the calculateTotalDues method by querying all MembershipLists, then looping through and accumlating the total:

      public double calculateTotalDues()
      {
          double total = 0;
          Query q = entityManager.createQuery(
              "SELECT m FROM MembershipList m");                 
      
          List<MembershipList> lists = q.getResultList();
          for (MembershipList list : lists)
          {
              total += list.calculateTotalDues();
          }
          
          return total;
      }
                     
                   

Part 3: Writing a Servlet

In this part, you will write a servlet that accesses the stateless session EJB.

Steps:

  1. As in earlier labs, right-click on the Web project's Java Resources: src folder and create a new servlet named MembershipServlet in the servlets package. The new servlet should override only doGet.

  2. Complete the servlet:

    1. Define a private field of type ManageMembership (the EJB interface) named memberEJB and use dependency injection to initialize it.

    2. In the doGet method, set the content type and retrieve a PrintWriter:

      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
                  

    3. Use session EJB reference to create two membership lists. Your code might look like:

      MembershipList list1 = memberEJB.createMembershipList("List 1");
      MembershipList list2 = memberEJB.createMembershipList("List 2");            
                  

    4. Create two associate entities. Your code might look like:

      Associate assoc1 = memberEJB.createAssociate("Sue", "sue@sue.com", 
              list1.getListID());        
      Associate assoc2 = memberEJB.createAssociate("Bill", "bill@bill.com", 
              list2.getListID());
                  

    5. Create four dues entities, associating them with the two associates:

      Dues dues1 = memberEJB.createDues(10, assoc1.getAssociateID());
      Dues dues2 = memberEJB.createDues(15, assoc1.getAssociateID());
      Dues dues3 = memberEJB.createDues(25, assoc2.getAssociateID());
      Dues dues4 = memberEJB.createDues(30, assoc2.getAssociateID());
                  

    6. Display the total dues to the Web page:

      out.println("Total dues: " + 
              memberEJB.calculateTotalDues());
                  

  3. In the Project Explorer, right-click on the servlet and choose Run As - Run on Server to run the servlet. You should see the total dues displayed in the resulting Web page.

    Note: If you find that you made a mistake with the entity, you should stop the server and restart and test again. Since we are using a top-down approach, this will cause the server to drop and recreate the database tables.

  4. Using similar techniques as at the end of the last lab, use the H2 admin console to examine the database tables and ensure that you understand them.

  5. Remove the project from the server to undeploy it.