Mock tests, Interview questions, Tutorials and Tech news
 
 
Home > Programming / tutorials > Mapping Composite Keys in Hibernate

Mapping Composite Keys in Hibernate

February 10th, 2010 Leave a comment Go to comments

In Real world applications it is natural to have tables with composite keys. The row will be identified with combination of key  column values of a row.

Consider Following scenario:

A test selling website defines tests and customers.

create table test(
test_id int(11) not null auto_increment,
test_name varchar(100),
test_desc varchar(150),
test_questions varchar(5),
primary key(test_id));

create table customer(
customer_id int(11) not null auto_increment,
name varchar(50) not null,
email_id varchar(100),
contact_no varchar(20),
primary key(customer_id));

To keep track of tests purchased by customers it maintains following table:

create table tests_purchased(
customer_id int(11) not null,
test_id int(11) not null,
created_date datetime not null,
primary key(customer_id, test_id));

Primary key of this table is combination of customer and test id.
To map such classes we have to use <composite-key> tag.  Both the test_id and customer_id acts as identifier to the tests_purchased table.

In hibernate we use session.load (entityClass, id_type_object) to find and load the entity using primary key. In case of composite keys, the id object should be a separate ‘id ‘ class (in above case a PurchasedTestId class)  which just declares the primary key attributes, something like below:

package entities;
import java.io.Serializable;
public class PurchasedTestId implements Serializable{
private Long testId;
private Long customerId;
// an easy initializing constructor
public PurchasedTestId(Long testId, Long customerId){
this.testId = testId;
this.customerId = customerId;
}
public Long getTestId() {
return testId;
}
public void setTestId(Long testId) {
this.testId = testId;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
@Override
public boolean equals(Object arg0) {
if(arg0 == null) return false;
if(!(arg0 instanceof PurchasedTestId)) return false;
PurchasedTestId arg1 = (PurchasedTestId) arg0;
return (this.testId.longValue() == arg1.getTestId().longValue()) && (this.customerId.longValue() == arg1.getCustomerId().longValue());

}
@Override
public int hashCode() {
int hsCode;
hsCode = testId.hashCode();
hsCode = 19 * hsCode+ customerId.hashCode();
return hsCode;
}

}

2 main things to note here are:
1. the Class implements Serializable
2. The equals() and hashcode() functions. It is important to implement these 2 functions as hibernate relies on  these methods to cache and compare the data.
So with this ID class, the PurchasedTest bean representing tests_purchased table would look like below:

package entities;
import java.util.Date;
public class PurchasedTest {
PurchasedTestId purchasedTestId;

Date purchaseDate;

public PurchasedTestId getPurchasedTestId() {
return purchasedTestId;
}
public void setPurchasedTestId(PurchasedTestId purchasedTestId) {
this.purchasedTestId = purchasedTestId;
}
public Date getPurchaseDate() {
return purchaseDate;
}
public void setPurchaseDate(Date purchaseDate) {
this.purchaseDate = purchaseDate;
}

}

Here it replaces both testId and customerId columns and declares single purchasedTestId variable.

The purchasedTest.hbm.xml file would look like below:

<?xml version=”1.0″?>
<!DOCTYPE hibernate-mapping PUBLIC
“-//Hibernate/Hibernate Mapping DTD 3.0//EN”
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd“>

<hibernate-mapping>
<class name=”entities.PurchasedTest” table=”tests_purchased”>

<composite-id name=”purchasedTestId”>
<key-property name=”testId” column=”TEST_ID” />
<key-property name=”customerId” column=”CUSTOMER_ID” />
</composite-id>

<property name=”purchaseDate” type=”timestamp”>
<column name=”created_date” />
</property>

</class>
</hibernate-mapping>

The <id> is replaced by <composite-id> tag. The name filed specifies the name of the PurchasedTestId type property in PurchasedTest bean.
Both the <key-property> tags represent the key columns.
With the above setting one can do operations on tests_purchased table like below:
// To save data

PurchasedTest ptest = new PurchasedTest();
PurchasedTestId purchasedTestId = new PurchasedTestId(1l,1l);
ptest.setPurchasedTestId(purchasedTestId);
ptest.setPurchaseDate(new Date());
session.save(ptest);
// To find data
PurchasedTestId purchasedTestId = new PurchasedTestId(1l,1l);
PurchasedTest ptest = (PurchasedTest)session.load(PurchasedTest.class, purchasedTestId);
System.out.println(“test ID ::”+ptest.getPurchasedTestId().getTestId());
System.out.println(“cust ID ::”+ptest.getPurchasedTestId().getCustomerId());

// deleting the row
PurchasedTestId purchasedTestId = new PurchasedTestId(1l,1l);
PurchasedTest ptest = (PurchasedTest)session.load(PurchasedTest.class, purchasedTestId);

session.delete(ptest);

To download full code click here.

Going for an interview or want to test your hibernate skills , check out our hibernate interview questions

Be Sociable, Share!
  1. November 28th, 2012 at 04:25 | #1

