9.5 Writing Native SQL Queries


Given the power and convenience of HQL, and the way it dovetails so naturally with the objects in your Java code, why wouldn't you want to use it? Well, there might be some special feature supported by the native SQL dialect of your project's database that HQL can't exploit. If you're willing to accept the fact that using this feature will make it harder to change databases in the future, Hibernate will let you write queries in that native dialect while still helping you write expressions in terms of properties and translate the results to objects. (If you didn't want this help, you could just use a raw JDBC connection to run a plain SQL query, of course.)

Another circumstance in which it might be nice to meet your database halfway is if you're in the process of migrating an existing JDBC-based project to Hibernate, and you want to take small steps rather than thoroughly rewriting each query right away.

9.5.1 How do I do that?

If you're embedding your query text inside your Java source code, you use the Session method createSQLQuery() instead of Example 3-8's createQuery() . Of course, you know better than to code like that, so I won't even show you an example. The better approach is to put the query in a mapping document like Example 3-9. The difference is that you use a sql-query tag rather than the query tag we've seen up until now. You also need to tell Hibernate the mapped class you want to return, and the alias that you're using to refer to it (and its properties) in the query.

As a somewhat contrived example, suppose we want to know all the tracks that end exactly halfway through the last minute they're playing (in other words, the time display on the jukebox would be h : mm :30 ). An easy way to do that would be to take advantage of HSQLDB's built-in SECOND function, which gives you the seconds part of a Time value. Since HQL doesn't know about functions that are specific to HSQLDB's SQL dialect, this will push us into the realm of a native SQL query. Example 9-23 shows what it would look like; add this after the HQL queries in Track.hbm.xml .

Example 9-23. Embedding a native SQL dialect query in a Hibernate mapping
 <sql-query name="com.oreilly.hh.tracksEndingAt">   <return alias="track" class="com.oreilly.hh.Track"/>   <![CDATA[        select {track.*}       from TRACK as {track}       where SECOND({track}.PLAYTIME) = :seconds     ]]> </sql-query> 

The return tag tells Hibernate we're going to be using the alias track in our query to refer to a Track object. That allows us to use the shorthand {track.*} in the query body to refer to all the columns from the TRACK table we need in order to create a Track instance. (Notice that everywhere we use the alias in the query body we need to enclose it in curly braces. This gets us "out of" the native SQL environment so we can express things in terms of Hibernate-mapped classes and properties.)

The where clause in the query uses the HSQLDB SECOND function to narrow our results to include only tracks whose length has a specified number in the seconds part. Happily, even though we're building a native SQL query, we can still make use of Hibernate's nice named query parameters. In this case we're passing in a value named " seconds " to control the query. (You don't use curly braces to tell Hibernate you're using a named parameter even in an SQL query; its parser is smart enough to figure this out.)

The code that uses this mapped SQL query is no different than our previous examples using HQL queries. The getNamedQuery() method is used to load both kinds, and they both implement the Query interface. So our Java method invoking this query should look familiar. Add the code in Example 9-24 after the printTrackSummary() method in QueryTest.java .

Example 9-24. Calling a native SQL mapped query
 /**  * Print tracks that end some number of seconds into their final minute.  *  * @param seconds, the number of seconds at which we want tracks to end.  * @param session the Hibernate session that can retrieve data.  * @throws HibernateException if there is a problem.  **/ public static void printTracksEndingAt(int seconds, Session session)     throws HibernateException {     Query query = session.getNamedQuery("com.oreilly.hh.tracksEndingAt");     query.setInteger("seconds", seconds);     List results = query.list();     for (ListIterator iter = results.listIterator() ; iter.hasNext() ; ) {         Track aTrack = (Track)iter.next();         System.out.println("Track: " + aTrack.getTitle() +                            ", length=" + aTrack.getPlayTime());     } } 

Finally, add some lines to main() that call this method. Example 9-25 shows them added after the invocation of printTrackSummary() .

Example 9-25. Calling printTracksEndingAt() to display tracks ending at a half minute
 ...     printTrackSummary(CreateTest.getArtist("Samuel Barber",                                            false, session), session);  System.out.println("Tracks ending halfway through final minute:");     printTracksEndingAt(30, session);  } finally {     // No matter what, close the session ... 

These changes produce the additional output shown in Example 9-26 when ant qtest is run.

Example 9-26. Sample output from the native SQL query
 qtest:      [java] Track: Video Killed the Radio Star [ID=1] ...      [java] Summary of tracks by Samuel Barber:      [java]        Total tracks: 2      [java]      Shortest track: 00:06:35      [java]       Longest track: 00:07:39      [java]  Tracks ending halfway through final minute:  [java]  Track: Russian Trance, length=00:03:30  

There's a lot more tedium and attention to detail required in using a native SQL query than an HQL query ( especially when your query starts getting complex or referring to multiple tables), but it's nice to know that it is possible on the rare occasions where you really need one.

9.5.2 What about...

...Well, lots of things? You undoubtedly suspect this chapter barely scratches the surface of what you can do with HQL. That's definitely true. When you start combining some of these capabilities, and working with collections, associations, and powerful expressions, you can achieve some remarkable things. We can't possibly cover them all in this introduction, so you'll want to take a close look at the HQL section and examples in the Hibernate reference documentation, and do some experimentation on your own.

When you look through the Hibernate Query Language chapter in the reference documentation, be sure to look at the interesting things you can use in expressions, especially as they apply to collections. Don't miss the way you can use array bracket notation to select elements of indexed collections ”you can even put arithmetic expressions inside the brackets.

The "Tips and Tricks" section that follows their longer examples gives some useful advice about working efficiently in different database environments, and using a variety of Hibernate interfaces to achieve useful results in ways you might not think of, especially if you're coming from a SQL background.

Hopefully, this discussion has helped you get a grounding in the basics, and it will serve as a starting point and anchor for the explorations on which you will embark!


This isn't your father's SQL....

Hibernate. A Developer's Notebook
Hibernate: A Developers Notebook
ISBN: 0596006969
EAN: 2147483647
Year: 2003
Pages: 65
Authors: James Elliott

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net