Understanding JPA, Part 2: Relationships the JPA way

Handle data relationships with object-oriented grace

1 2 Page 2
Page 2 of 2

Many-to-many relationships

There is one last leg of relationship mapping left to consider. An order can consist of one or more products, whereas a product can be associated with zero or more orders. This is a many-to-many relationship, as illustrated in Figure 4.

Diagram of a many-to-many relationship.
Figure 4. A many-to-many relationship (click to enlarge)

To define a many-to-many relationship, the sample application will introduce a join table named ORDER_DETAIL that holds the association between the Order and Product tables, as shown in Listing 10.

Listing 10. A sample entity illustrating a many-to-many relationship

@Entity(name = "ORDERS") 
public class Order {
       @Id 
       @Column(name = "ORDER_ID", nullable = false)
       @GeneratedValue(strategy = GenerationType.AUTO)
       private long orderId;
       
       @Column(name = "CUST_ID")
       private long custId;
       
       @Column(name = "TOTAL_PRICE", precision = 2)
       private double totPrice;
       
       @OneToOne(optional=false,cascade=CascadeType.ALL, mappedBy="order",
       targetEntity=Invoice.class)
       private Invoice invoice;
       
       @ManyToOne(optional=false)
       @JoinColumn(name="CUST_ID",referencedColumnName="CUST_ID")
       private Customer customer;
       
       @ManyToMany(fetch=FetchType.EAGER)
       @JoinTable(name="ORDER_DETAIL",
               joinColumns=
               @JoinColumn(name="ORDER_ID", referencedColumnName="ORDER_ID"),
         inverseJoinColumns=
               @JoinColumn(name="PROD_ID", referencedColumnName="PROD_ID")
       )
       private List<Product> productList;       
       ...............
       The other attributes and getters and setters goes here

}

The @JoinTable annotation is used to specify a table in the database that will associate order IDs with product IDs. The entity that specifies the @JoinTable is the owner of the relationship; in this case, the Order entity is the owner of the relationship with the Product entity.

Listing 11 demonstrates how to fetch the product details for a particular Order.

Listing 11. Fetching objects involved in a many-to-many relationship

..........
EntityManagerFactory entityManagerFactory = 
       Persistence.createEntityManagerFactory("testjpa");
EntityManager em = entityManagerFactory.createEntityManager();
Order order = em.find(Order.class, 111);
System.out.println("Product  : " + order.getProductList());
em.close();
entityManagerFactory.close();
..........

Bidirectional many-to-many relationships

One product can be included in multiple orders. Once you have mapped the owning side, the inverse side becomes very easy to map, as you can see in Listing 12.

Listing 12. A sample entity illustrating a bidirectional many-to-many relationship

@Entity(name = "PRODUCT") 
public class Product {
       @Id
       @Column(name = "PROD_ID", nullable = false)
       @GeneratedValue(strategy = GenerationType.AUTO)
       private long prodId;
       
       @Column(name = "PROD_NAME", nullable = false,length = 50)
       private String prodName;
       
       @Column(name = "PROD_DESC", length = 200)
       private String prodDescription;
       
       @Column(name = "REGULAR_PRICE", precision = 2)
       private String price;
       
       @Column(name = "LAST_UPDATED_TIME")
       private Date updatedTime;
       @ManyToMany(mappedBy="productList",fetch=FetchType.EAGER)
       private List<Order> orderList;       
       ...............
       The other attributes and getters and setters goes here
}

Listing 13 illustrates how to fetch all the orders for a particular product.

Listing 13. Fetching objects involved in a bidirectional many-to-many relationship

..........
EntityManagerFactory entityManagerFactory = 
       Persistence.createEntityManagerFactory("testjpa");
EntityManager em = entityManagerFactory.createEntityManager();
Product product = em.find(Product.class, 2000);
System.out.println("Order details for product   : " + product.getOrderList());
em.close();
entityManagerFactory.close();
..........

The final show

Now, cast your mind back to the example application that started this tutorial. The sample company, XYZ, wants to find out the following:

  • The number of products each customer has
  • The products cancelled by each customer

Now that the relationships have been defined, this can be very easily achieved with just a few lines of code, as shown in Listing 14.

Listing 14. Putting all the relationships together

..............
Query query = em.createQuery("SELECT customer FROM CUSTOMER customer");
List list= query.getResultList();

for(Customer customer:list){
       List prodList = new ArrayList();
       List prodListCancelled = new ArrayList();
       if(customer.getOrders()!=null){
               for(Order allOrders: customer.getOrders()){
                       if(allOrders.getInvoice().getOrderCancelledDt() == null){
                       //Find out how many products each customer has
                       prodList.addAll(allOrders.getProductList());
                       }else{
                       //Find out how many products cancelled by each customer
                       prodListCancelled.addAll(allOrders.getProductList());
                       }
               }
       }
}
..............

The code in Listing 14 retrieves all customers from the CUSTOMER table. For each customer, it retrieves the number of products ordered, filters the uncanceled orders, and adds them to prodList. It also retrieves the number of products for each cancelled order for each customer, and puts that number into prodListCancelled.

Thus, prodList contains products used by a particular customer, and prodListCancelled contains products cancelled by a particular customer. And with that information in hand, XYZ can easily solicit the customers it's most interested in for its customer satisfaction surveys.

Untangling data relationships

This article's sample application was designed to demonstrate a variety of types of data relationships. In this article, you have seen how complex CRUD operations involving multiple relational tables can be handled in an object- oriented fashion using the Java Persistence API. This can drastically reduce the complexity of enterprise application queries and maintenance overhead.

JPA has certainly simplified data persistence by providing a standard API usable in both Java SE and EE environments. Hopefully the sample application shown here will give you ideas on how JPA can help you in your own projects.

Aditi Das is a technical architect with Infosys Technologies and has seven years of specialized experience in Java and JEE. She is a Sun-certified enterprise architect (SCEA), Web component developer (SCWCD), business component developer (SCBCD), and Web service developer (SCDJWS). She is very much inspired by the Head First philosophy of learning new technologies, and hopes that someday a book will come out on the past, present, and future of SOA.

Learn more about this topic

1 2 Page 2
Page 2 of 2