08 Queries
- 2. Hibernate query options The Hibernate Query Language (HQL): The Criteria API for type-safe queries: Direct SQL with Hibernate mapping: session. createQuery (" from Category cat where cat.name like '%Flights' ").list(); session. createCriteria (Category.class) .add( Expression.like("name", "%Flights") ) .list(); session. createSQLQuery ( " select {cat.*} from CATEGORY {cat} where NAME like '%Flights' ", "cat", Category.class) .list();
- 3. Executing queries We execute queries with the Query object from createQuery() and createSQLQuery() Criteria object from createCriteria() Pagination: Getting a list or results, or a single result: Iterating: Query q = session.createQuery("from User user order by user.name asc") .setFirstResult(30) .setMaxResults(10) .list(); List result = q. list (); Object o = q.setMaxResults(1). uniqueResult (); Iterator it = q. iterate ();
- 4. Executing queries A ScrollableResults let’s us step through the query results, just like a JDBC scrollable ResultSet : ScrollableResults users = session.createQuery( "from User user order by user.name asc") .scroll(); while ( users.next() ) { User user = results.get(0); … } users.close();
- 5. Binding parameters We should never use String manipulation: We use named parameter binding : Positional parameters are not recommended: String queryString = "from Item i where i.description like '" + string + "'"; List result = session.createQuery(queryString).list(); String queryString = "from Item item " + "where item.description like :searchString " + "and item.date > :minDate "; List result = session.createQuery(queryString) . setString ("searchString", searchString ) . setDate ("minDate", minDate ) .list(); String queryString = "from Item item where item.description like ? "; Query q = session.createQuery(queryString). setString ( 0 , searchString);
- 6. More parameter binding techniques Use setEntity() to bind a persistent entity: Use setProperties() to bind properties of a JavaBean to several named parameters simultaneously: Use setParameterList() to bind multiple values: session.createQuery("from Item item where item.seller = :seller") .setEntity("seller", seller) ; Item item = new Item(); item. setSeller (seller); item. setDescription (description); String queryString = "from Item item " + "where item.seller = :seller and " + "item.description like :description "; session.createQuery(queryString). setProperties (item).list(); session.createQuery("from Item item where item.id in (:idList)") .setParameterList(“idList", idList) ;
- 7. Externalizing named queries We can execute a named query : The query is defined in the mapping metadata: We can also name SQL queries: Query q = session.getNamedQuery(" findItemsByDescription ") .setString("description", description); <query name=" findItemsByDescription "><![CDATA[ from Item item where item.description like :description ]]></query> <sql-query name=" findItemsByDescription "><![CDATA[ select {item.*} from item where description like :description ]]> <return alias="item" class="Item"/> </sql-query>
- 8. Basic queries in HQL and with Criteria The simplest query: Using an alias: Polymorphic queries: from Bid from BillingDetails from CreditCard from java.lang.Object // Returns all persistent objects! from Bid as bid session.createCriteria(Bid.class) session.createCriteria(BillingDetails.class)
- 9. Restriction Restriction with a where clause in HQL: Using a Criterion : All regular SQL operators are supported in expressions: from User user where user.email = 'foo@hibernate.org' Criterion emailEq = Expression.eq("email", "foo@hibernate.org); Criteria crit = session.createCriteria(User.class); crit.add( emailEq ); User user = (User) crit.uniqueResult(); from User user where user.email = 'foo@hibernate.org' and user.firstname like 'Max%' and user.lastname is not null or user.signupDate < :mondayLastWeek
- 10. String matching We can use wildcards for string pattern matching: The Criteria API allows string matching without string manipulation: We can also call arbitrary SQL functions in the where clause: from User user where user.firstname not like " %Foo B% " session.createCriteria(User.class) .add( Expression.like("firstname", "G", MatchMode.START ) ); from User user where lower(user.email) = 'foo@hibernate.org' session.createCriteria(User.class) .add( Expression.eq("email", "foo@hibernate.org"). ignoreCase() ) );
- 11. Ordering results The order by clause is used in HQL: The Criteria API also supports ordering of results: HQL and the Criteria API give you all the power of SQL without falling back to table and column names. from User user order by user.username desc List results = session.createCriteria(User.class) .addOrder( Order.asc ("lastname") ) .addOrder( Order.asc ("firstname") ) .list(); from Item item order by item.successfulBid.amount desc, item.id asc
- 12. Joining associations The join SQL operation combines the data from two tables: PRICE PRICE 3 Baz 1.00 NAME ITEM_ID 1 1 2 Foo Foo Bar 2.00 2.00 50.00 from ITEM I inner join BID B on I.ITEM_ID = B.ITEM_ID AMOUNT ITEM_ID BID_ID 1 2 3 1 1 2 10.00 20.00 55.00 NAME ITEM_ID 1 1 2 Foo Foo Bar 2.00 2.00 50.00 from ITEM I left outer join BID B on I.ITEM_ID = B.ITEM_ID AMOUNT ITEM_ID BID_ID 1 2 3 1 1 2 10.00 20.00 55.00 null null null
- 13. Fetching associations in Hibernate We can fetch associated objects in HQL: For all Item s returned, the item.bids collection is fully initialized, all with a single SQL outer join query. We may only fetch one collection in one HQL query, preserving performance in SQL. We can also use a Criteria: from Item item left join fetch item.bids where item.description like '%gc%' session.createCriteria(Item.class) .setFetchMode("bids", FetchMode.EAGER) .add( Expression.like("description", "gc", MatchMode.ANYWHERE) )
- 14. Fetching single-ended associations We can also initialize (fetch) single-ended associations: IMPORTANT: HQL always ignores the outer-join setting in the mapping metadata, we have to use an explicit fetch join. The Criteria will not ignore the metadata, disable it with FetchMode.LAZY ! from Bid bid left join fetch bid.item left join fetch bid.bidder where bid.amount > 100 session.createCriteria(Bid.class) .setFetchMode("item", FetchMode.EAGER) .setFetchMode("bidder", FetchMode.EAGER) .add( Expression.gt("amount", new Float(100) ) )
- 15. Using aliases with joins We need an alias to express restrictions on joined objects: This query returns ordered pairs of Items and Bids: The select clause is optional: from Item item join item.bids bid where item.description like '%gc%' and bid .amount > 100 Query q = session.createQuery(" from Item item join item.bids bid "); Iterator pairs = q.list().iterator(); while ( pairs.hasNext() ) { Object[] pair = (Object[]) pairs.next(); Item item = (Item) pair[0]; Bid bid = (Bid) pair[1]; } select item from Item item join item.bids bid where bid.amount > 100
- 16. Using implicit association joins We can query components (dereference with a dot): We can join single-ended associations implicitly: We have to be careful (how many SQL joins is this?): from User user where user . address . city = 'Bangkok' from Bid bid where bid . item . description like '%gc%' from Bid bid where bid.item.category.name like 'Laptop%' from Bid bid where bid.item.category.name like 'Laptop%' and bid.item.successfulBid.amount > 100
- 17. Theta-style joins First, we create a Cartesian Product: Then, we add a join condition in the where clause: Theta-style (inner) joins are used when related tables (classes) are not referenced with a foreign key relationship (no automatic joining). from User, LogRecord from User user, LogRecord log where user.username = log.username
- 18. Report queries in HQL The select clause is used for projection: The result may be an Object[] of non-transactional objects: Dynamic instantiation is much easier to handle: select item from Item item join item.bids bid where bid.amount > 100 select item.id, item.description, bid.amount from Item item join item.bids bid where bid.amount > 100 select new ItemRow ( item.id, item.description, bid.amount ) from Item item join item.bids bid where bid.amount > 100
- 19. Aggregation in HQL The aggregation functions are count() , min() , max() , sum() and avg() Count all items with a successful bid: The total of all successful bids: The result is an ordered pair of Float s: select count (item.successfulBid) from Item item select sum (item.successfulBid.amount) from Item item select min (bid.amount), max (bid.amount) from Bid bid where bid.item.id = 1
- 20. Grouping in HQL If we aggregate, we have to group every property in the select : We can further restrict the result on the group: Use report queries (projection, aggregation, grouping, dynamic instantiation) to optimize performance, only retrieve required data. select bid.item.id , avg(bid.amount) from Bid bid group by bid.item.id select item.id , count(bid) ), avg(bid.amount) from Item item join item.bids bid where item.successfulBid is null group by item.id having count(bid) > 10
- 21. Dynamic queries with Query By Example Complex searches are best expressed with a QBE query: QBE can be combined with QBC: User exampleUser = new User(); exampleUser.setFirstname ("Max"); exampleUser.setEmail ("@hibernate.org"); List result = findUsers( exampleUser ); public List findUsers(User user) throws HibernateException { return getSession().createCriteria(User.class) .add( Example.create(user) .ignoreCase().enableLik (MatchMode.ANYWHERE) ) .list(); } createCriteria(User.class) .add( Example.create(user) .ignoreCase().enableLike(MatchMode.ANYWHERE) ) . createCriteria("items") .add( Expression.isNull("successfulBid") );
- 22. Filtering persistent collections We can filter the collection of bids of an already loaded Item : HQL filter queries have an implicit from and where clause: A filter doesn't have to return objects from the collection: Query q = session.createFilter ( item.getBids() , "order by this .amount asc" ); Query q = session.createFilter( item.getBids(), "" ); // This is valid... List result = q.setFirstResult(50).setMaxResults(100).list() // and useful! List results = session.createFilter( item.getBids(), "select elements (this.bidder.bids)" ).list()
- 23. Subqueries in HQL Correlated subquery, total items sold by users, if more than 10: Uncorrelated subquery, all bids that are 1 within the maximum: We can use SQL quantifiers, ANY / ALL / SOME / IN: from User user where 10 < ( select count(item) from user.items where item.successfulBid is not null ) from Bid bid where bid.amount + 1 >= ( select max(b.amount) from Bid b ) from Item item where 100 > all ( select b.amount from item.bids b ) from Item item where 100 < any ( select b.amount from item.bids b ) from Item item where 100 = some ( select b.amount from item.bids b ) from Item item where 100 in ( select b.amount from item.bids b )
- 24. Native SQL queries Direct native SQL queries use Hibernate shortcuts: We can return Object[] s (tuples): We can externalize SQL queries to the mapping file: List results = session.createSQLQuery("select {uzer.*} from user uzer", " uzer ", User.class ).list(); List tuples = session.createSQLQuery( "select {u.*}, {b.*} from user u inner join bid b where u.id = b.bidder_id", new String[] { "u", "b" }, new Class[] {User.class, Bid.class} ).list(); <sql-query name="findUsersAndBids"><![CDATA[ select {u.*}, {b.*} from user u inner join bid b where u.id = b.bidder_id ]]> <return alias="u" class="User"/> <return alias="b" class="Bid"/> </sql-query>