Now, as you may or may not have gathered from going through these tutorialsk, I'm not a big fan of Query Languages. I mean, it's not that I don't appreciate what they do, it's just that I'm a Java programmer at heart, and writing Java code is what I do best. I'm good with simple SQL, and can bluff my way through a database driven application, but I'm certainly not an SQL master, and when it comes to writing crazy
outer joins, or
group by statements, well, I'm afraid that my SQL skills are sorely lacking.
So, when someone told me that I could largely eliminate my need to write dry and mundane SQL statements, I have to say that I was more than enthused. I mean, I've always thought that a Java developer should be able to think of the data they need to query in the form of the Java objects that make up their domain model, and I know that HQL tries to bring that dream to fruition, but it wasn't until I started using the Criteria API that I realized somebody had really embraced the idea of object-oriented data access, and had implemented it in a very sound and easy to use manner.
Creating smart, simple, and effective queries is what the Criteria API allows us to do, and as such, using the Criteria API will be the focus of this tutorial.
Example Database
The Criteria API allows us to do some very interesting queries, so for the examples in this chapter, I'm going to be executing queries against a user table in the database that contains the following values:
1 : mj : abc123 : mj@mcnz.com
2 : mario : pass : mario@scja.ca
3 : sk8trgrl : password : avril@scja.com
4 : ridley : mypassword : getbent@scja.ca
5 : kerrr : pwd : sheehan@princessjava.com
6 : astra : pwd : rabbit@princessjava.com
7 : cameron : 90210 : me@scwcd.com
8 : stephen : low : stanley@pulpjava.com
9 : che : password : ernesto@pulpjava.com
10 : remy : password : rabbit@scja.com
I created this database by coding the following into a runnable main method. Feel free to do the same.
public static void main(String args[]) {
HibernateUtil.recreateDatabase();
Session session = HibernateUtil.beginTransaction();
{
User u = new User();
u.setLoginName("mj");
u.setPassword("abc123");
u.setEmailAddress("mj@mcnz.com");
u.setVerified(false);
u.setLastAccessTime(new java.util.Date());
u.setRegistrationDate(new GregorianCalendar(2006,01,01));
session.saveOrUpdate(u);
}{
User u = new User();
u.setLoginName("mario");
u.setPassword("pass");
u.setEmailAddress("mario@scja.ca");
u.setVerified(true);
u.setLastAccessTime(Date.valueOf("2008-1-1"));
u.setRegistrationDate(new GregorianCalendar(2007,01,01));
session.saveOrUpdate(u);
}{
User u = new User();
u.setLoginName("sk8trgrl");
u.setPassword("password");
u.setEmailAddress("avril@scja.com");
u.setVerified(false);
u.setLastAccessTime(new java.util.Date());
u.setRegistrationDate(new GregorianCalendar(2008,01,01));
session.saveOrUpdate(u);
}
{
User u = new User();
u.setLoginName("ridley");
u.setPassword("mypassword");
u.setEmailAddress("getbent@scja.ca");
u.setVerified(true);
u.setLastAccessTime(new java.util.Date());
u.setLastAccessTime(Date.valueOf("2006-10-5"));
u.setRegistrationDate(new GregorianCalendar(2006,5,11));
session.saveOrUpdate(u);
}{
User u = new User();
u.setLoginName("kerri");
u.setPassword("pwd");
u.setEmailAddress("sheehan@princessjava.com");
u.setVerified(false);
u.setLastAccessTime(Date.valueOf("2008-2-25"));
u.setRegistrationDate(new GregorianCalendar(2007,12,12));
session.saveOrUpdate(u);
}{
User u = new User();
u.setLoginName("astra");
u.setPassword("pwd");
u.setEmailAddress("rabbit@princessjava.com");
u.setVerified(false);
u.setLastAccessTime(new java.util.Date());
u.setRegistrationDate(new GregorianCalendar());
session.saveOrUpdate(u);
}{
User u = new User();
u.setLoginName("cameron");
u.setPassword("90210");
u.setEmailAddress("me@scwcd.com");
u.setVerified(true);
u.setLastAccessTime(Date.valueOf("2008-9-15"));
u.setRegistrationDate(new GregorianCalendar(2008,8,12));
session.saveOrUpdate(u);
}{
User u = new User();
u.setLoginName("stephen");
u.setPassword("low");
u.setEmailAddress("stanley@pulpjava.com");
u.setVerified(false);
u.setLastAccessTime(Date.valueOf("2008-2-25"));
u.setRegistrationDate(new GregorianCalendar(2008,02,15));
session.saveOrUpdate(u);
}{
User u = new User();
u.setLoginName("che");
u.setPassword("password");
u.setEmailAddress("ernesto@pulpjava.com");
u.setVerified(true);
u.setLastAccessTime(Date.valueOf("1999-7-26"));
u.setRegistrationDate(new GregorianCalendar(1999,3,9));
session.saveOrUpdate(u);
}{
User u = new User();
u.setLoginName("remy");
u.setP@assword("password");
u.setEmailAddress("rabbit@scja.com");
u.setVerified(false);
u.setLastAccessTime(new java.util.Date());
u.setRegistrationDate(new GregorianCalendar(2007,05,21));
session.saveOrUpdate(u);
}
HibernateUtil.commitTransaction();
}
The SQL Insert Statement
For anyone that has their heart set on writing an SQL statement to populate their database with the same data as mine, here's the pertinent SQL statement:
INSERT INTO 'user'
('id','emailAddress','lastAccessTime','login_name','password','registrationDate','verified') VALUES
(1,'mj@mcnz.com','2008-01-13 14:49:01','mj','abc123','2006-02-01',0x00),
(2,'mario@scja.ca','2008-01-01 00:00:00','mario','pass','2007-02-01',0x01),
(3,'avril@scja.com','2008-01-13 14:49:01','sk8trgrl','password','2008-02-01',0x00),
(4,'getbent@scja.ca','2006-10-05 00:00:00','ridley','mypassword','2006-06-11',0x01),
(5,'sheehan@princessjava.com','2008-02-25 00:00:00','kerrr','pwd','2008-01-12',0x00),
(6,'rabbit@princessjava.com','2008-01-13 14:49:01','astra','pwd','2008-01-13',0x00),
(7,'me@scwcd.com','2008-09-15 00:00:00','cameron','90210','2008-09-12',0x01),
(8,'stanley@pulpjava.com','2008-02-25 00:00:00','stephen','low','2008-03-15',0x00),
(9,'ernesto@pulpjava.com','1999-07-26 00:00:00','che','password','1999-04-09',0x01),
(10,'rabbit@scja.com','2008-01-13 14:49:01','remy','password','2007-06-21',0x00);
The User Class to this Point
Here is the User class, as it has been coded and JPA annotated, up until this point.
Please note that a new, toString() method has been added to the class. This will make printing out information associated with the user much simpler.
package com.examscam.model;
import javax.persistence.*;
import org.hibernate.Session;
import com.examscam.HibernateUtil;
@Entity
@Table(name = "user", schema = "examscam")
public class User {
private Long id;
private String loginName;
private String password;
private String encryptedPassword;
private String emailAddress;
private Boolean verified;
private java.util.Date lastAccessTime;
private java.util.Calendar registrationDate;
@Id
@GeneratedValue
@Column(name = "id")
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Transient
public String getEncryptedPassword(){return encryptedPassword;}
public void setEncryptedPassword(String ep) {
this.encryptedPassword = ep;
}
@Column(name = "login_name")
public String getLoginName() {return loginName;}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
@Column(name = "password", nullable=false)
public String getPassword() {return password;}
public void setPassword(String password) {
this.password = password;
}
public String getEmailAddress() {return emailAddress;}
@Temporal(TemporalType.TIMESTAMP)
public java.util.Date getLastAccessTime() {
return lastAccessTime;
}
@Temporal(TemporalType.DATE)
public java.util.Calendar getRegistrationDate() {
return registrationDate;
}
@Basic
public Boolean isVerified() {return verified;}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public void setLastAccessTime
(java.util.Date lastAccessTime) {
this.lastAccessTime = lastAccessTime;
}
public void setRegistrationDate
(java.util.Calendar registrationDate){
this.registrationDate = registrationDate;
}
public void setVerified(Boolean verified) {
this.verified = verified;
}
public String toString() {
return getId() + " : " +
getLoginName() + " : " +
getPassword() + " : " +
getEmailAddress();
}
}
A User Management Application?
So far, we've been giving the User class a pretty solid workout. It's a good class, and it's pretty representative of the type of component you'll see in just about any enterprise application.
Now, just imagine a standard application that managed instances of the User class. You'd probably have some type of search function that allows the end user to search for a user based on a login_name, or based on the id, or the email address. You may even want to see everyone that hasn't verified their email address, or perhaps, everyone that has logged in during the previous day. These are all reasonable searches that a simple User management application might require. So, how many SQL, or should I say, HQL queries, would be required to implement such a system? Well, if you're using the Criteria API, the answer is none.
The User Management Application
The hypothetical User management application is something we will build in the following tutorials, but the Criteria API that we learn here will be a central part of its creation.
Welcome to the Criteria API
If you want to use the Criteria API to perform queries based on the User, all you have to do it create an instance of the User class, populate the properties on which you wish to search, and pass the populated instance to Hibernate. Behind the scenes, Hibernate will generate all of the required SQL, execute the query, and package the results up into a list of very handsome User objects. The Criteria API is the most elegant and flexible way to query your database in an object-oriented way.
So, say you wanted to find all of the Users in your database that hadn't verified their email address. All you'd have to do is follow a few, simple steps.
The first thing you do is create an instance of the User class. For our current use case, we just want users that are not verified, so we create a new User instance, and initialize the verified property to false:
User user = new User();
user.setVerified(false);
From there, we create something called an Example object based on the initialized User. It's easily done by passing the recently created User instance to the static create method of the Example class:
Example example = Example.create(user);
And from there, you just ask the Hibernate Session to create a special Criteria object based on your User class. You then add the Example to the criteria instance. When the list() method is invoked on the criteria instance, the calling program will be returned a list of User instances that match the query criteria. Look at the code, it's quite elegant:
Criteria criteria = session.createCriteria(User.class);
criteria.add(example);
List results = criteria.list();
Can you believe it? That's all you have to do. This very simple snippet of Java code will result in a query executing against the database that returns all of the users who have a verified property of false. You don't have to write a single lick of SQL - the Criteria API takes care of it all for you!
The Complete FindVerifiedUsers Class
package com.examscam.criteria;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import com.examscam.HibernateUtil;
import com.examscam.model.User;
public class FindVerifiedUsers {
public static void main(String[] args) {
User user = new User();
user.setVerified(false);
Example example = Example.create(user);
Session session = HibernateUtil.beginTransaction();
Criteria criteria = session.createCriteria(User.class);
criteria.add(example);
List results = criteria.list();
HibernateUtil.commitTransaction();
for (int i = 0; i<results.size(); i++) {
System.out.println(results.get(i).toString());
}
}
}
When I run the FindVerifiedUsers class, I can see Hibernate kicking out the following SQL query:
Hibernate: select this_.id as id0_0_, this_.emailAddress as emailAdd2_0_0_, this_.lastAccessTime as lastAcce3_0_0_, this_.login_name as login4_0_0_, this_.password as password0_0_, this_.registrationDate as registra6_0_0_, this_.verified as verified0_0_ from examscam.user this_ where (this_.verified=?)
When the code runs, the query finds that six of the ten users in the database have not verified their email address. The application actually spits out the following data:
1 : mj : abc123 : mj@mcnz.com@
3 : sk8trgrl : password : avril@scja.com
5 : kerrr : pwd : sheehan@princessjava.com
6 : astra : pwd : rabbit@princessjava.com
8 : stephen : low : stanley@pulpjava.com
10 : remy : password : rabbit@scja.com Expanding You Criteria Queries
But wait - it gets even better. Lets say you wanted to find all of the people who registered today, and have not verified their email accounts? Well, you just create another User instance, initialize the verified property to false, and initialize the registrationDate property to today's date. Again, create an org.hibernate.criterion.Example object based on the User instance, create a Hibernate Criteria query object based on the User.class, add the Example object to the Criteria Query object, and then have the Criteria query object list the results. It's just that easy!
User user = new User();
user.setVerified(false);
user.setRegistrationDate(new GregorianCalendar());
Example example = Example.create(user);
Session session = HibernateUtil.beginTransaction();
Criteria criteria = session.createCriteria(User.class);
criteria.add(example);
List results = criteria.list();
HibernateUtil.commitTransaction();
for (int i = 0; i<results.size(); i++) {
System.out.println(results.get(i).toString());
}As this query runs, one record is returned. Apparently, Astra is the only person to register today and not verify her email:
6 : astra : pwd : rabbit@princessjava.com Note that the User class, provided at the beginning of this chapter, has a new toString() method that formats a user's details in the form of:
id : loginName : password : email.
Returning a Single Row of Results
If you know for a fact that a given query will only return a single row, you can use the uniqueResult() method of the Criteria class. This will return a single, lazy, java.lang.Object instance that you will need to cast into the appropriate type.
If we wanted to do a query, based on a loginName and a password, you know, a basic query to facilitate the process of a client logging into our application, we could simply initialize the loginName and password properties of a User instance appropriately, and use the Example and Criteria objects to process the query. A username and password combination should be unique across the application, so we can use the uniqueResult method to process the results:
public static void main(String[] args) {
User user = new User();
user.setLoginName("mj");
user.setPassword("abc123");
Example example = Example.create(user);
Session session = HibernateUtil.beginTransaction();
Criteria criteria =
session.createCriteria(User.class);
criteria.add(example);
User u = (User)criteria.uniqueResult();
System.out.println(u);
}
Running this main method generates the following result:
1 : mj : abc123 : mj@mcnz.com
Helpful Methods of the Example Class
Sometimes, your database will contain a variety of entries that have been initialized to zero by default, even though a field initialized to zero is pretty much meaningless. By default, the Example class will match on zero valued properties, but you can reverse this behavior by invoking the excludeZeroes() method on the Example instance:
example.excludeZeroes();
Example example = Example.create(new User());
example.excludeZeroes();
example.excludeNone()
On the flipside, criteria queries ignore matching null values. However, you may actually want to pull records where a given property
has been initialized to a null value. For example, you might want to retrieve every record where the loginName wasn't initialized properly, and has been assigned a null value. To enable matches on null values, you just call the excludeNone() method on your Example instance. The excludeNone() method has the added functionality of enabling matching on fields initialized to zero as well.
Example example = Example.create(new User());
example.excludeNone();
example.excludeProperty(String
property)
Furthermore, you can also exclude a particular named property with the aptly named excludeProperty method. This will ensure that matches on this particular property will not be used to decide whether or not a particular record is returned .
Example example = Example.create(new User());
example.excludeProperty("verified");
Like Queries with the Criteria API
I'm always amazed when the database gurus I know get in front of the awful DB2 Control Console and start whipping out all sorts of crazy SQL statements that use weird escape characters to do nutty like and matching queries. Those skills are simply beyond my grasp. That's why I was so excited to see that the Example class in the criteria API makes it incredibly easy to issue 'like' or 'fuzzy matching' queries by simply invoking a few methods of the API.
For example, say I wanted to find all of the users whose email address ended with .com. It'd be easy! All I'd have to do is create a criteria query that uses an example User instance, and have that instance's email property initialized to .com. Of course, simply initializing the email property to .com won't return any results, because there is no email address in my database that is exclusively .com. However, if I invoke the enableLike() method, and set the MatchMode to END, the criteria query will return all of the results where the end of the email address is .com. Pretty neat, eh?
public static void main(String[] args) {
User user = new User();
user.setEmailAddress(".com");
Example example = Example.create(user);
example.enableLike(MatchMode.END);
Session session = HibernateUtil.beginTransaction();
Criteria criteria = session.createCriteria(User.class);
criteria.add(example); List results = criteria.list();
for (int i = 0; i<results.size(); i++) {
System.out.println(results.get(i).toString());
}
HibernateUtil.commitTransaction();
}
When I run the main method coded above, I get the following results, which are correctly filtered based on the .com suffix at the end of each user's email address:
1 : mj : abc123 : mj@mcnz.com
3 : sk8trgrl : password : avril@scja.com
5 : kerrr : pwd : sheehan@princessjava.com
6 : astra : pwd : rabbit@princessjava.com
8 : stephen : low : stanley@pulpjava.com
10 : remy : password : rabbit@scja.com
org.hibernate.criterion.MatchMode
The MatchMode class has four properties that allow you to set the fuzzy matching facilities for an Example instance. The default is EXACT, while ANYWHERE is the most generous. END and START are bookend properties that only allow matches at the front or at the end of a given property.
static MatchMode ANYWHERE
Match the pattern anywhere in the string
static MatchMode END
Match the end of the string to the pattern
static MatchMode EXACT
Match the entire string to the pattern
static MatchMode START
Match the start of the string to the pattern
Really Fuzzy Matches:
example.ignoreCase()
Obviously, the ANYWHERE property will generate the fuzziest matches out of the four MatchMode options, but to get really fuzzy matches, you might also want to ignore character casing. That is easily achieved by calling the ignoreCase() method on the example object before executing the query. Here's a sample of the ignoreCase() method in use, with the results of running the code snippet below:
User user = new User();
user.setPassword("PASS");
Example example = Example.create(user);
example.enableLike(MatchMode.ANYWHERE);
example.ignoreCase();
example.excludeProperty("verified");
2 : mario : pass : mario@scja.ca
3 : sk8trgrl : password : avril@scja.com
4 : ridley : mypassword : getbent@scja.ca
9 : che : password : ernesto@pulpjava.com
10 : remy : password : rabbit@scja.com
FindAll Query using the Criteria API
As you can see, the Criteria API is amazing at generating an SQL statement on the fly that filters out results based upon an example instance. However, what happens if you don't pass an Example instance to your criteria query? Well, if the query doesn't have any information on which to filter the query, which is essentially what the Example object does, then the criteria query will bring back all of the records based on the class the criteria instance is based upon. So, to find all of the User instances in your database, a criteria query would be as simple as this:
public class FindAll {
public static void main(String args[]) {
User user = new User();
Session session = HibernateUtil.beginTransaction();
Criteria criteria=
session.createCriteria(User.class);
/*Notice that there is no Example object!!!*/
List results = criteria.list();
HibernateUtil.commitTransaction();
for (int i = 0; i<results.size(); i++) {
System.out.println(results.get(i).toString());
}
}
}
Running the main method of the FindAll class brings back the following results, which is essentially, the entire database:
1 : mj : abc123 : mj@mcnz.com
2 : mario : pass : mario@scja.ca
3 : sk8trgrl : password : avril@scja.com
4 : ridley : mypassword : getbent@scja.ca
5 : kerrr : pwd : sheehan@princessjava.com
6 : astra : pwd : rabbit@princessjava.com
7 : cameron : 90210 : me@scwcd.com
8 : stephen : low : stanley@pulpjava.com
9 : che : password : ernesto@pulpjava.com
10 : remy : password : rabbit@scja.com
Pagination & Limiting the Results
Now, if your database has any size to it at all, you don't want to be executing too many findAll queries; actually, you don't want to be executing any. Not only is it a ridiculous load on your database, but end users typically have a hard time sifting through more than ten or twenty results at a time. So, it's no surprise to find out that the Criteria API makes it incredibly easy to limit the number of results that get delivered back from a query.
The Criteria object has two very helpful methods for facilitating the paging of results. The setFirstResult(int) method allows you to determine the index number at which the first result should be returned. The setMaxResults(int) method allows you to determine how many results will actually be returned from the query.
So, to get the first five results from the database, you would first pass the number zero to the setFirstResult(int) method, and pass 5 to the setMaxResults(int) method. This would return to you five results, with the first result being the first record in the database, and the last instance returned would map to the fifth record in the database. To get the next five records, you would issue a setFirstResults(5) invocation on the criteria object, and again, setMaxResults(int) to five, setMaxResults(5). It's all very simple . Here's how it looks in code:
Criteria criteria =
session.createCriteria(User.class);
criteria.setFirstResult(0);
criteria.setMaxResults(5);
List results = criteria.list();
The FindFirstFive Class
package com.examscam.criteria;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import com.examscam.HibernateUtil;
import com.examscam.model.User;
public class FindFirstFive {
public static void main(String args[]) {
User user = new User();
Session session = HibernateUtil.beginTransaction();
Criteria criteria = session.createCriteria(User.class);
criteria.setFirstResult(0);
criteria.setMaxResults(5);
List results = criteria.list();
HibernateUtil.commitTransaction();
for (int i = 0; i<results.size(); i++) {
System.out.println(results.get(i).toString());
}
}
}
Running this class against the sample database defined at the beginning of this chapter generates the following results, which as you can see, is the first five records in the database:
1 : mj : abc123 : mj@mcnz.com
2 : mario : pass : mario@scja.ca
3 : sk8trgrl : password : avril@scja.com
4 : ridley : mypassword : getbent@scja.ca
5 : kerrr : pwd : sheehan@princessjava.com
Ordering Results: The Order Class
A common requirement for any application is to have the results that are generated sorted by a particular column or attribute. Again, the Criteria API makes sorting your results extremely easy by providing you with an addOrder method in the Criteria class.
The addOrder method takes something called an Order object as an argument, which itself is a pretty straight forward component.
An Order object is created by using one of the two static methods in the Order class, namely the static asc(String) and desc(String) methods. Basically, you just chose whether you want your list to be sorted in ascending order, or descending order, and pass to the appropriate method the name of the field on which you want to sort. So, to sort all of my User's based on their loginName, in a descending order, I would create an Order object like so:
Order order = Order.asc("loginName");
Then, you just pass the order object to the Criteria object's addOrder method, and your results will come back ordered in exactly the manner that you have specified. The full class and query results are provided below:
User user = new User();
Session session = HibernateUtil.beginTransaction();
Criteria criteria = session.createCriteria(User.class);
Order order = Order.asc("loginName");
criteria.addOrder(order);
List results = criteria.list();
HibernateUtil.commitTransaction();
for (int i = 0; i<results.size(); i++) {
System.out.println(results.get(i).toString());
}
The FindAllOrderedByLoginName Class
package com.examscam.criteria;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import com.examscam.HibernateUtil;
import com.examscam.model.User;
public class FindAllOrderedByLoginName {
public static void main(String args[]) {
User user = new User();
Session session = HibernateUtil.beginTransaction();
Criteria criteria = session.createCriteria(User.class);
Order order = Order.asc("loginName");
criteria.addOrder(order);
List results = criteria.list();
HibernateUtil.commitTransaction();
for (int i = 0; i<results.size(); i++) {
System.out.println(results.get(i).toString());
}
}
}
Executing the main method of this class generates the following output, ordered ascending by the loginName field.
6 : astra : pwd : rabbit@princessjava.com
7 : cameron : 90210 : me@scwcd.com
9 : che : password : ernesto@pulpjava.com
5 : kerrr : pwd : sheehan@princessjava.com
2 : mario : pass : mario@scja.ca
1 : mj : abc123 : mj@mcnz.com
10 : remy : password : rabbit@scja.com
4 : ridley : mypassword : getbent@scja.ca
3 : sk8trgrl : password : avril@scja.com
8 : stephen : low : stanley@pulpjava.com
Using the Restrictions Class

