3.2 Finding Persistent Objects

     

It's time to throw the giant lever into reverse and look at how you load data from a database into Java objects.

Use Hibernate Query Language to get an object-oriented view of the contents of your mapped database tables. These might have started out as objects persisted in a previous session, or they might be data that came from completely outside your application code.

3.2.1 How do I do that?

Example 3-5 shows a program that runs a simple query using the test data we just created. The overall structure will look very familiar, because all the Hibernate setup is the same as the previous program.

Example 3-5. QueryTest.java
 1 package com.oreilly.hh;  2  3 import net.sf.hibernate.*;  4 import net.sf.hibernate.cfg.Configuration;  5  6 import java.sql.Time;  7 import java.util.*;  8  9 /** 10 * Retrieve data as objects 11 */ 12 public class QueryTest { 13 14     /** 15      * Retrieve any tracks that fit in the specified amount of time. 16      * 17      * @param length the maximum playing time for tracks to be returned. 18      * @param session the Hibernate session that can retrieve data. 19      * @return a list of {@link Track}s meeting the length restriction. 20      * @throws HibernateException if there is a problem. 21      */ 22     public static List tracksNoLongerThan(Time length, Session session) 23         throws HibernateException 24     { 25         return session.find("from com.oreilly.hh.Track as track " + 26                             "where track.playTime <= ?", 27                             length, Hibernate.TIME); 28     } 29 30     /** 31      * Look up and print some tracks when invoked from the command line. 32      */ 33     public static void main(String args[]) throws Exception { 34         // Create a configuration based on the properties file we've put 35         // in the standard place. 36         Configuration config = new Configuration(); 37 38         // Tell it about the classes we want mapped, taking advantage of 39         // the way we've named their mapping documents. 40         config.addClass(Track.class); 41 42         // Get the session factory we can use for persistence 43         SessionFactory sessionFactory = config.buildSessionFactory(); 44 45         // Ask for a session using the JDBC information we've configured 46         Session session = sessionFactory.openSession(); 47         try { 48             // Print the tracks that will fit in five minutes 49             List tracks = tracksNoLongerThan(Time.valueOf("00:05:00"), 50                                              session); 51             for (ListIterator iter = tracks.listIterator() ; 52                  iter.hasNext() ; ) { 53                 Track aTrack = (Track)iter.next(); 54                 System.out.println("Track: \"" + aTrack.getTitle() + 55                                     "\", " + aTrack.getPlayTime()); 56             } 57      } finally { 58            // No matter what, close the session 59            session.close(); 60      } 61 62      // Clean up after ourselves 63      sessionFactory.close(); 64    } 65 } 

Again, add a target (Example 3-6) at the end of build.xml to run this test.

Example 3-6. Ant target to invoke our query test
 <target name="qtest" description="Run a simple Hibernate query"         depends="compile">   <java classname="com.oreilly.hh.QueryTest" fork="true">      <classpath refid="project.class.path"/>   </java> </target> 

With this in place, we can simply type ant qtest to retrieve and display some data, with the results shown in Example 3-7. To save space in the output, we've edited our log4j.properties to turn off all the ' Info ' messages, since they're no different than in the previous example. You can do this yourself by changing the line:

 log4j.logger.net.sf.hibernate=info 

to replace the word info with warn:

 log4j.logger.net.sf.hibernate=  warn  

Example 3-7. Running the query test
 %  ant qtest  Buildfile: build.xml prepare: compile:     [javac] Compiling 1 source file to /Users/jim/Documents/Work/OReilly/ Hibernate/Examples/ch03/classes qtest:     [java] Track: "Russian Trance", 00:03:30     [java] Track: "Video Killed the Radio Star", 00:03:49      BUILD SUCCESSFUL Total time: 11 seconds 

3.2.2 What just happened ?

In Example 3-5, we started out by defining a utility method, tracksNoLongerThan() on lines 14-28, which performs the actual Hibernate query. It retrieves any tracks whose playing time is less than or equal to the amount specified as a parameter. Hibernate queries are written in HQL (Hibernate Query Language ”explored in Chapter 9). This is a SQL-inspired but object-oriented query language with important differences from SQL. HQL is explored in Chapter 9. For now, note that it supports parameter placeholders, much like PreparedStatement in JDBC. And, just like in that environment, using them is preferable to putting together queries through string manipulation. As you'll see below, however, Hibernate offers even better ways of working with queries in Java.

NOTE

If you don't want to deal with any query syntax at all, check out criteria queries in Chapter 8!

The query itself looks a little strange . It starts with ' from ' rather than ' select something ' as you might expect. While you can certainly use the more familiar format, and will do so when you want to pull out individual properties from an object in your query, if you want to retrieve entire objects you can use this more abbreviated syntax.

NOTE

When you're working with pre-existing databases and objects, it's important to realize that HQL queries refer to object properties rather than database table

Also note that the query is expressed in terms of the mapped Java objects and properties rather than the tables and columns . Since the object being used in the query is in the same package as the mapping document, we don't actually need to use a fully qualified class name like this, but it helps remind readers of the query that it's expressed in terms of objects. Keeping the names consistent is a fairly natural choice, and this will always be the case when you're using Hibernate to generate the schema and the data objects, unless you tell it explictly to use different column names .

Also, in HQL ”just as in SQL ”you can alias a column or table to another name; in fact, you must use aliases to refer to specific JavaBeans properties of mapped classes in your where clauses.

With all that in mind, the meaning of the HQL query ' from com.oreilly.hh.Track as track where track.playTime <= ?' breaks down like this:

  • We're retrieving instances of our persistent Java data bean Track . (This happens to be stored in the database table called TRACK , but that is managed by the mapping document for class Track ; the table could be called anything.) Since we started the query with the from clause (rather than a select clause), we want to obtain entire Track instances as the result of the query.

  • Within the query, we are defining an alias ' track ' to refer to one particular instance of the Track class at a time, so we can specify the properties of the instances we'd like to retrieve.

  • The instances we want are those where the playTime property has a value that's less than or equal to the first (and only) query parameter we're passing in at runtime. (Once again, the fact that the playTime property is mapped to the PLAYTIME column is a detail that is buried in the Track mapping document, and it need not be true.)

HQL reads a lot like SQL, but the semantics are different ”they refer to the world of the Java program rather than the world of the relational database. This will be more clear in later examples and in Chapter 9 where we perform more complex queries.

The rest of the program should look mighty familiar from the previous example. Our try block is simplified because we don't need a transaction, as we're not changing any data. We still use one so that we can have a finally clause to close our session cleanly. The body is quite simple, calling our query method to request any tracks whose playing time is five minutes or less, and then iterating over the resulting Track objects, printing their titles and playing times.

3.2.3 What about...

...Deleting objects? If you've made changes to your data creation script and want to start with a 'clean slate' in the form of an empty database so you can test them, all you need to do is run ant schema again. This will drop and recreate the Track table in a pristine and empty state. Don't do it unless you mean it!

If you want to be more selective about what you delete, you can either do it through SQL commands in the HSQLDB UI ( ant db ), or you can make a variant of the query test example that retrieves the objects you want to get rid of. Once you've got a reference to a persistent object, passing it to the Session 's delete() method will remove it from the database:

 session.delete(aTrack); 

You've still got at least one reference to it in your program, until aTrack goes out of scope or gets reassigned, so conceptually the easiest way to understand what delete() does is to think of it as turning a persistent object back into a transient one.

Another way to use the delete() method is to pass it an HQL query string that matches multiple objects. This lets you delete many persisted objects at once, without writing your own loop. A Java-based alternative to ant schema , and a slightly less violent way of clearing out all the tracks, would therefore be something like this:

 session.delete("from com.oreilly.hh.Track"); 

Don't forget that regardless of which of these approaches you use, you'll need to wrap the data manipulation code inside a Hibernate transaction, and commit the transaction if you want your changes to 'stick.'




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