    Thank you, this helped me. :)

  2. August 7th, 2012 at 07:08 | #2

    Very useful post smitha, thanks for sharing the info.

    Apple Health Club | Best Tips to Stay Healthy

  3. Natraj
    February 23rd, 2012 at 10:17 | #3

    Very helpful. Its a neatly represented the concept.

  4. Revathy
    January 5th, 2012 at 04:03 | #4

    Hi Sumita,
    It is possible to declare generator tag in the composite id field for hibernate,.that is my table my 2 primary keys and i want one of those primary keys to be auto incremented.It si possible in hibernate if not what is the other way by which i can implement the composite primary keys and generator tag.?Please respond immediatley.Thanks a ton.

  5. himanshu
    May 14th, 2011 at 03:26 | #5

    foreign key concept is not implemented

  6. bkrakesh
    January 18th, 2011 at 00:05 | #6

    I just saw one article , which explain implementation of composite primary key using annotation with simple example.
    http://j2eereference.com/2011/01/implementing-composit-primary-key-with-jpa-and-hibernate/

  7. Sridevi Nanubala
    March 1st, 2010 at 01:40 | #7

    Hi Smitha,

    It is very nice of you, that you have explained about composite-id in hibernate so clearly.
    Further to this, Could you please hint out , how to invoke the query and execute with HibernateTemplate.Find
    Eagerly looking for your comment.

    Thanks in advance.
    Sridevi.

  8. Changsu Jiang
    February 19th, 2010 at 13:05 | #8

    @David M. Karr

    regarding your question in the last paragraph, you just need to define the mapping to the object reference to be “insertable=false, updatable=false” if you use annotation.

    for example:

    public class PurchaseTest {
    private PurchaseTestId pk = new PurchaseTestId();
    private Customer customer;

    @EmbeddedId
    public PurchaseTestId getPk() {
    return this.pk;
    }

    public void setPk(PurchaseTestId pk) {
    this.pk = pk;
    }

    public Customer getCustomer() {
    return this.customer;
    }

    @ManyToOne
    @JoinColumn(name=”CUSTOMER_ID”, insertable=false, updatable=false)
    public void setCustomer(Customer customer) {
    this.customer = customer;
    }
    }

  9. David M. Karr
    February 19th, 2010 at 12:11 | #9

    One thing I’ve noticed with both Hibernate and JPA is that when you map an entity with composite keys, it becomes awkward when one or more of those keys that make up the composite key could represent an object reference that could be navigated to, if it wasn’t defined as a (boxed) primitive.

    For instance, your “PurchasedTestId” class maps the PKs for “customer” and “test” with Long values, instead of something like “Customer” or “Test”. If you had a need to navigate from that instance to the Customer or Test object, you couldn’t do it directly.

    I’ve often wondered what would happen if you defined two properties in a class, one a boxed primitive used in the composite key, and the other a true object reference, and you mapped both of them in the mapping to the same column. I think there’s a possibility this could work when reading from the database, but I don’t know what it would do when writing.

  1. June 15th, 2011 at 12:44 | #1

Get Adobe Flash playerPlugin by wpburn.com wordpress themes