One to One Mapping

Jason and I spent a bit of time today trying to get OneToOne mappings to work out.

Consider the following example. Suppose I have two persistent objects, Person and Patient. Patient is a role played by a Person. One of the common ways of expressing one-to-one relationships is to have the dependent object share the same primary key as the controlling object. (This is typical in what UML calls “compositional relationships”).

The Hibernate documentation uses a similar example involving Heart and Body. Their example doesn’t tell you the special, secret to making stuff work. We’re better than them, though, and we’ll tell you the secrets of making this work.

Here is an early version of our objects:

@Entity
public class Person {

  private Long id;

  // other attributes here...

  @Id @GeneratedValue(strategy=GenerationType.AUTO)
  public Long getId() {
    return this.id;
  }

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

  // other getters/setters here...

}

And here is our Patient:

@Entity
public class Patient {

  private Long id;
  private Person person;

  @Id
  public Long getId() {
    return this.id;
  }
  public void setId(Long id) {
    this.id = id;
  }

  @OneToOne(cascade = CascadeType.ALL, optional=false)
  @PrimaryKeyJoinColumn
  public Person getPerson() {
    return this.person;
  }

  public void setPerson(Person person) {
    this.person = person;
  }
}

If you set up your code like this, you’ll find yourself getting an error message that says

Exception raised - org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): com.example.Patient

The problem is that we’ve confused Hibernate. We’ve let it believe that it should look at the Id column to determine the primary id of the table. In truth, we should have told Hibernate that the Person object is the thing that provides the id. In order to help Hibernate understand how to properly resolve the id, we need to annotate the id column of the Patient with a special “Generator”, like so:

@Id @GeneratedValue(generator="foreign")
  @GenericGenerator(name="foreign", strategy = "foreign", parameters={
    @Parameter(name="property", value="person")
  })
  public Long getId() {
    return this.id;
  }

This annotation says that the generated value of my Id column comes from a special generator that simply reads a foreign key value off of the person object.

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

One thought on “One to One Mapping

  1. Pingback: Hibernate Annotation Examples | i-Proving –

Leave a Reply