Hibernate Made Easy
Simplified Data Persistence with Hibernate & JPA Annotations
Suggested Retail Price:   $54.98
Current Amazon Purchase Price:   $54.98
When you buy through us on Amazon:   $45.98
You won't find a better price!
Buy through us...Help support the site.
Most Popular Videos
'Cuz some people don't like reading...
Hibernate JPA Video CBT Tutorials Setting up the environment  Setting Up Hibernate
Hibernate and RSA IRAD Rational Software Architect Application Developer CBT Tutorial  Hibernate & Rational (IRAD)
Hibernate JPA Training  Many to Many Associations
Java Persistence With Hibernate Tutorials  Inheritance Mapping
Working with Compund Primary Keys  Compound Primary Keys
Hibernate and Eclipse Tools CBT Tutorial  Using Hibernate & Eclipse
Portlet Programming
Do you Portal? Then you need this...
WebSphere: What is WebSphere? Java J2EE JEE Portal and Beyond

I Even Made Portlets Real Easy :)
Please Spread the Word!
Why keep HiberBook a secret???







If you found this site helpful, please tell other people about it. I'd really, really appreciate it.

SCJA Java Study Guide
Written with your success in mind...
WebSphere: What is WebSphere? Java J2EE JEE Portal and Beyond

Get Java Certified by Sun
Is Google Clairvoyant?
Google thinks you'll like this...
What is WebSphere?
I wrote this... I know you'll love it...
WebSphere: What is WebSphere? Java J2EE JEE Portal and Beyond

Buy it now!
Most Popular Purchases
What people are buying from us...
Popular Hibernate Purchases  Hibernate Made Easy
Popular Hibernate JPA Books  Harnessing Hibernate
The Best Spring and Hibernate Books  Java Persistence w Hibernate
Java Persistence API Books  Spring in Action
Popular Spring and JBoss Books  What is WebSphere?
Popular Spring and JBoss Books  JSR168 Portlet Programming
The SCJA Exam Questions
I wrote this, too. A Great Exam Prep...
WebSphere: What is WebSphere? Java J2EE JEE Portal and Beyond

Get Sun Java Certified
Most Popular Tutorials
High hit reads on this site...
Hibernate and JPA Links  How Hibernate Works
Hibernate and JPA Free Tutorials  Recommended Books
Hibernate and JPA Simple Examples  Coding Advanced DAOs
Hibernate and JPA Free CBTs  Using The Criteria API
Hibernate and JPA Simple Examples  What is Hibernate?
down caret hibernate jpaAre you Harnessing It?
One of the three books you need...
Harnessing Hibernate Book by James Elliott (Author), Tim O'Brien (Author), Ryan Fowler (Author)
Read My Review
Win Yourself $100!!!
The 100% Error Free Code Challenge...

The $100 Code Challenge - No Code Errors, Guaranteed!!!
Learn How to Win a Benjamin

HardCore Hibernate
Yeah, you need this book, too...
Hibernate and JPA Simple Examples

If my book makes it Easy, this book makes Hibernate Hardcore. This is the reference standard...You need it.

Read My Review
Friendly Links
Websites we like...
Hibernate and JPA Links  JavaRanch: Big Moose Saloon
Hibernate and JPA Free Tutorials  Apache.org
Hibernate and JPA Free CBTs  Pickering is Springfield
Hibernate and JPA Simple Examples  Hibernate.org
Hibernate and JPA Simple Examples  mysql.org
Hibernate and JPA Simple Examples  Coast to Coast
Hibernate and JPA Simple Examples  What is IBM WebSphere?
Hibernate and JPA Simple Examples  Desktop Tower Defense
Is Amazon Psychic?
Amazon thinks you'll like this...

My Other Books & Stuff
Other decent books of mine :)...
Sun Certified Java Associate SCJA Study Guide  Java Associate Study Guide
SCJA Mock Exam Questions  SCJA Questions Guide
JSR-168 Portlet Development Book  JSR168 Portlet Programming
WebSphere Introduction and Support Book  What is WebSphere?
The Simpsons is based on Pickering Ontario  Pickering is Springfield
Oshawa Ontario, Discerning Bombs A book about like in Oshawa Ontario  Discerning Bombs on Oshawa
A Good Book on Hibernate and JPA the Java Persistence API  Hibernate Made Easy
From Amazon with Love
Check this stuff out...
Please Spread the Word!
Why keep HiberBook a secret???







If you found this site helpful, please tell other people about it. I'd really, really appreciate it.

