Archive for the ‘Hibernate’ Category

Hibernate Annotations for a One-To-Many Mapping Featuring a Many Side, Composite, Primary Key having a Composite, Foreign, Sub-Key

Tuesday, October 14th, 2008

Sure, that’s a honking title, but it is what’s needed to describe a Hibernate mapping that recently required some head scratching on my part to figure out. I’ve been off of Hibernate (as in off of the wagon) for a while now as I’ve been busy writing stored procedures. Now that I’ve fallen back on the wagon, and am working on a Spring/Hibernate project, I’ve started to recall what a joy/pain Hibernate can be.

This project has a legacy database so I had to map the database objects as is. The relationship is between a parent and child table (or master/detail set of tables), which I’m going to refer to as Parent and Child, respectively:

Parent and Child Tables

As the title of this post indicates Parent has a composite primary key. The interesting, and tricky to handle in Hibernate item is the primary key of the child table. This is also a composite key, and a sub-key of the Child primary key is a foreign key to Parent. The DDL for this relationship gives an exact picture of the relationship:

create table parent (
  id      integer,
  version integer,
  constraint parent_pk primary key (id, version)
);

create table child (
  id      integer,
  parent_id integer,
  parent_version integer,
  constraint child_pk
    primary key (id, parent_id, parent_version),
  constraint child_fk
    foreign key (parent_id, parent_version)
    references parent (id, version)
);

Two fields from Child (parent_id and parent_version ) point back to the owning Parent (id, version). There are a couple of aspects that make this harder to model from the Hibernate perspective. All of the keys involved are composite, so you have to use embedded objects to represent each key.

Secondly, there is no separation between the link to the parent and the existence of a child instance. Hibernate is most easy to deal with when these are two different items. That is why you see recommendations 1) against unidirectional OneToMany relationships with a foreign key on the owned entity, and 2) and that you use a join table in these cases. The join table lets you separate the existence of a child (a record in the child table) from the existence of the relationship to parent (a record in the join table). In this case, since the foreign key (the link) is part of the primary key (existence), these two concept are not separated and we need to just deal with it.

Against the above recommendation, one has to go with a OneToMany with no join table. I didn’t invent how to model this, the canonical posting for relationships with composite keys are here and here. However, those posting don’t deal with the combined primary and foreign key and when both keys are composite so there is some extra detail here. I’ve packaged the example up as an Eclipse project and the complete source code is available here.

Because the keys are composite, the Java object model has embedded objects that represent the primary key values, as shown in following class diagram:

Parent and Child Class Diagram

The main domain objects are named Parent and Child, which would also hold all of the additional attributes in a real application (I’m showing only key values here for clarity). The primary key objects are ParentPrimaryKey and ChildPrimaryKey. An important point to note here is the representation of the foreign key in ChildPrimaryKey. ChildPrimaryKey holds a reference to the owning Parent as an instance variable (named parentForeignKey).

Moving on to the annotations and the coding tasks that need to be applied to each of classes, we start with Parent:

package com.beavercreekconsulting.example;

import java.util.List;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "parent")
public class Parent {

  @Id
  ParentPrimaryKey primaryKey = new ParentPrimaryKey();

  @OneToMany(mappedBy = "primaryKey.parentForeignKey",
    fetch=FetchType.EAGER)
  private List children;

  public ParentPrimaryKey getPrimaryKey() {
    return primaryKey;
  }

  public void setPrimaryKey(ParentPrimaryKey primaryKey) {
    this.primaryKey = primaryKey;
  }

  public List getChildren() {
    return children;
  }

