Saturday, January 5, 2013

Hibernate Many to Many with Additional Columns

So we had a slight hiccup in our plans to dynamically place products according to their location points on a screen. We had to associate each view with a set of products and each product, a location, easy. But here's the fun part, products could be part of many views and views could be part of many products. This meant we had to accompany location data along with each product that belongs to a view. Which boils down to adding additional columns to a M-M association.

I did, I guess what anyone would do in a situation quite like this, break the problem down in to manageable parts.  First map the association to an entity class, use a composite primary key for the new entity (you don't have to do this, you can go about it the usual way i.e having two individual entities), add the additional columns to the new entity and finally decorate the classes involved with annotations. 

Ok let's get down to business then. Here are my two entity classes, 
@Entity()
@Table(name = "assortment")
public class Assortment {
 private Long id;
 private String imgURL;
 private Set<AssortmentProduct> products = new HashSet<AssortmentProduct>(0);
       @OneToMany(fetch = FetchType.LAZY, mappedBy = "assortmentProductId.assortment", cascade = {
   CascadeType.PERSIST, CascadeType.MERGE }, orphanRemoval = true)
 @Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE })
 public Set<AssortmentProduct> getProducts() {
  return products;
 }
 // Other Fields, accessors and mutators

And as for the Product entity,
@Entity
@Table(name = "product")
public class Product implements java.io.Serializable {
 private static final long serialVersionUID = 1L;
 private int id;
 private Set<AssortmentProduct> assortmentProducts = new HashSet<AssortmentProduct>(
   0);
@OneToMany(fetch=FetchType.LAZY,mappedBy="assortmentProductId.product")
 public Set<AssortmentProduct> getAssortmentProducts() {
  return assortmentProducts;
 }
 // Other Fields, accessors and mutators


Here is my composite primary key class,
@Embeddable
public class AssortmentProductId implements Serializable {
 private static final long serialVersionUID = -3712895453750701217L;
 private Assortment assortment;
 private Product product;
        /**
  * other stuff 
  */

Make sure you override the equals and hashcode methods of your composite primary key class as its important to do so.
And now the association entity,

import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

@Entity
@Table(name = "assortment_product")
@AssociationOverrides({
  @AssociationOverride(name = "assortmentProductId.product", joinColumns = { @JoinColumn(name = "product_id") }),
  @AssociationOverride(name = "assortmentProductId.assortment", joinColumns = { @JoinColumn(name = "assortment_id") }) })
public class AssortmentProduct {

 private AssortmentProductId assortmentProductId = new AssortmentProductId();
 private PlaceHolder placeHolder;

 @EmbeddedId
 public AssortmentProductId getAssortmentProductId() {
  return assortmentProductId;
 }

 public void setAssortmentProductId(AssortmentProductId assortmentProductId) {
  this.assortmentProductId = assortmentProductId;
 }

 @Transient
 public Assortment getAssortment() {
  return assortmentProductId.getAssortment();
 }

 @Transient
 public Product getProduct() {
  return assortmentProductId.getProduct();
 }

 @OneToOne
 public PlaceHolder getPlaceHolder() {
  return placeHolder;
 }
}
And that's it. 

No comments:

Post a Comment