0% found this document useful (0 votes)
5 views

Module 07 - Hibernate HQL

Uploaded by

surafelbehailu90
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

Module 07 - Hibernate HQL

Uploaded by

surafelbehailu90
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 43

Module 07: Hibernate Query

Language
CS544: Enterprise Architecture

© 2014 Time2Master 1
Retrieving Entities
 So far we’ve discussed how to:
 Use load() or get() to find an entity by its ID
 Access related entities from loaded entities

 But what if you don’t know an entity’s ID?


 Hibernate offers several ways to query the DB
1. Using the Hibernate Query Language (HQL)
2. Using the programmatic Criteria API
3. Using Native SQL Queries in your DBs SQL dialect

© 2014 Time2Master 2
Hibernate Query Language
 HQLs syntax is relatively similar to SQL, but is
object oriented instead of relational
 HQL understands objects and attributes
 HQL understands associations between objects
 HQL understands inheritance and polymorphism

List<Account> accounts = session.createQuery("from Account act "


+ "where act.class <> CheckingAccount "
+ "and act.owner.firstname = 'Frank'").list();

HQL query that retrieves a list


of accounts that are not
checking accounts and whose
owners first name is Frank

© 2014 Time2Master 3
Hibernate Query Language:

THE QUERY OBJECT

© 2014 Time2Master 4
The HQL Query Object
 Basics of using HQL Queries:
 Create a HQL Query: session.createQuery()
Query query = session.createQuery("from Person");

 Retrieve a list of objects: query.list()


session = sessionFactory.openSession(); Queries should also be
tx = session.beginTransaction(); executed inside transactions

Query query = session.createQuery("from Person"); Create query


List<Person> people = query.list();
Execute query
tx.commit();

© 2014 Time2Master 5
Named Queries
 Named HQL queries can be stored inside mapping data
 Can then be retrieved by name for execution
 Separates HQL query strings from Java code
 Can also be used to organize queries in one location
XML mapping:
<hibernate-mapping package="entities">
<class name="Person">
...
Name needs to be Globally Unique
</class>
<query name="Person.Everybody"><![CDATA[ Use CDATA tags around the query
from Person to make sure < and > comparison
]]></query> operators do not conflict with XML
</hibernate-mapping>

Annotation mapping:
@Entity
@NamedQueries({
Optional @NamedQueries can be used to specify multiple @NamedQuery
@NamedQuery(name="Person.Everybody", query="from Person")
})
public class Person { Globally Unique name
@Id
@GeneratedValue
private int id;
...
© 2014 Time2Master 6
Using Named Queries
 Retrieve a named query by its name
 Then use it like any other query
session = sessionFactory.openSession(); Query defined
tx = session.beginTransaction();
in metadata
Query query = session.getNamedQuery("Person.Everybody");
List<Person> people = query.list(); Executed like a normal query
for (Person p : people) {
System.out.println(p.getFirstname() + " " + p.getLastname());
}

tx.commit();

© 2014 Time2Master 7
Hibernate Query Language:

THE FROM CLAUSE

© 2014 Time2Master 8
From Clause
 The simplest HQL query only contains a ‘from’
clause – the select clause is optional*
List<Person> people = session.createQuery("from Person").list();

Returns all objects of the


Person class in the database

 HQL keywords are not case sensitive


 FROM, from, FrOm, and fRoM are the same keyword
 Class and property names are case sensitive
 Person and person are two different classes!

© 2014 Time2Master 9
Polymorphic Queries
 HQL has very solid polymorphism support
 You can query for mapped inheritance hierarchies
 Will return all accounts regardless of sub-type
List<Account> accounts = session.createQuery("from Account").list();

 For classes that implement any interface


 Will return all objects that implement the interface
Query q = session.createQuery("from java.io.Serializable");
List<Object> objects = q.list();

 Or even for every any object what so ever


 Be careful – Will return every object in the database
Query q = session.createQuery("from java.lang.Object");
List<Object> objects = q.list();

© 2014 Time2Master 10
Aliases
 You can alias class names for ease of reference
 Just like SQL allows you to alias tables names
List<Person> people = session.createQuery("from Person as p").list();

 Just like SQL the ‘as’ keyword is optional


List<Person> people = session.createQuery("from Person p").list();

 We will demonstrate this feature more in the


queries later on in this module

© 2014 Time2Master 11
Pagination
 HQL has build in pagination support to select
part of a large result set
 Crucial feature when users need to work with
large result sets (like a ‘from Person’ query)
Query query = session.createQuery("from Person");

query.setFirstResult(0); Zero based index – 0 is the beginning


query.setMaxResults(50); Page size
List<Person> people = query.list(); Returns the first 50 people
for (Person p : people) {
System.out.println(p.getFirstname() + " " + p.getLastname());
}

© 2014 Time2Master 12
Order By
 The ‘order by’ clause sorts the list in a
particular order Order people by lastname
Query query = session.createQuery("from Person p order by p.lastname");
List<Person> people = query.list();
for (Person p : people) {
System.out.println(p.getFirstname() + " " + p.getLastname());
}

 The ‘asc’ and ‘desc’ keywords can be added to


specify ascending or descending order
Query query = session.createQuery("from Person p order by p.lastname desc");
List<Person> people = query.list();
for (Person p : people) { By lastname descending
System.out.println(p.getFirstname() + " " + p.getLastname());
}

© 2014 Time2Master 13
Hibernate Query Language:

THE WHERE CLAUSE

© 2014 Time2Master 14
The Where Clause
 The where clause lets you add constraints to
the result set, refining the list Select all person objects
whose first name is John
Query query = session.createQuery("from Person where firstname = 'John'");
List<Person> people = query.list();
for (Person p : people) {
System.out.println(p.getFirstname() + " " + p.getLastname());
}

 HQL supports the same expressions as SQL in


addition to several OO specific expressions
Query query = session.createQuery("from Person p where p.accounts[0].id = 3");
List<Person> people = query.list();
Select all people whose
first account id is 3

© 2014 Time2Master 15
HQL Expressions
Type Operators
Literals ‘string’, 128, 4.5E+3, ‘yyyy-mm-dd hh:mm:ss’
Arithmetic +, -, *, /
Comparison =, <>, >=, <=, !=, like
Logical and, or, not
Grouping (, )
Concatenation ||
Values in, not in, between, is null, is not null, is empty, is not empty
Case case … when … then ... else … end, case when … then … else … end

Type Functions
Temporal current_date(), current_time(), current_timestamp(), second(…),
minute(…), hour(…), day(…), month(…), year(…)
String concat(… , …), substring(), trim(), lower(), upper(), length(), str()
Collection Index(), size(), minindex(), maxindex()
© 2014 Time2Master 16
Indexed Collection Expressions
 HQL provides the [] expression syntax for
accessing elements of indexed collections
Query query = session.createQuery("from Person p where p.accounts[0].id = 3");
List<Person> people = query.list();
Accounts is mapped as List

 Both Lists and Maps can be accessed


Query query = session.createQuery("from Person p where p.pets['Mimi'].species = 'Cat'");
List<Person> people = query.list();
Retrieves all the people Map of pets using the
who have a cat named Mimi pet name as its key

© 2014 Time2Master 17
Query Parameters
 It is considered very bad practice to use
concatenation to insert values into Queries
 Opens the door for HQL (SQL) injection
 Makes query messy and less readable
Makes messy code, and breaks
String firstname = "John"; if there is a ‘ inside firstname
Query query = session.createQuery(
"from Person where firstname = '"+firstname+"'"); Don’t try this at home

 Named parameters should be used instead


String firstname = "John";
Query query = session.createQuery("from Person where firstname = :first");
query.setParameter("first", firstname);
Much cleaner, and can be
used with named queries
stored in XML / annotations

© 2014 Time2Master 18
Setting Parameters
 Hibernate provides two ways for setting
parameters on queries
1. The setParameter(name, param, [type]) method
String firstname = "John";
Query query = session.createQuery("from Person where firstname = :first");

query.setParameter("first", firstname, Hibernate.STRING);

List<Person> people = query.list(); Type argument is optional,


except for temporal types

2. The setType(name, param) methods


String firstname = "John";
Query query = session.createQuery("from Person where firstname = :first");

query.setString("first", firstname); Type is specified by the method name


List<Person> people = query.list();

© 2014 Time2Master 19
Positional Parameters
 Hibernate also supports positional parameters
 Similar to JDBC using ? to indicate a parameter
First parameter Second parameter
Query query = session.createQuery(
"from CheckingAccount where balance < ? and overdraftLimit > ?");
query.setParameter(0, 1000.0);
query.setParameter(1, 100.0); Set parameters, 0 for the
List<Account> accounts = query.list(); first, 1 for the second

 The use of positional parameters is not recommended


1. Less self documenting than named parameters
2. More brittle to change than named parameters

© 2014 Time2Master 20
Unique Result
 Use query.uniqueResult() if you are certain
that the result will only contain a single object
 Throws NonUniqueResultException if the resultset
contains more than one object
 Method returns null if the result set is empty
Query query = session.createQuery("from Person");
query.setMaxResults(1); MaxResults = 1,
Person p1 = (Person) query.uniqueResult();
guarantees unique result

Person p = (Person)session.createQuery("from Person where id = 1").uniqueResult();

Specific Id guarantees
unique result

© 2014 Time2Master 21
Special Attribute: id
 Even if you did not call your identity column
id, you can still refer to it in HQL as id*
Query query1 = session.createQuery("from Employee where employeeId = 1");
Employee e1 = (Employee)query1.uniqueResult();
System.out.println(e1.getFirstname() + " " + e1.getLastname());
Both queries
Query query2 = session.createQuery("from Employee where id = 1");
Employee e2 = (Employee)query2.uniqueResult();
produce the
System.out.println(e2.getFirstname() + " " + e2.getLastname()); same result

@Entity
public class Employee {
@Id Id property
@GeneratedValue “employeeId”
private int employeeId;
private String firstname; Hibernate Output:
private String lastname;
@Temporal(TemporalType.DATE) Hibernate: /* from Employee where employeeId = 1 */ ...
private Date birthdate; Jim Grove
Hibernate: /* from Employee where id = 1 */ ...
... Jim Grove

© 2014 Time2Master 22
Special Attribute: class
 Hibernate also provides a special attribute
that allows you to check the class of an object
List<Account> accounts = session.createQuery("from Account act "
+ "where act.class <> CheckingAccount "
+ "and act.owner.firstname = 'Frank'").list();

© 2014 Time2Master 23
Hibernate Query Language:

JOINS

© 2014 Time2Master 24
Joins
 Hibernate supports implicit and explicit joins
 Explicit joins use the join keyword
 Has to explicitly state which class to select
Join keyword
Query query = session.createQuery(
Select p not a "select p from Person p join p.address a where a.zip = :zip");
query.setParameter("zip", "90009");
List<Person> people = query.list();

 An implicit join uses the dot notation to join an


object of another class without using ‘join’
Query query = session.createQuery("from Person p where p.address.zip = :zip");
query.setParameter("zip", "90009");
List<Person> people = query.list(); Joins Address

People often make mistakes


with this on the exams
© 2014 Time2Master 25
Inner Joins
 Inner joins require that both objects exist –
unlike outer joins where one can be null
 If one is null, the potential result is discarded
 Implicit joins are always inner joins
 Just using the ‘join’ keyword signifies an inner join
 All previous join examples where inner joins

Query query = session.createQuery("from Person p where p.address.zip = :zip");


query.setParameter("zip", "90009");
List<Person> people = query.list();
Person has to have an address, a
person without an address would
not be included in the result set

© 2014 Time2Master 26
Outer Joins
 Outer joins allow one side to be null
 A left outer join allows the joined object to be null
Query query = session.createQuery("from Person p left outer join p.address a ");
List<Object[]> peopleAddr = query.list();
Outer keyword
is optional
Resultset:
John Willis is included
even though his address Frank Brown Chicago, Illinois
reference was null John Willis
Marry Doe Los Angeles, California

 A right outer join allows the initial side to be null


Query query = session.createQuery("from Person p right outer join p.address a ");
List<Object[]> peopleAddr = query.list();
Outer keyword
is optional
Resultset:
New York is included Frank Brown Chicago, Illinois
even though we don’t Marry Doe Los Angeles, California
New York, New York
have a person there
© 2014 Time2Master 27
Joining a Collection
 You have to use explicit joins for collections:
Query query = session.createQuery(
"from Person p join p.numbers n where n like :number");
query.setParameter("number", "641%");
List<Person> people = query.list();

 If you don’t include a select clause you get:


Person Number

firstName: John, lastName: Brown type: home, number: 641-472-1234

firstName: John, lastName: Brown type: mobile, number: 641-919-1234

firstName: Alice, lastName: Long type: mobile, number: 641-233-1234

firstName: Alice, lastName: Long type: home, number: 641 469-1234

© 2014 Time2Master 28
Only Persons
 You have to use explicit joins for collections:
Query query = session.createQuery(
"select p from Person p join p.numbers n where n like :number");
query.setParameter("number", "641%");
List<Person> people = query.list();

 Although we only have persons objects, we


still have duplicates: Person
firstName: John, lastName: Brown

firstName: John, lastName: Brown

firstName: Alice, lastName: Long

firstName: Alice, lastName: Long

© 2014 Time2Master 29
Distinct Keyword
 The distinct keyword removes duplicates

Query query = session.createQuery(


"select distinct p from Person p join p.numbers n where n like :number");
query.setParameter("number", "641%");
List<Person> people = query.list();

Person

firstName: John, lastName: Brown

firstName: Alice, lastName: Long

© 2014 Time2Master 30
Fetch Joins
 Another feature of Hibernate joins is the
ability to eagerly fetch collections
 This is done by outer joining the collection in the
sql statement

 For instance if we want to pre-load all the


accounts associated with a person
Query query = session.createQuery("from Person p left join fetch p.accounts where p.id = 1");
Person p = (Person)query.uniqueResult();

If you do not outer join, people Loads person and also pre-caches
without an account will be omitted all associated accounts

© 2014 Time2Master 31
Join Fetch Considerations
 You can not constrain a join fetch-ed collection
 E.g. “from Person p join fetch accounts a where a = …”
is an invalid query
 When eager fetching, it is possible that duplicate
initial objects occur due to the outer join
 Duplicates can be removed by using ‘select distinct’
 Do not fetch more than one collection in parallel,
doing so will create a Cartesian product*
 You can not use pagination with eager fetching

© 2014 Time2Master 32
Hibernate Query Language:

THE SELECT CLAUSE

© 2014 Time2Master 33
Selecting
 The select clause specifies which entities and
or properties the query should return
@Entity
public class Book {
A result can contain: @Id
@GeneratedValue

 Entities What we’ve done so far


private int id;
private String title;
private String author;
 Properties private double price;
@Temporal(TemporalType.DATE)
private Date publish_date;
 A mix of the two ...

Selecting a property: Only selects the book title


Query query = session.createQuery("select b.title from Book b");
List<String> titles = query.list();
for (String title: titles) {
System.out.println(title); Returns a list of Strings
}

© 2014 Time2Master 34
Selecting Multiple Items
 Using select you can also specify more than
one entity and / or property
 By default these will be returned as an Object[]
Query query = session.createQuery( Select an entity and two properties
"select person, pet.species, adr.city "
+ "from Pet pet join pet.owner person "
+ "join person.address adr ");
List<Object[]> items = query.list(); Result set is a List<Object[]>

Person p = null; String petType = null; String city = null;


for (Object[] item : items) {
p = (Person) item[0]; petType = (String) item[1]; city = (String) item[2];

System.out.println(p.getFirstname() + " " + p.getLastname()


+ " owns a " + petType + " in " + city);
}

 Alternately you can have the result formatted as


java.util.List, java.util.Map or even a new Object
© 2014 Time2Master 35
Select – List()
 You can format a mixed result as a java.util.List
instead of an Object[] using “new list()”
Select new list(… , … , …)
Query query = session.createQuery(
"select new list(person, pet.species, adr.city) "
+ "from Pet pet join pet.owner person "
+ "join person.address adr ");
List<List<Object>> items = query.list(); Returns a List<List<Object>>

Person p = null; String petType = null; String city = null;


for (List<Object> item : items) {
p = (Person) item.get(0);
petType = (String) item.get(1);
city = (String) item.get(2);

System.out.println(p.getFirstname() + " " + p.getLastname()


+ " owns a " + petType + " in " + city);
}

© 2014 Time2Master 36
Aliases and Maps
 The select clause also allows you to specify
aliases for selected values
 This is only really useful when using Maps
new map(.., …, …)
Query query = session.createQuery(
"select new map(p as person, sum(a.balance) as liquid) "
+ "from Person p join p.accounts a group by p.id "); Returns List<Map<String, Object>>
List<Map<String,Object>> items = query.list();

Person p = null; Double liquid = null; Aliased values become map keys
for (Map<String,Object> item : items) {
p = (Person)item.get("person"); liquid = (Double)item.get("liquid");

System.out.println(p.getFirstname() + " " + p.getLastname()


+ "'s liquid assets: " + liquid);
}

© 2014 Time2Master 37
As New Object
 Results can even be formatted as new objects,
provided the required constructor exists
 Mapped classes can use their simple class name
 Unknown classes need Fully Qualified Domain Name
Fully Qualified Domain Name
Query query = session.createQuery( used: new entities.Home(p, a)
"select new entities.Home(p, a) " No annotations or
+ "from Person p " XML configuration,
+ "join p.address a "); class is unknown to
List<Home> homes = query.list(); Returns a package entities; Hibernate
List<Home>
Person p = null; Address a = null; public class Home {
for (Home home : homes) { private Person person;
p = home.getPerson(); private Address address;
a = home.getAddress();
public Home(Person p, Address a) {
System.out.println(p.getFirstname() this.person = p;
+ " " + p.getLastname() this.address = a; Needs to have
+ " has a home in "+ a.getCity()); } the constructor
} used in the query
...
© 2014 Time2Master 38
Aggregates
 HQL also provides aggregate functions:
 avg(…), sum(…), min(…), max(…)
 count(*), count(…), count(distinct …), count(all …)
Sum of all account balances
Query query = session.createQuery(
"select new map(p as person, sum(a.balance) as liquid) "
+ "from Person p join p.accounts a group by p.id ");

 Group by clause specifies groups to aggregate


Group by person.id – in other
Query query = session.createQuery(
words: a group is all the
"select new map(p as person, sum(a.balance) as liquid) "
+ "from Person p join p.accounts a group by p.id "); accounts for each person

 The having clause can filter groups


Query query = session.createQuery(
"select new map(p as person, sum(a.balance) as liquid) "
+ "from Person p join p.accounts a group by p.id " Only show groups with a
+ "having sum(a.balance) > 100 "); balance greater than 100
© 2014 Time2Master 39
Hibernate Query Language:

BULK UPDATES / DELETES

© 2014 Time2Master 40
Bulk Operations
 HQL also supports bulk updates and deletes
 Similar to the DML features of SQL
Bulk Update:
Query query = session.createQuery("update Account set balance = balance - :fee");
query.setParameter("fee", 5.0);
int updated = query.executeUpdate(); Apply a fee to all accounts

Bulk Delete:
Query query = session.createQuery("delete Book where publish_date < :date");
query.setParameter("date", df.parse("01/01/2002"));
int deleted = query.executeUpdate(); Delete all old books

© 2014 Time2Master 41
Active Learning
 If a person can have many cars; write an HQL
query that selects everyone that owns a car that
has color = silver.

 Assuming we have a ‘Person’ class with a ‘name’


property that is mapped to `Student` table with a
`FullName` column. What would be the HQL
query to select everyone whose name starts with
an ‘M’?

© 2014 Time2Master 42
Module Summary
 In this module we covered the different aspects
of the Hibernate Query Language
 Overall HQL is very similar to SQL
 The minimal requirement for a query is a from clause
 The where clause can be used to refine the result set
 You can implicitly or explicitly join other tables
 Using the select clause you can define what should be
returned and in which format it should be returned
 HQL can also be used for bulk updates and deletes

© 2014 Time2Master 43

You might also like