DatabaseUsing Criteria in Hibernate for Advanced Queries

Using Criteria in Hibernate for Advanced Queries

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

In Hibernate, the Criteria API helps us build criteria query objects dynamically. Criteria is a another technique of data retrieval apart from HQL and native SQL queries. The primary advantage of the Criteria API is that it is intuitively designed to manipulate data without using any hard-coded SQL statements. Programmatic behavior offers compile-time syntax checking; as a result, most error messages are delivered during compilation. Although it’s convenient, this does not provide any performance enhancing ability over HQL or native SQL queries. In fact, it’s a preference given to developers to choose according to taste and necessity. You may use all three or stick to one. Let’s see if we can find some examples to appreciate the Criteria Query API.

Getting Started with Hibernate Criteria

The org.hibernate.Criteria interface defines several methods for creating query objects programmatically. We generally use a Session object to call the createCriteria() method and create a Criteria object. Before going hands on, let’s create some utility classes to test the examples down the line.

//...import statements
@Table(name = "PRODUCT")
@Entity
public class Product implements Serializable {
   private static final long serialVersionUID = 1L;
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private int productId;
   private String name;
   private String description;
   private double price;
   @ManyToOne
   private Supplier supplier;

   //...constructors, getters and setters, toString
}

Listing 1: Product.java

//...import statements
@Table(name = "SUPPLIER")
@Entity
public class Supplier implements Serializable {
   private static final long serialVersionUID = 1L;
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private int supplierId;
   private String name;
   @OneToMany
   private List<Product> products = new ArrayList<>();

   //...constructors, getters, and setters
}

Listing 2: Supplier.java

//...import statements
public class HibernateUtil {
   private static final SessionFactory sessionFactory;
   private static final StandardServiceRegistry serviceRegistry;
   static {
      try {
         Configuration config =
            new Configuration().configure("hibernate.cfg.xml");
         serviceRegistry = new StandardServiceRegistryBuilder()
            .applySettings(config.getProperties()).build();
         sessionFactory = config.buildSessionFactory(serviceRegistry);
      } catch (Throwable ex) {
         System.err.println("Initial SessionFactory creation failed." + ex);
         throw new ExceptionInInitializerError(ex);
      }
   }
   public static Session openSession() {
      return sessionFactory.openSession();
   }
Note: Probably, there is a bug in Hibernate 4.3.5. SessionFactory does not end even if the program stops. The stopConnectionProvider() method generally is not needed. If you face the same problem, use the following code to shut down the session factory explicitly.
   public static void stopConnectionProvider() {
      final SessionFactoryImplementor sessionFactoryImplementor =
         (SessionFactoryImplementor) sessionFactory;
      ConnectionProvider connectionProvider =
         sessionFactoryImplementor.getConnectionProvider();
      if (Stoppable.class.isInstance(connectionProvider)) {
         ((Stoppable) connectionProvider).stop();
      }
   }
}

Listing 3: HibernateUtil.java

