Chapter 8. HQL and JPA Query Language

When you use JDBC to access databases, you write SQL statements for the query and update tasks. In such cases, you're dealing with tables, columns, and joins. When you use Hibernate, most update tasks can be accomplished through the APIs provided by Hibernate. However, you still need to use a query language for the query tasks. Hibernate provides a powerful query language called Hibernate Query Language (HQL).

HQL is database independent and translated into SQL by Hibernate at runtime. When you write HQL, you can concentrate on the objects and properties without knowing much detail about the underlying database. You can treat HQL as an object-oriented variant of SQL.

In this chapter, you see how to query the database using HQL and JPA Query Language (JPA QL). Hibernate basically provides three ways to query a database:

  • HQL and JPA QL, which is a subset of HQL

  • Criteria API (Criteria and Example)

  • Direct SQL

This chapter looks at HQL, JPA QL, and Direct SQL. You see how to form a query, bind parameters (named and position), and execute the query. You learn how to manipulate the result, which can help you deal with large result sets. You also query associated objects.

Using the Query Object

Problem

How do you create a Query object in hibernate? How do you enable pagination? What are the various ways to bind parameters to a query?

Solution

A Query is an object-oriented representation of an actual query. The Hibernate interface org.hibernate.Query and the JPA interface javax.persistence.Query provide methods to control the execution of a query and bind values to query parameters. In Hibernate, you can obtain an instance of the Query object by calling Session.createQuery(). And in JPA, the EntityManager provide an instance of the Query object with a call like EntityManager.createQuery().

How It Works

Let's see how to use the Query object, starting with how to create it.

Creating a Query Object

In the previous chapters, you've seen some basic HQL statements for querying objects. For example, returning to the bookshop example, you can use the following HQL to query for all books, and then call the list() method to retrieve the result list that contains book objects:

Query query = session.createQuery("from Book");
List books = query.list();

The from Clause

Now, let's look at the from clause, which is the only required part of a HQL statement. The following HQL statement queries for books whose name is "Hibernate". Notice that name is a property of the Book object:

from Book
where name = 'Hibernate'

Or, you can assign an alias for the object. This is useful when you're querying multiple objects in one query. You should use the naming conventions for classes and instances in Java. Note that the as keyword is optional:

from Book as book
where book.name = 'Hibernate'

You can then integrate the alias with the query:

Query query = session.createQuery("from Book where book.name='Hibernate'");
List books = query.list();

You need to use the EntityManager to create a javax.persistence.Query instance in JPA QL. The select clause is required in Java persistence, as shown here:

public List<Book> readFromManager() {

    EntityManager manager = SessionManager.getEntityManager();
    EntityTransaction tran = manager.getTransaction();
    tran.begin();
    Query query = manager.createQuery("select b from Book b");
    List<Book> list = query.getResultList();
    return list;
  }

Note that you need to place the persistence.xml file in the META-INF folder. Also remember to place all the required jars in the classpath.

The where Clause

In HQL, you can use where clauses to filter results, just as you do in SQL. For multiple conditions, you can use and, or, and not to combine them. This is called applying restrictions:

from Book book
where book.name like '%Hibernate%' and book.price between 100 and 200

You can check whether an associated object is null or not by using is null or is not null:

from Book book
where book.publisher is not null

Notice that the null check can't be performed on a collection. So, if you have a query like the following, you get an exception from Hibernate:

from Book8_1 book
where book.chapters is not null

Hibernate provides the empty key word, which you can use to check if a collection is empty:

from Book8_1 book
where book.chapters is not empty

Let's say you want to retrieve all books published by the publishers "Apress" and "friendsOfED". In technical terms, you want to retrieve based on a property (publisher's name) of the association (publisher). For this kind of requirement, you can use implicit joins in the where clause. You create an implicit join by using the . dot operator:

from Book book
where book.publisher.name in ('Apress', 'friendsOfED')

Remember that for a collection association, if you reference it more than one time, you should use an explicit join to avoid duplicated joins. Explicit joins require the use of the join keyword. Querying using joins is described in detail later in this chapter.

Hibernate provides a function that lets you check the size of a collection. You can use the special property size or the special size() function. Hibernate uses a select count(...) subquery to get the size of the collection:

from Book book
where book.chapters.size > 10
from Book book
where size(book.chapters) > 10

Pagination

When the number of records returned by a query is large, it's desirable to retrieve only a subset of the actual number of records. The subset is usually equal to the page size (number of records); this feature is called pagination. The Hibernate and JPA Query interfaces provide two methods to enable pagination:

  • setFirstResult(int beginPoint): Sets the first row with the given beginPoint

  • setMaxResult(int size): Sets the returned result-set size

The implementation looks like this:

public List<Book> readFromManager() {

    EntityManager manager = SessionManager.getEntityManager();
    EntityTransaction tran = manager.getTransaction();
    tran.begin();
    Query query = manager.createQuery("select b from Book b");
    query.setFirstResult(5);
    query.setMaxResults(15);
    List<Book> list = query.getResultList();
    return list;
  }

In this example, the records starting with the fifth row and the next 15 records after that are retrieved.

Parameter Binding

You use parameter binding to bind values to the query parameters. Parameter binding makes the code look cleaner; the code is also safer. It isn't recommended that you inject user input into a query using string concatenation; this can lead to a security issue called SQL injection.

Hibernate and JPA support two types of parameter binding:

  • Named parameter binding

  • Positional parameter binding

You can specify query parameters the same way you do for SQL queries. If you're sure only one unique object will be returned as a result, you can call the uniqueResult() method to retrieve it. Null is returned if nothing matches:

query = session.createQuery("from Book where isbn = ?");
query.setLong(0, 520);
Book bookCh8 = (Book) query.uniqueResult();

This example uses ? to represent a query parameter and set it by index (which is zero-based, not one-based as in JDBC). This kind of parameter binding is called positional parameter binding. You can also use named parameter binding for queries. The advantages of using named parameters is that they're easy to understand and can occur multiple times:

Query query = session.createQuery("from Book where isbn =:isbn");
query.setLong("isbn", 520);
Book bookCh8 = (Book) query.uniqueResult();
System.out.println(bookCh8);

Named Queries

You can put query statements in any mapping definitions and refer them by name in the code. These are called named queries. But for easier maintenance, you should centralize all your named queries in one mapping definition, say NamedQuery.hbm.xml. In addition, setting up a mechanism for naming queries is also beneficial—you need to assign each query a unique name. You should also put the query string in a <![CDATA[...]]> block to avoid conflicts with special XML characters. Named queries don't have to be HQL or JPA QL strings—they can be native SQL queries:

<hibernate-mapping>
  <query name="Book.by.isbn">
    <![CDATA[from Book where isbn = ?]]>
  </query>
</hibernate-mapping>

To reference a named query, you use the session.getNamedQuery() method:

Query query = session.getNamedQuery("Book.by.isbn");
query.setString(0, "1932394419");
Book book = (Book) query.uniqueResult();

The JPA specifies the @NamedQuery and @NamedNativeQuery annotations. The name and query attributes are required. You can place these annotations in the metadata of a particular entity or into a JPA XML file:

@NamedQueries({
  @NamedQuery(
  name="Book.by.isbn",
  query="from Book where isbn = ?"
  )
})
@Entity (name="book")
@Table  (name="BOOK", schema="BOOKSHOP")
public class Book{...}

Using the Select Clause

Problem

How and where do you use the select clause?

Solution

The previous examples query for entire persistent objects. You can instead query for particular fields by using the select clause. The select clause picks which objects and properties to return in the query result set.

How It Works

The following query returns all the book names in a list:

select book.name
from Book book

You can use the SQL aggregate functions, such as count(), sum(), avg(), max(), and min(), in HQL. They're translated in the resulting SQL:

select avg(book.price)
from Book book

Implicit joins can also be used in the select clause. In addition, you can use the keyword distinct to return distinct results:

select distinct book.publisher.name
from Book book

To query multiple fields, use a comma to separate them. The result list contains elements of Object[]:

select book.isbn, book.name, book.publisher.name
from Book book

You can create custom types and specify them in the select clause to encapsulate the results. For example, let's create a BookSummary class for the bookIsbn, bookName, and publisherName fields. The custom type must have a constructor of all the fields:

public class BookSummary {
  private String bookIsbn;
  private String bookName;
  private String publisherName;
  public BookSummary(String bookIsbn, String bookName, String publisherName) {
    this.bookIsbn = bookIsbn;
this.bookName = bookName;
    this.publisherName = publisherName;
  }
  // Getters and Setters
}

select new BookSummary(book.isbn, book.name, book.publisher.name)
from Book book

The results can be encapsulated in collections such as lists and maps. Then, the query result is a list of collections. For example, if you use a list, the result is a List of List objects, as shown here:

String queryString = "select new list(book.isbn, book.name, book.publisher.name)
from Book book";
Query query = session.createQuery(queryString);
List books = query.list();

if(books!=null)
{
  System.out.println("Size- "+books.size());
  Iterator it = books.iterator();
  while(it.hasNext())
  {
    List book = (List)it.next();
    Iterator listIt = book.iterator();
    while(listIt.hasNext())
    {
      System.out.println("Inside book- "+listIt.next());
    }//end of second(inner) while loop
    System.out.println(" ");
  }//end of first while loop
}//end of if

For the map collection, you need to use the keyword as to specify the map key for each field. In the case of a map, the result is a List of Map objects:

String queryString =
  "select new map(
     book.isbn as bookIsbn, book.name as bookName, book.publisher.name as publisherName
   ) from Book book";
Query query = session.createQuery(queryString);
List books = query.list();

if(books!=null)
{
  System.out.println("Size- "+books.size());
  Iterator it = books.iterator();
  while(it.hasNext())
  {
    Map book = (Map)it.next();
    Set bookSet = book.keySet();
Iterator listIt = bookSet.iterator();
    while(listIt.hasNext())
    {
      String key = (String)listIt.next();
      System.out.println("Inside chapter{ Key- "+key+" },{ Value- "+book.get(key)+"}");
    }
      System.out.println(" ");
  }
}

Joining

Problem

How do you create various types of joins in HQL and JPA QL?

Solution

Hibernate and JPA support both inner and outer joins. You use the dot operator (.) to express implicit association joins.

How It Works

Let's look at all the different types of joins you can take advantage of.

Explicit Joins

In HQL, you can use the join keyword to join associated objects. The following query finds all the books published by the publisher "Apress". The result contains a list of object pairs in the form of an array of Objects(Object[]). Each pair consists of a book object and a publisher object:

from Book book join book.publisher publisher
where publisher.name = 'Apress'

In addition to many-to-one associations, all other kinds of associations can be joined. For example, you can join the one-to-many association from book to chapters. The following query finds all the books containing a chapter titled "Hibernate Basics". The result contains a list of object pairs; each pair consists of a book object and a collection of chapters:

from Book book join book.chapters chapter
where chapter.title = 'Hibernate Basics'

Implicit Joins

In the previous joins, you specify the join keyword to join associated objects. This kind of join is called an explicit join. You can also reference an association directly by name: this causes an implicit join. For example, the previous two queries can be expressed as follows. The result contains only a list of book objects, because no join is specified in the from clause:

from Book book
where book.publisher.name = 'Manning'

Note that implicit joins are directed along many-to-one or one-to-one associations. They're never directed along many-to-many associations. This means you can never use something like

String QueryString3 =
  "from Book8_1 book where book.chapters.title = 'Many-To-Many Association'";
Query query1 = session.createQuery(QueryString3);
List<Chapter8_1> chptrs = query1.list();

The correct usage is as follows:

String queryString =
  "from Book8_1 book join book.chapters chapter where chapter.title=:title";
Query query = session.createQuery(queryString);
query.setString("title", "Many-To-Many Association");
List books = query.list();

Outer Joins

If you use the following HQL to query for books joined with publishers, books with null publisher aren't included. This type of join is called an inner join, and it's the default if you don't specify a join type or if you specify inner join. It has the same meaning as an inner join in SQL:

from Book book join book.publisher

If you want to get all books regardless of whether the publisher is null, you can use a left join by specifying left join or left outer join:

from Book book left join book.publisher

HQL supports two other types of joins: right joins and full joins. They have the same meaning as in SQL but are seldom used.

Matching Text

The following HQL retrieves books and their associated chapters where at least one of the chapter titles includes the word Hibernate. The result contains pairs consisting of a book and a chapter:

from Book book join book.chapters chapter
where chapter.title like '%Hibernate%'

Fetching Associations

You can use join fetch to force a lazy association to be initialized. It differs from a pure join in that only the parent objects are included in the result:

from Book book join fetch book.publisher publisher

An inner join fetch query doesn't return book objects with a null publisher. If you want to include them, you must use left join fetch:

from Book book left join fetch book.publisher publisher

Creating Report Queries

Problem

How do you create queries that group and aggregate data?

Solution

You use the select clause to generate report queries. The groupby and having clauses are used for aggregation. You saw how to use the select clause earlier in this chapter.

How It Works

Let's see how to aggregate and group data.

Projection with Aggregation Functions

HQL and JPA QL support the aggregate functions count(), min(), max(), sum(), and avg(). If you want to know how many books are in your bookshop, you have to do something like this:

String q2 = "select count(i) from Book i";
Query query = session.createQuery(q2);
Long count = (Long) query.uniqueResult();

To find the minimum and maximum prices of the books in the bookshop, you can use a query like the following:

String q3 = "select min(i.price),max(i.price) from Book i";
Query query = session.createQuery(q3);
Object[] count = (Object[]) query.uniqueResult();
System.out.println("Minimum price-  "+count[0]);
System.out.println("Maximum price- "+count[1]);

Note that this query's return type is an Object[] array.

Grouping Aggregated Results

You can sort the result list with an order by clause. Multiple fields and ascending/descending order can be specified:

from Book book
order by book.name asc, book.publishDate desc

HQL also supports the group by and having clauses. They're translated into SQL by Hibernate:

select book.publishDate, avg(book.price)
from Book book
group by book.publishDate

select book.publishDate, avg(book.price)
from Book book group by book.publishDate
having avg(book.price) > 10

Summary

In this chapter, you've learned how to query a database using Hibernate Query Language. You've seen how to use the from, select, and where clauses that form the backbone of most queries. And you've learned how to bind parameters to the query parameters. You should be able to externalize queries by using named queries. If the result set size is large, you can use pagination to retrieve a subset of the original result set. You've learned to use the dot (.) operator to create implicit joins and the join keyword to create explicit joins. You've also seen how to use aggregate functions like min(), max(), avg(), count(), and sum().

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.70.247