Personally, I love the ability to do
queries by using the Criteria API's Example class. Equally powerful is
the Criteria API's Restrictions class.
As you know by now, simply passing the class type to the Criteria API, and executing a query, will return all of the database rows associated with that particular class. So, for example, the following code is basically a select
all query from the table associated with the User class:
Criteria criteria = session.createCriteria(User.class);
List results = criteria.list(); Now, instead of grabbing all of the User objects, you can use an instance of the Restrictions class to limit the results. So, lets just say you wanted to get all of the users who have an id that is greater than 5. To do that, you just create a Restrictions class, set a gt (greater than) restriction on the id field to the value of 5, and then add the restriction to the Criteria object. Once you execute your query, you'll only get records with an id that is greater than 5.
So, the following code generates the restricted results below:
Session session = HibernateUtil.beginTransaction();
Criteria criteria =
session.createCriteria(User.class);
criteria.add(Restrictions.gt("id", 5));
List results = criteria.list();
HibernateUtil.commitTransaction();
for (int i = 0; i<results.size(); i++) {
System.out.println(results.get(i).toString());
}
Notice the results are restricted to users with ids > 5.
6 : astra : pwd : rabbit@princessjava.com
7 : cameron : 90210 : me@scwcd.com
8 : stephen : low : stanley@pulpjava.com
9 : che : password : ernesto@pulpjava.com
10 : remy : password : rabbit@scja.com
The Restrictions Class Diagram
The Criterion Interface
When we invoke some of the more helpful static methods of the Restrictions class, such as gt, le, iLike or isNull, the objects returned from these methods all implement the Criterion interface. It is these types of objects, objects that implement the Criterion interface, that can be passed to the Criteria class' add method. And while all of our examples have really only added a single Restriction, or a single Example, the fact is, you can create very complex queries by passing multitudes of Criterion objects to the Criteria's add method. Take a look at the following query, and notice how many different Criterion objects are being used to create the custom query:
public static void main(String args[]) {
Session session = HibernateUtil.beginTransaction();
Criterion c1 = Restrictions.gt("id", (long)2);
Criterion c2 = Restrictions.lt("id", (long)8);
Criterion c3 = Restrictions.isNotNull("emailAddress");
User user = new User();
user.setEmailAddress(".com");
Example c4 = Example.create(user);
c4.enableLike(MatchMode.END);
c4.ignoreCase();
Criteria criteria = session.createCriteria(User.class);
criteria.add(c1);
criteria.add(c2);
criteria.add(c3);
criteria.add(c4);
List results = criteria.list();
HibernateUtil.commitTransaction();
for (int i = 0; i<results.size(); i++) {
System.out.println(results.get(i).toString());
}
}Running the query in this main method returns the following:
3 : sk8trgrl : password : avril@scja.com
5 : kerri : pwd : sheehan@princessjava.com
6 : astra : pwd : rabbit@princessjava.com
7 : cameron : 90210 : me@scwcd.com The Criterion Interface and Some Implementing Classes
findByCriterion(Criterion: criterion)
Because the Criterion object is so adaptable and comprehensive, it is very common to see the core classes involved in an application have their own findByCriterion method, either defined in a Data Access Object (DAO)Class, or even just a custom Helper class. A very elegant implementation of this method that leverages the varargs and for:each loop features of Java 5 is as follows:
package com.examscam.criteria;
import java.util.List;import org.hibernate.*;
import org.hibernate.criterion.*; import com.examscam.model.User;
import com.examscam.HibernateUtil;
public class UserHelper {
public static List findByCriterion(Criterion... criterion){
Session session = HibernateUtil.getSession();
Criteria criteria=session.createCriteria(User.class);
for (Criterion crit : criterion) {
criteria.add(crit);
}
return criteria.list();
}
}
So, with such a method, an application could obtain a very specific List of Users by passing many criterion instances to the UserHelper's findByCriterion method:
List specificUsers = UserHelper.findByCriterion(
Restrictions.gt("id", (long)2),
Restrictions.lt("id", (long)8),
Restrictions.isNotNull("emailAddress")
);
A findAll using findByCriterion()
On the other hand, a user could find all of the User instances in the database by invoking the findByCriterion method, but without passing in any criterion objects at all:
List allUsers = UserHelper.findByCriterion();
Indeed, leveraging the Criteria API in will greatly improve your ability to create and manage all of your query centric applications.
Use it. You'll love it! |