//...import statements
public class Main {
   public static void main(String[] args) {
      Session session = HibernateUtil.openSession();
      session.beginTransaction();
      Supplier s1 = new Supplier("Phoenix");
      Product p1 = new Product("INC 100", "Neonatal
         intensive care incubator", 7890.55, s1);
      Product p2 = new Product("TINC 101", "Transport
         incubator", 9916.45, s1);
      Product p3 = new Product("INC 200", "Neonatal
         intensive care incubator 200", 8719.75, s1);
      s1.getProducts().add(p1);
      s1.getProducts().add(p2);
      s1.getProducts().add(p3);
      session.save(p1);
      session.save(p2);
      session.save(p3);
      session.save(s1);
      session.close();
      HibernateUtil.stopConnectionProvider();
   }
}

Listing 4: Main.java

<?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE hibernate-configuration PUBLIC
      "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
      "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
   <session-factory>
      <!-- Database connection settings -->
      <property name="connection.driver_class">
         com.mysql.jdbc.Driver</property>
      <property name="connection.url">
         jdbc:mysql://localhost:3306/mydatabase</property>
      <property name="connection.username">user1</property>
      <property name="connection.password">secret</property>
      <!-- JDBC connection pool (use the built-in) -->
      <property name="connection.pool_size">1</property>
      <!-- SQL dialect -->
      <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
      <!-- Echo all executed SQL to stdout -->
      <property name="show_sql">true</property>
      <!-- <property name="hbm2ddl.auto">create</property> -->
      <!-- Mapping files -->
      <mapping class="org.example.entity.Product" />
      <mapping class="org.example.entity.Supplier" />
   </session-factory>
</hibernate-configuration>

Listing 5: hibernate.cfg.xml

Simplest Criteria

We can use the following code to create a criteria object. This criteria will return the list of suppliers available in the database.

Criteria criteria=session.createCriteria(Supplier.class);
List<Supplier> suppliers=criteria.list();

Once this is run, we get a list of the Supplier instance. Then, we can loop through the list to get its details, such as:

for(Supplier s: suppliers){
   System.out.println(s.toString);
}

Criteria with Restrictions

To winnow down the result of our queries, we can add some restriction by using the static methods provided by the org.hibernate.criterion.Restrictions class. We can set one or more constraints to narrow down the result to a Criteria object with the add method. For example, we can use the eq and/or like method to retrieve objects that have a property value that equals our restriction.

Criteria c2 = session.createCriteria(Product.class);
c2.add(Restrictions.like("name", "INC"));
c2.add(Restrictions.like("description","%Transport%"));

Pattern matching also can be done with enumeration provided by the the rg.hibernate.criterion.MatchMode object, such as:

  • START: Matches the beginning of the string
  • END: Matches the end of the string
  • EXACT: Matches exactly the string
  • ANYWHERE: Matches any part of the string
Criteria c2 = session.createCriteria(Product.class);
c2.add(Restrictions.ilike("description","incubator",
   MatchMode.END));

ilike() is a case-insensitive variation of like. To retrieve NULL values from the database, the isNull() restriction should be used.

Criteria c2 = session.createCriteria(Product.class);
c2.add(Restrictions.isNull("description"));

To find products whose price is greater than, say $8000, use this:

Criteria c2 = session.createCriteria(Product.class);
c2.add(Restrictions.gt("price", new Double(8000.0));

We can write more complicated queries by using the logical AND OR constraints, such as

Criteria c2 = session.createCriteria(Product.class);
c2.add(Restrictions.gt("price", new Double(8000.0));
c2.add(Restrictions.ilike("description","incubator", MatchMode.END));
2.add(Restrictions.like("name", "IN%"));
LogicalExpression logicOr=Restrictions.or("name", "description");
c2.add(logicOr);
List<Product> list = c2.list();

Result Set Paging

A result set returned by the database query can be set for pagination. Pagination sets the limit of our view in the list of the result. We further can navigate forward and back through the results. This is particularly useful for performance reasons, especially when a query returns a large list of data.

Criteria c2 = session.createCriteria(Product.class);
c2.setFirstResult(10);
c2.setMaxResults(50)

Sorting Query Results

We often need to sort the list of data returned by the result set. We do it as follows:

Criteria c2 = session.createCriteria(Supplier.class);
c2.addOrder(Order.desc("name"));

Obtaining a Unique Result

To obtain a unique result from the result set, use this code:

Criteria c2 = session.createCriteria(Product.class);
c2.add(Restrictions.le("price",new Double(8000.0)));
c2.setMaxResults(1);
Product product=(Product)c2.uniqueResult();

Associations

We can join two or more associated classes to obtain a result. The association can be one-to-one, one-to-many, or many-to-one. Suppose we want to find the name of the Suppliers who sell Product with a price over $7000. The query would be as follows:

Criteria c2 = session.createCriteria(Supplier.class);
Criteria c3 = c2.createCriteria("products");
c3.add(Restrictions.ge("price",new Double(7000.0)));
List<Supplier> list = c2.list();

Projections and Aggregates

Hibernate supports properties, aggregate functions, and the GROUP BY clause. The Projection class is much like Restriction. It also provide several static factory methods to obtain projection instances. The following example shows some of the aggregate functions along with projection.

Criteria c2 = session.createCriteria(Product.class);
ProjectionList projectionList=Projection.projectionList();
projectionList.add(Projections.min("price"));
projectionList.add(Projections.max("price"));
projectionList.add(Projections.avg("price"));
projectionList.add(Projections.countDistinct("name"));
c2.setProjection(projectionList);
List<Supplier> list = c2.list();

QBE: Query By Example

In QBE, we can populate an instance and use it as a template. The values contained in the instance then can be used to build the criteria. In Hibernate QBE, functionality is contained in the org.hibernate.criterion.Example class. It is convenient on some occasions where we can create a partial object and send it to the criteria rather than sending values as its parameters.

Suppose we want to match the names of Supplier. Instead of sending the name, what we can do is create a Supplier object and pass it to the criterion, as follows:

Criteria c2 = session.createCriteria(Supplier.class);
Supplier supplier=new Supplier();
supplier.setName("Phoenix");
c2.add(Example.create(supplier));
List<Supplier> list = c2.list();

QBE is very convenient for searches where the criterion is built from user input. It is best for advanced searches where multiple fields are at play.

Conclusion

The Criterion API is one of the best parts of Hibernate. Unless you have worked with this API, you cannot appreciate its power. The syntax is simple and very intuitive. Although HQL is not difficult, it however, can be easily understood why some developers prefer the Criteria Query API. I put it way above HQL and native SQL queries because it offers compile-time syntax checking, convenience of use, is simple, and has a very intuitive learning curve.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories