Tuesday, November 8, 2011

Hibernate by Example - Part 2 (DetachedCriteria)

So last time we helped out justice league to effectively manage their super heroes. Today we focus on how The Avengers will be using Hibernate's Detached Criteria to find out their enemies with respect to each superhero so as to protect their super heroes. You can download the working example from here.

In this example we take only two entities into consideration. The Avenger & The Villain. We build a relationship between the two using a join table. Let us have a look at the domain mappings used in this example.




package com.avengers.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.Type;

/**
 * The domain class representing each member of the avengers
 * 
 * @author Dinuka.Arseculeratne
 * 
 */
@Entity
@Table(name = "Avengers")
public class Avenger implements Serializable {

 /**
  * The primary key of the Avenger table
  */
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 @Column(name = "avenger_id")
 private Long avengerId;

 /**
  * The name of the avenger member
  */
 @Column(name = "avenger_name")
 private String avengerName;

 /**
  * A flag which holds whether the avenger's powers are awesome
  */
 @Type(type = "yes_no")
 @Column(name = "is_awesome")
 private boolean isAwesome;

 /**
  * The list of enemies the avenger has
  */
 @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
 @JoinTable(name = "AVENGERS_AND_VILLAINS", joinColumns = { @JoinColumn(name = "avenger_id") }, inverseJoinColumns = { @JoinColumn(name = "villain_id") })
 private List<Villain> enemyList = new ArrayList<Villain>();

 public Long getAvengerId() {
  return avengerId;
 }

 public void setAvengerId(Long avengerId) {
  this.avengerId = avengerId;
 }

 public String getAvengerName() {
  return avengerName;
 }

 public void setAvengerName(String avengerName) {
  this.avengerName = avengerName;
 }

 public boolean isAwesome() {
  return isAwesome;
 }

 public void setAwesome(boolean isAwesome) {
  this.isAwesome = isAwesome;
 }

 public List<Villain> getEnemyList() {
  return enemyList;
 }

 public void addEnemy(Villain enemy) {
  enemyList.add(enemy);
 }

 @Override
 public String toString() {
  return "Avenger [avengerId=" + avengerId + ", avengerName="
    + avengerName + ", isAwesome=" + isAwesome + ", enemyList="
    + enemyList + "]";
 }

 
}

This class maps an avenger. I have used minimal fields in order to keep this example as simple and short as possible. And the Villain domain looks as follows;
package com.avengers.domain;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.Type;

/**
 * This class represents the Villain forces against the avengers
 * 
 * @author Dinuka.Arseculeratne
 * 
 */
@Entity
@Table(name = "Villains")
public class Villain implements Serializable {

 /**
  * The primary key of the Enemy table
  */
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 @Column(name = "villain_id")
 private Long villaiId;

 /**
  * The name of the enemy
  */
 @Column(name = "villain_name")
 private String villainName;

 /**
  * A flag which checks whether the villain is super awesome
  */
 @Type(type = "yes_no")
 @Column(name = "is_awesome")
 private boolean isAwesome;

 public Long getVillaidId() {
  return villaiId;
 }

 public void setVillaidId(Long villaidId) {
  this.villaiId = villaidId;
 }

 public String getVillainName() {
  return villainName;
 }

 public void setVillainName(String villainName) {
  this.villainName = villainName;
 }

 public boolean isAwesome() {
  return isAwesome;
 }

 public void setAwesome(boolean isAwesome) {
  this.isAwesome = isAwesome;
 }

 @Override
 public String toString() {
  return "Villain [villaiId=" + villaiId + ", villainName=" + villainName
    + ", isAwesome=" + isAwesome + "]";
 }

}

Ok now that we have defined our domains, let us see how data retrieval happens with DetachedCriteria. I have used DetachedCriteria here because The Avengers were very specific and said they do not want anything to do with the Hibernate session, so hence i used DetachedCriteria which does not require a hibernate session to be present.

Our primary objective is to retrieve The Avenger to whom a villain belongs to. Note that this assumes the same villain cannot be a villain to more than one superhero. So moving on i give below the method that retrieves an avenger based on the villain name passed.

public Avenger retrieveAvengerByVillainName(String villainName) {

  Avenger avenger = null;
  /**
   * Selected a detached criteria so we do not need a session to run it
   * within.
   */
  DetachedCriteria criteria = DetachedCriteria.forClass(Avenger.class);

  /**
   * Here we are doing an inner join with the Villain table in order to do
   * a name comparison with the villainName passed in as a method
   * parameter
   */
  DetachedCriteria villainCriteria = criteria.createCriteria("enemyList");

  villainCriteria.add(Restrictions.eq("villainName", villainName));

  villainCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

  @SuppressWarnings("unchecked")
  List<Avenger> avengerList = getHibernateTemplate().findByCriteria(
    criteria);

  if (!avengerList.isEmpty()) {
   avenger = avengerList.get(0);
   getHibernateTemplate().initialize(avenger.getEnemyList());
  }
  return avenger;

 }
In this method what we do is first we create a criteria for our master class which in this case is Avenger.class. Then we need to do a join with the Villain table and hence we create a sub criteria from our main criteria with the name of the list we defined within the Avenger domain class. Then it is a matter of equaling the property of the Villain domain to the villain name passed in.

The power of the Criteria API is such that you can create dynamic queries with ease which would be a hassle if we were to use pure HQL which would require substantial string concatenations in order to achieve the same.

A sample test class called AvengerTest.java is given with the attachment which you can find at the top of the most. Note that you need to remove the comment on avenger-context.xml in order to create the tables needed for this example. So that is about it.

The Avengers can be risk averse now that they have a system by which they can relate any super villain to a super hero within their league.

As always your comments and suggestions are always welcome and appreciated.

Thank you for taking the time to read!!!!

7 comments:

  1. Keep It Up .good work .. Explore JOIN Fetch too .. thas handy when you join stuff.. Sanjeewa

    ReplyDelete
  2. Cool blog! Its very entertaining and informative. Thanks!

    ReplyDelete
  3. Thx alot for leaving by a comment. Appreciate it :)

    ReplyDelete
  4. Thanks soo much! This example really helped me!

    ReplyDelete
  5. Good Work. Give little more explanation

    ReplyDelete
  6. Thanks for the clear post which helped me to understand Criteria with Join in a simple way. Keep up the good work.

    ReplyDelete