More Google Stuff...
Links to entertain you...
Most Popular Purchases
What people are buying from us...
Popular Hibernate Purchases  Hibernate Made Easy
Popular Hibernate JPA Books  Harnessing Hibernate
The Best Spring and Hibernate Books  Java Persistence w Hibernate
Java Persistence API Books  Spring in Action
Popular Spring and JBoss Books  What is WebSphere?
Popular Spring and JBoss Books  JSR168 Portlet Programming
How to use Compound Primary Keys with Hibernate and JPA Annotations
A quick tutorial on how to create a compound key for your Java classes with Hibernate and JPA

Compound Primary Keys with Hibernate and JPA Annotations

A common requirement of many data driven applications is to define tables that use composite primary keys, which essentially means that instead of having one column in a database table to uniquely identify a record, the table uses two columns that together, represent a unique combination.

Compound primary keys are very common, and to be honest, I feel just a tad guilty about getting this far in my tutorials about Hibernate without addressing the concept, but I assure you, there is a very good reason for the delay. You see, when a database table uses a composite primary key, the Hibernate layer is forced to create a separate class that maps directly to that primary key, and then that class must become an embedded property of the JPA annotated class that maps to the database table of interest. Since the @Embeddable annotation wasn't covered until the previous tutorial, any earlier discussion of compound primary keys would have been a bit premature.

In this chapter, we will explore the creation of compound primary key classes, and discover how those classes can be embedded within a JPA annotated class.

Compound Keys and the Interest Table

I have a sweet little database table named interest, that has one numeric property called rate, and two non-unique, numeric properties called userId and bankId, which together, uniquely represent a record in the interest table.

So, how do we map compound primary keys in Hibernate using JPA annotations? Well, when we run into compound primary keys, the Java developer is forced to create a separate, unique class that will represent the primary key combination. For the interest table, we will create a separate class called CompoundKey to manage the compound primary key.

package com.examscam.mappings;
import javax.persistence.Embeddable;
/* First Iteration of the CompoundKey Class */
@Embeddable
public class CompoundKey implements 
                             java.io.Serializable{
  private Long userId;
  private Long bankId;
  public CompoundKey() {}
  public CompoundKey(Long user, Long bank) {
    userId = user;
    bankId = bank;
  }

  public Long getBankId() {return bankId;}
  public void setBankId(Long bankId) {
    this.bankId = bankId;
  }
  public Long getUserId() {return userId;}
  public void setUserId(Long userId) {
    this.userId = userId;
  }
}
 

Basic Requirements of Compound Key Classes

There are a couple of quick notes that you should make about the CompoundKey class. First of all, the class is not decorated with the @Entity annotation, but instead, the class uses the @Embeddable annotation, emphasizing that this primary key class will actually be used by, or embedded into, another JPA annotated class that is responsible for defining the database table mappings.

The other important addition to the CompoundKey class is the implementation of the java.io.Serializable interface. All compound keys must implement this interface, otherwise, when Hibernate maps the compound key to the database, you'll get the following exception:

org.hibernate.MappingException:
composite-id class must implement Serializable

Primary Key Comparisons

Our CompoundKey class has a two argument constructor that allows you to create instances in the following manner:

CompoundKey key01 = 
   new CompoundKey(new Long(1), new Long(2));
CompoundKey key02 = 
   new CompoundKey(new Long(1), new Long(2));

Now, from looking at that code, are key01 and key02 equal to each other? Think about it from a Hibernate perspective; if we have two instances of the primary key class, perhaps generated by two separate Hibernate Sessions calling load or get, and both of them have the exact same values for userId and bankId, are those two instances logically and comparably the same?

Well, it's not really up for debate ? two primary keys with two identical key values MUST BE EQUAL, but, as it stands right now, the key01.equals(key02); method call will return false, and that is disastrous to our Hibernate applications.

Comparing Instances with .equals(Object o)

In Hibernate, all compound primary key classes must override the .equals() and the .hashCode() methods inherited from the Object class. Actually, all Hibernate Entity classes should override .equals() and .hashCode() with their own, custom .equals() and .hashCode() methods so that the same persistent object loaded from separate Hibernate Sessions will equate to true. You see, when objects are created, they are given a unique memory location, and the default implementation of the .equals() method is to compare the memory locations of two instances. As a result, the following code generates an output of false.

CompoundKey key01 = 
   new CompoundKey(new Long(1), new Long(2));
CompoundKey key02 = 
   new CompoundKey(new Long(1), new Long(2));
boolean flag = key01.equals(key02):
System.out.println(flag);  // prints out false!!!

However, we can override the default implementation of the .equals() method, and generate a boolean value based on the comparison of the various instance variables within the class. With this in mind, we can override the .equals() method of the CompoundKey class by returning a true value if the two instances being compared have the same value for the userId and bankId properties:

public boolean equals(Object key) {
  boolean result = true;
  if (!(key instanceof CompoundKey)) {return false;}
    Long otherUserId = ((CompoundKey)key).getUserId();
    Long otherBankId = ((CompoundKey)key).getBankId();
    if (bankId == null || otherBankId == null) {
      result = false;
    }else {
      result = bankId.equals(otherBankId);
    }
    if (userId == null || otherUserId == null) {
      result = false;
    }else {
      result = userId.equals(otherUserId);
    }
  return result;
}

Overriding .equals() and hashCode()

package com.examscam.mappings;import javax.persistence.Embeddable;
/* Final Iteration of the CompoundKey Class */
@Embeddable
public class CompoundKey implements java.io.Serializable{
  private Long userId;  private Long bankId;
  public CompoundKey() { }
  public CompoundKey(Long user, Long bank) {
    userId = user;  bankId = bank;
  }
  public Long getBankId() {return bankId;}
  public void setBankId(Long bankId) {
    this.bankId = bankId;
  }
  public Long getUserId() {return userId;}
  public void setUserId(Long userId) {
    this.userId = userId;
  }

  public boolean equals(Object key) {
   boolean result = true;
   if (!(key instanceof CompoundKey)) {return false;}
    Long otherUserId = ((CompoundKey)key).getUserId();
    Long otherBankId = ((CompoundKey)key).getBankId();
    if (bankId == null || otherBankId == null) {
      result = false;
    }else {
      result = bankId.equals(otherBankId);
    }
    if (userId == null || otherUserId == null) {
      result = false;
    }else {
      result = userId.equals(otherUserId);
    }
   return result;
  }

  public int hashCode() {
    int code = 0;
    if (userId!=null) {code +=userId;}
    if (bankId!=null) {code +=bankId;}
    return code;
  }
}

Overriding the hashCode() Method

Any time you override the .equals() method, you must override the .hashCode() method in a way that ensures that two objects whose .equals() comparison generates a true result will also generate a common .hashCode() value.

I'm going to simply leverage the values of the userId and bankId properties as I override the hashCode() method. This is obviously an oversimplified implementation of hashCode(), as it will produce duplicates for instances that have keys that add up to the same value, but for this very simplified example, it will suffice.

  public int hashCode() {
    int code = 0;
    if (userId!=null) {code +=userId;}
    if (bankId!=null) {code +=bankId;}
    return code;
  }

Once the .hashCode() and .equals() methods are properly implemented and overridden, the following code, which uses .equals() to compare two CompoundKey instances that share common attribute values, but are stored in different memory locations, will generate a boolean comparison result of true.

CompoundKey key01 = 
   new CompoundKey(new Long(1), new Long(2));
CompoundKey key02 = 
   new CompoundKey(new Long(1), new Long(2));
boolean flag = key01.equals(key02):
System.out.println(flag);  // now returns true!!!

Using the CompoundKey Class

So, once you have a primary key class that implements Serializable, is decorated with the @Embeddable tag, and has overridden the .equals() and the .hashCode() methods, you are ready to embed your compound key class within a JPA annotated entity class. For this example, we will create a class called Interest, with two properties, one of type double to represent an interest rate, and a property of type CompoundKey that we will name id. With the @Id annotation over the getter for the id field, our Interest class would look like this:

package com.examscam.mappings;
import javax.persistence.*; import org.hibernate.Session;
import com.examscam.HibernateUtil;
@Entity
public class Interest {

  private CompoundKey id;
  private double rate;

  @Id
  public CompoundKey getId() {return id;}
  public void setId(CompoundKey id) {this.id=id;}
  public double getRate() {return rate;}
  public void setRate(double rate) {this.rate=rate;}

  public static void main(String args[]) {
    Interest rate = new Interest();
    rate.setRate(18.5);

    Long wayne=new Long(99); Long mario=new Long(88);
    CompoundKey key = new CompoundKey(wayne, mario);
    rate.setId(key);

    HibernateUtil.recreateDatabase();
    Session session = HibernateUtil.beginTransaction();
    session.save(rate);
    HibernateUtil.commitTransaction();
  }
}

Running the Interest Class

