Composite identifiers or Composite keys in Hibernate

You will find many cases where you must take multiple columns to build an identified for the data in a row of table. For example, if you are storing the voter list data in table, then to identify the record of a voter, you could search using his name, father name and date of birth as the search criteria because the chances are very low if two ones will have the same values for these three fields.

Here, these three fields (name, father name and date of birth) are working like a key to identify the records or you can refer this group fields as the identifier for a record in database. The identified which is composed (or created) of multiple attributes(columns) is called composite identifiers.

 

Composite identifiers in Hibernate or JPA

You can create the composite identifier in Hibernate or in JPA using the two annotations @IdClass and @EmbeddedId. 

 

@IdClass Vs @EmbeddedId

@IdClass is used to create the composite identifier using the fields of the same entity on which this annotation is applied. @EmbeddedId allows you to separate the fields those are the part of the composite key in separate class.  
 

Using @IdClass

package example.configuration.demo4.datamodel;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;

@Entity
@Table(name="book")
@IdClass( PK.class )
public class Book {
	@Id
	private String title;
	@Id
	private String authorName;
	@Id
	private String publisherName;
	
	private float price;
	
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	public String getAuthorName() {
		return authorName;
	}
	public void setAuthorName(String authorName) {
		this.authorName = authorName;
	}
	public String getPublisherName() {
		return publisherName;
	}
	public void setPublisherName(String publisherName) {
		this.publisherName = publisherName;
	}
	
}

class PK implements Serializable{
	
	private String title;
	private String authorName;
	private String publisherName;
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((authorName == null) ? 0 : authorName.hashCode());
		result = prime * result + ((publisherName == null) ? 0 : publisherName.hashCode());
		result = prime * result + ((title == null) ? 0 : title.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;
		PK other = (PK) obj;
		if (authorName == null) {
			if (other.authorName != null)
				return false;
		} else if (!authorName.equals(other.authorName))
			return false;
		if (publisherName == null) {
			if (other.publisherName != null)
				return false;
		} else if (!publisherName.equals(other.publisherName))
			return false;
		if (title == null) {
			if (other.title != null)
				return false;
		} else if (!title.equals(other.title))
			return false;
		return true;
	}
	
}

Running demo

package example.configuration.demo4;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

import example.configuration.demo4.datamodel.Book;


public class MainClass {
	public static void main(String[] args){
		
		StandardServiceRegistry registry=new StandardServiceRegistryBuilder().configure().build();
		SessionFactory factory=new MetadataSources(registry).buildMetadata().buildSessionFactory();
		Session session=factory.openSession();
		
		Transaction tx=session.beginTransaction();
		
		Book book=new Book();
		book.setAuthorName("John");
		book.setPrice(2342);
		book.setPublisherName("Epub");
		book.setTitle("Learning Java");
		
		session.save(book);
		tx.commit();
		session.close();
	}
}

 

 

Using @EmbeddedId

package example.configuration.demo5;

import java.io.Serializable;

import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name="book")
public class Book {
	
	
	@EmbeddedId
	private PK pk;
	
	private float price;
	
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	
	public PK getPk() {
		return pk;
	}
	public void setPk(PK pk) {
		this.pk = pk;
	}
	
}

@Embeddable
class PK implements Serializable{
	
	private String title;
	private String authorName;
	private String publisherName;
	
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getAuthorName() {
		return authorName;
	}
	public void setAuthorName(String authorName) {
		this.authorName = authorName;
	}
	public String getPublisherName() {
		return publisherName;
	}
	public void setPublisherName(String publisherName) {
		this.publisherName = publisherName;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((getAuthorName() == null) ? 0 : getAuthorName().hashCode());
		result = prime * result + ((getPublisherName() == null) ? 0 : getPublisherName().hashCode());
		result = prime * result + ((getTitle() == null) ? 0 : getTitle().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;
		PK other = (PK) obj;
		if (getAuthorName() == null) {
			if (other.getAuthorName() != null)
				return false;
		} else if (!getAuthorName().equals(other.getAuthorName()))
			return false;
		if (getPublisherName() == null) {
			if (other.getPublisherName() != null)
				return false;
		} else if (!getPublisherName().equals(other.getPublisherName()))
			return false;
		if (getTitle() == null) {
			if (other.getTitle() != null)
				return false;
		} else if (!getTitle().equals(other.getTitle()))
			return false;
		return true;
	}
	
	
}
package example.configuration.demo5;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;


public class MainClass {
	public static void main(String[] args){
		
		StandardServiceRegistry registry=new StandardServiceRegistryBuilder().configure().build();
		SessionFactory factory=new MetadataSources(registry).buildMetadata().buildSessionFactory();
		Session session=factory.openSession();
		
		Transaction tx=session.beginTransaction();
		
		PK pk=new PK();
		pk.setAuthorName("John");
		pk.setPublisherName("epub");
		pk.setTitle("Learning Hibernate");
		
		Book book=new Book();
		
		book.setPrice(2342);
		book.setPk(pk);
		
		session.save(book);
		tx.commit();
		session.close();
	}
}

However, in both cases, the database structure would be same. You would not see any difference in table in both cases. Hibernate generate table using the following SQL:

    create table book (
       authorName varchar(255) not null,
        publisherName varchar(255) not null,
        title varchar(255) not null,
        price float not null,
        primary key (authorName, publisherName, title)
    )

and the table will have the 3 key columns.

Composite Identifiers demo Hibernate

 

Tags