Friday, January 30, 2009

Using Criteria API in Hibernate

Hibernate is awesome! I'm pretty excited about Hibernate and I'll continue to use it as primary ORM solution for my future projects. Today I'd like to share typical usage scenarios of Hibernate Criteria API which provides extremely powerful capabilities to build strong typed queries.

Let's start with few simple classes (Employer and Employee) mapped to database tables. We'll use those in future examples.
@Entity
@Table( name = "employees" )
public class Employee {
@Id
@GeneratedValue( strategy = GenerationType.AUTO )
@Column( name = "id" )
private Integer id;

@Column( name = "name" )
private String name;

@Column( name = "email" )
private String email;

@ManyToOne
private Employer employer;

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

public Integer getId() {
return this.id;
}

public void setName( String name ) {
this.name = name;
}

public String getName() {
return this.name;
}

public void setEmail( String email ) {
this.email = email;
}

public String getEmail() {
return this.email;
}
};

@Entity
@Table( name = "employers" )
public class Employer {
@Id
@GeneratedValue( strategy = GenerationType.AUTO )
@Column( name = "id" )
private Integer id;

@OneToMany( cascade = CascadeType.ALL, mappedBy = "employer" )
private Set<Employee> employees = new HashSet<Employee>();

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

public Integer getId() {
return this.id;
}

public Set<Employee> getEmployees() {
return this.employees;
}

public void setEmployees( Set<Employee> employees ) {
this.employees = employees;
}
};

Let say we have to find Employees and Employers using different search criteria. For all examples I assume that helper method getCurrentSession() returns Hibernate's session and each code fragment is running within transaction.

1) Find all Employees with name "Tom" (the easiest one).
// Get current session
final Session session = getCurrentSession();

// Create Criteria for Employee class
final Criteria criteria = session.createCriteria( Employee.class );
// Add name = "Tom" restriction
criteria.add( Restrictions.eq( "name", "Tom" );

// Run query and get the results
List< Employee gt; employees = criteria.list();
// Do something here
...

2) Find all Employers which don't have any Employees.
// Get current session
final Session session = getCurrentSession();

// Create Criteria for Employer class
final Criteria criteria = session.createCriteria( Employer.class );
// Add empty Employees restriction
criteria.add( Restrictions.isEmpty( "employees" );

// Run query and get the results
List< Employer gt; employers = criteria.list();
// Do something here
...

3) Find all Employers which have Employees with name "Tom".
// Get current session
final Session session = getCurrentSession();

// Create Criteria for Employer class
final Criteria criteria = session.createCriteria( Employer.class );
// Create alias for Employees collection
criteria.createAlias( "employees", "employees" );
// Add name = "Tom" restriction
criteria.add( Restrictions.eq( "employees.name", "Tom" );

// Run query and get the results
List< Employer > employers = criteria.list();
// Do something here
...

4) For this example let me introduce another table "black_list" and BlackListEntry entity mapping.
@Entity
@Table( "black_list" )
public class BlackListEntry {
@Id
@GeneratedValue( strategy = GenerationType.TABLE )
@Column( name = "email" )
private String email;

public void setEmail( String email ) {
this.email = email;
}

public String getEmail() {
return this.email;
}
};

The purpose of this entity is pretty simple: a black list of e-mail addresses. Those should be exclude from any mailing lists. Let's find all Employees whose e-mails are in that black list.
// Get current session
final Session session = getCurrentSession();

// Create detached Criteria
DetachedCriteria detachedCriteria =
DetachedCriteria.forClass( BlackListEntry.class );
// Create projection to select 'email' only
detachedCriteria.setProjection( Projections.property( "email" ) );

// Create Criteria for Employee class
final Criteria criteria = session.createCriteria( Employee.class );
// Add subquery restriction (all emails in black list)
criteria.add( Subqueries.propertyIn( "email", detachedCriteria ) );

// Run query and get the results
List< Employee > employees = criteria.list();
// Do something here
...

That's it. :-)