Running the main method of the Interest class successfully adds a new record to the interest table, with the appropriate fields being initialized according to the code.

  public static void main(String args[]) {
    Interest rate = new Interest();
    rate.setRate(18.5);

    Long wayne=new Long(99); Long mario=new Long(88);
    CompoundKey key = new CompoundKey(wayne, mario);
    rate.setId(key);

    HibernateUtil.recreateDatabase();
    Session session = HibernateUtil.beginTransaction();
    session.save(rate);
    HibernateUtil.commitTransaction();
  }
	
			
			

Of course, to have Hibernate recognize the Interest class, it must be added to the AnnotationConfiguration object in the HibernateUtil class. Notice that the CompoundKey class does not need to be added. As the CompoundKey class is marked as being @Embeddable, only the embedding class, Interest, needs to be explicitly added to the AnnotationConfiguration object.

public static Configuration getInitializedConfiguration() {
   AnnotationConfiguration config = 
               new AnnotationConfiguration();
  /* add all of your JPA annotated classes here!!! */
  config.addAnnotatedClass(Interest.class);
  config.configure();
  return config;
}

More Compound Key Mappings

Personally, I like the idea of implementing compound primary keys by simply creating a compound key class, marking it as being @Embeddable, and then creating an instance variable in the embedding class that simply gets marked with an @Id tag. To me, it's fairly simple, fairly straight forward, intuitive, and easy. However, there are alternatives, one of which is to use the @IdClass annotation in your persistent entity class, and then simply provide setters and getters for the properties defined in the primary key class.

Using the @IdClass JPA Annotation

With the @IdClass annotation, you simply point to the class implementing your compound primary key. The @IdClass is placed right after the @Entity tag, just before the class declaration. We'll create a new class called Fracture to demonstrate:

@Entity
@IdClass(com.examscam.mappings.CompoundKey.class)
public class Fracture {  }

With the @IdClass annotation, you don't declare an instance variable of type CompoundKey, but instead, just define instance variables for each of the primary key fields. You then mark the corresponding getter tags with standard @Id annotations.

@Entity
@IdClass(com.examscam.mappings.CompoundKey.class)
public class Fracture {
  Long bankId; 
  Long userId; 
  String bone;
  
  @Id
  public Long getBankId() {return bankId;}
  @Id
  public Long getUserId() {return userId;}
  
  public void setBankId(Long bankId){this.bankId = bankId;}
  public void setUserId(Long userId){this.userId = userId;}
  public String getBone() {return bone;}
  public void setBone(String bone) {this.bone = bone;}
}

The Compound Fracture: Using @IdClass

package com.examscam.mappings;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import org.hibernate.Session;
import com.examscam.HibernateUtil;

@Entity
@IdClass(com.examscam.mappings.CompoundKey.class)
public class Fracture {
  Long bankId; Long userId; String bone;

  @Id
  public Long getBankId() {return bankId;}

  @Id
  public Long getUserId() {return userId;}
  public void setBankId(Long bankId){this.bankId = bankId;}
  public void setUserId(Long userId){this.userId = userId;}
  public String getBone() {return bone;}
  public void setBone(String bone) {this.bone = bone;}

  public static void main(String args[]) {
    Fracture bone = new Fracture();
    bone.setBone("arm");
    bone.setBankId( new Long(99));
    bone.setUserId(new Long(88));
    HibernateUtil.recreateDatabase();
    Session session=HibernateUtil.beginTransaction();
    session.save(bone);
    HibernateUtil.commitTransaction();
  }

}

Running the main Method

Running the main method of the Fracture class successfully gets the Hibernate framework to write a record to the Fracture table, with the appropriate key fields populated.

public static void main(String args[]) {
  Fracture bone = new Fracture();
  bone.setBone("arm");
  bone.setBankId( new Long(99));
  bone.setUserId(new Long(88));
  HibernateUtil.recreateDatabase();
  Session session=HibernateUtil.beginTransaction();
  session.save(bone);
  HibernateUtil.commitTransaction();
}

Making Sure HibernateUtil is Updated

As we run these main methods that test the Fracture and the Interest classes, we must remember that all classes marked with the @Entity tag must be added to the AnnotationConfiguration object. The getInitializedConfiguration() method of the HibernateUtil class is where we typically perform this initialization.

public static Configuration getInitializedConfiguration() {
   AnnotationConfiguration config = 
               new AnnotationConfiguration();
  /* add all of your JPA annotated classes here!!! */
  //config.addAnnotatedClass(ClientDetail.class);
  //config.addAnnotatedClass(Address.class);
  //config.addAnnotatedClass(Skill.class);
  config.addAnnotatedClass(Interest.class);
  config.addAnnotatedClass(Fracture.class);
  config.configure();
  return config;
}

The @EmbeddedId JPA Annotation