  public void setChildren(List children) {
    this.children = children;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result
      + ((children == null) ? 0 : children.hashCode());
    result = prime * result
      + ((primaryKey == null) ? 0 : primaryKey.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    final Parent other = (Parent) obj;
    if (children == null) {
        if (other.children != null) return false;
    } else if (!children.equals(other.children)) return false;
    if (primaryKey == null) {
      if (other.primaryKey != null) return false;
    } else if (!primaryKey.equals(other.primaryKey)) return false;
    return true;
  }
}

The Entity and Table annotations are straightforward JPA annotations. The Id annotation on the primaryKey instance variable indicates that an embedded object will be used for the primary key.

The @OneToMany annotation with the mappedBy element indicates a bidirectional relationship with the Many side as the owner.

Annotation of the ParentPrimaryKey is straightforward:

package com.beavercreekconsulting.example;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class ParentPrimaryKey implements Serializable {

  private static final long serialVersionUID = -2912693560354598053L;

  @Column(name = "id")
  Integer id;

  @Column(name = "version")
  Integer version;

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public Integer getVersion() {
    return version;
  }

  public void setVersion(Integer version) {
    this.version = version;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((id == null) ? 0 : id.hashCode());
    result = prime * result + ((version == null) ? 0 : version.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    final ParentPrimaryKey other = (ParentPrimaryKey) obj;
    if (id == null) {
      if (other.id != null) return false;
    } else if (!id.equals(other.id)) return false;
    if (version == null) {
      if (other.version != null) return false;
    } else if (!version.equals(other.version)) return false;
    return true;
  }
}

The embeddable class needs to implement Serializable, so I added Eclipse generated serialVersionUID, hashCode() and equals().

The ChildPrimaryKey class is pretty similar to ParentPrimaryKey:

package com.beavercreekconsulting.example;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;

@Embeddable
public class ChildPrimaryKey implements Serializable {

  private static final long serialVersionUID = -452758257162645576L;

  @Column(name="id")
  Integer id;

  @ManyToOne
  Parent parentForeignKey;

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public Parent getParentForeignKey() {
    return parentForeignKey;
  }

  public void setParentForeignKey(Parent parentForeignKey) {
    this.parentForeignKey = parentForeignKey;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((id == null) ? 0 : id.hashCode());
    result = prime * result
      + ((parentForeignKey == null) ? 0 : parentForeignKey.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    final ChildPrimaryKey other = (ChildPrimaryKey) obj;
    if (id == null) {
      if (other.id != null)
        return false;
    } else if (!id.equals(other.id))
      return false;
    if (parentForeignKey == null) {
      if (other.parentForeignKey != null)
        return false;
    } else if (!parentForeignKey.equals(other.parentForeignKey))
      return false;
    return true;
  }
}

The main annotations are @Column on the id field and @OneToMany on the parentForeignKey field. This class also needs to implement java.io.Serializable, so I added serialVersionUID, hashCode() and equals(). Eclipse pointed out that the hashCode() method might not work correctly due to it’s dependence on parentForeignKey unless Parent also implements hashCode(), so I added that to Parent.

The final class, Child has the most complicated annotations. The goal of the annotations is to point the persistence layer to the embedded primary key object for the purposes of object identification. Here is a quick check list of the annotation that need to be applied:

  1. The instance variable that points to the primary key object should be annotated with @EmbeddedId.
  2. Supply getters and setters for for primary key fields that are annotated with @Transient and delegate the setting and getting to the embedded primary key class.
  3. Use @AssociationOverrides to point the values of the embedded class. The name of the association to override has the form of <instance-var-name>.<embedded-class-property>.
  4. Use an @AssociationOverride for each instance variable involved. You are noting the fields of this class and how they will be mapped into the parent.
  5. For the composite foreign key, use @JoinColumn to point from the database field back to the mapped attribute in the parent object.

I do hope the above points help you to map your object in the unfortunate event that you have to deal with a similar situation. If nothing else, just copy-n-paste-n-edit from the following code:

package com.beavercreekconsulting.example;

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

@Entity
@Table(name = "child")
@AssociationOverrides( {
  @AssociationOverride(name = "primaryKey.id",
    joinColumns = @JoinColumn(name = "id")),
  @AssociationOverride(name = "primaryKey.parentForeignKey",
    joinColumns = {
      @JoinColumn(name = "parent_id",
        referencedColumnName = "id"),
      @JoinColumn(name = "parent_version",
        referencedColumnName = "version") }) })
public class Child {

  @EmbeddedId
  ChildPrimaryKey primaryKey = new ChildPrimaryKey();

  public ChildPrimaryKey getPrimaryKey() {
    return primaryKey;
  }

  public void setPrimaryKey(ChildPrimaryKey primaryKey) {
    this.primaryKey = primaryKey;
  }

  @Transient
  public Integer getId() {
    return getPrimaryKey().getId();
  }

  public void setId(Integer id) {
    getPrimaryKey().setId(id);
  }

  @Transient
  public Parent getParentForeignKey() {
    return getPrimaryKey().getParentForeignKey();
  }

  public void setParentForeignKey(Parent foreignKey) {
    getPrimaryKey().setParentForeignKey(foreignKey);
  }
}

The final section of code I have is an Example class which does a set of CRUD operations with this mapping. I’ve used Spring in the example since I’m starting to believe this is Hibernate’s natural environment:

package com.beavercreekconsulting.example;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

import org.springframework.beans.factory.BeanFactory;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
* Main entry point for example
*/
public class Example {

  HibernateTemplate hibernateTemplate;

  public static void main(String[] args) {
    ClassPathResource resource = new ClassPathResource("beans.xml");
    BeanFactory bf = new XmlBeanFactory(resource);
    Example example = (Example) bf.getBean("example");
    example.go();
  }

  @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
  private void go() {
    create();
    read();
    update();
    delete();
    hibernateTemplate.flush();
  }

  private void update() {
    List parents = hibernateTemplate.find("from Parent");
    for (Parent p : parents) {
        Child c = new Child();
        c.setParentForeignKey(p);
        c.setId(new Random().nextInt());
        p.getChildren().add(c);
        hibernateTemplate.save(c);
    }
  }

  private void read() {
    List parents = hibernateTemplate.find("from Parent");
  }

  private void delete() {
  List parents = hibernateTemplate.find("from Parent");
  for (Parent p : parents) {
    for (Child c : p.getChildren()) {
      hibernateTemplate.delete(c);
    }
    hibernateTemplate.delete(p);
    }
  }

  void create() {
    ParentPrimaryKey ppk = new ParentPrimaryKey();
    ppk.setId(new Random().nextInt());
    ppk.setVersion(new Random().nextInt());
    Parent p = new Parent();
    p.setPrimaryKey(ppk);
    hibernateTemplate.save(p);

    Child c = new Child();
    p.setChildren(Arrays.asList(c));
    c.setParentForeignKey(p);
    c.setId(new Random().nextInt());
    hibernateTemplate.save(c);
  }

  public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
    this.hibernateTemplate = hibernateTemplate;
  }

  public HibernateTemplate getHibernateTemplate() {
    return hibernateTemplate;
  }
}