The third way I know of managing a compound primary key, and perhaps the easiest, is through the @EmbeddedId annotation. With the compound primary key class properly implemented, all you have to do in your persistent entity class is declare an instance variable in the type of your compound primary key class, and then mark the getter for the id field with the @EmbeddedId tag. Here's how it looks with the Prison class:

The Prison Compound: Using @EmbeddedId

package com.examscam.mappings;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import org.hibernate.Session;
import com.examscam.HibernateUtil;

@Entity
public class Prison {
  private String city;
  private CompoundKey id;

  public String getCity() {return city;}
  public void setCity(String city) {this.city=city;}

  @EmbeddedId
  public CompoundKey getId() {return id;}
  public void setId(CompoundKey id) {this.id = id;}
	
  public static void main(String args[]) {
    Prison jail = new Prison();
    jail.setCity("Milhaven");
    Long wayne = new Long(99);
    Long mario = new Long(88);
    CompoundKey key = new CompoundKey(wayne, mario);
    jail.setId(key);
    HibernateUtil.recreateDatabase();
    Session session = HibernateUtil.beginTransaction();
    session.save(jail);
    HibernateUtil.commitTransaction();	
  }
}

Running the Prison Class

After adding the Prison.class to the AnnotationConfiguration in the HibernateUtil class, the main method can be executed, and as we would expect, the prison table in the database, which contains a compound primary key, is updated successfully.

public static void main(String args[]) {

    Prison jail = new Prison();
    jail.setCity("Milhaven");
    Long wayne = new Long(99);
    Long mario = new Long(88);

    CompoundKey key = new CompoundKey(wayne, mario);
    jail.setId(key);

    HibernateUtil.recreateDatabase();
    Session session = HibernateUtil.beginTransaction();
    session.save(jail);
    HibernateUtil.commitTransaction();	
}


hibernate java hibernate spring hibernate hibernate cache hibernate class hibernate collection hibernate configuration hibernate database hibernate dialect hibernate download hibernate example hibernate mapping hibernate query hibernate sql hibernate tutorial hibernate xml struts hibernate xp hibernate 3 hibernate 3.0 hibernate api hibernate caching hibernate cfg xml hibernate dao hibernate examples hibernate framework hibernate generator hibernate in action hibernate jdbc hibernate list hibernate one to one hibernate plugin hibernate properties hibernate tool hibernate tools hibernate training hibernate tutorials java persistence with hibernate jboss hibernate linux hibernate standby hibernate ubuntu hibernate xdoclet hibernate hibernate synchronizer computer hibernate disable hibernate enable hibernate hibernate 2 hibernate 3.2 hibernate annotation hibernate annotations hibernate bag hibernate batch hibernate blob hibernate button hibernate c3p0 hibernate cascade hibernate command hibernate composite id hibernate composite key hibernate config hibernate connection hibernate criteria hibernate date hibernate delete hibernate discriminator hibernate documentation hibernate ehcache hibernate entitymanager hibernate enum hibernate fetch hibernate file hibernate filter hibernate flush hibernate formula hibernate forum hibernate hbm hibernate hbm2ddl hibernate hbm2ddl auto hibernate hql hibernate id hibernate in vista hibernate inheritance hibernate insert hibernate interceptor hibernate interview questions hibernate inverse hibernate javadoc hibernate join hibernate jpa hibernate lazy hibernate lazy loading hibernate load hibernate logging hibernate many to many hibernate many to one hibernate map hibernate mapping file hibernate merge hibernate mode hibernate named query hibernate order hibernate order by hibernate org hibernate performance hibernate problems hibernate property hibernate proxy hibernate query language hibernate reference hibernate restrictions hibernate reverse engineering hibernate save hibernate saveorupdate hibernate search hibernate select hibernate sequence hibernate session hibernate sessionfactory hibernate set hibernate shortcut hibernate show_sql hibernate source hibernate sql query hibernate stored procedure hibernate template hibernate timestamp hibernate transaction hibernate type hibernate update hibernate usertype hibernate validator hibernate version how to hibernate laptop hibernate net sf hibernate netbeans hibernate org hibernate dialect org hibernate session sleep hibernate sleep vs hibernate spring and hibernate standby vs hibernate turn off hibernate vista hibernate what is hibernate windows hibernate windows xp hibernate all 150 Help java spring apache xml ajax cache cmp j2ee s truts tomcat ejb jboss jsf maven primary key ruby on rails foreign key hibernation ibatis one to one spring framework xdoclet hql hybernate jdo many to many middlegen ojb one to many

eXTReMe Tracker