Cover | Table of Contents | Colophon
In this chapter.
Getting an Ant Distribution Getting the HSQLDB Database Engine Getting Hibernate Setting Up a Project Hierarchy
I used to wonder why people bothered with Ant when they could use Make. Now that I've seen how well it manages Java builds, I feel lost without it.
ANT_HOME. Let's say you've expanded the archive into the directory /usr/local/apache-ant-1.5.1; you may want to create a symbolic link to make it easier to work with, and to avoid the need to change any environment configuration when you upgrade to a newer version:
/usr/local $ ln -s apache-ant-1.5.1 ant
hsqldb is a relational database engine written in Java, with a JDBC driver, supporting a rich subset of ANSI-92 SQL (BNF tree format). It offers a small (less than 160k), fast database engine which offers both in memory and disk based tables. Embedded and server modes are available. Additionally, it includes tools such as a minimal web server, in-memory query and management tools (can be run as applets), and a number of demonstration examples.
Go on, download HSQLDB. Heck, take two, they're small!
In this chapter:
Writing a Mapping Document Generating Some Class Cooking Up a Schema Connecting Hibernate to MySQL
Don't forget that Hibernate and its extensions let you work in other ways, starting with classes or data if you've got them.
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping 3 PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" 4 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"> 5 <hibernate-mapping> 6 7 <class name="com.oreilly.hh.Track" table="TRACK"> 8 <meta attribute="class-description"> 9 Represents a single playable track in the music database. 10 @author Jim Elliott (with help from Hibernate) 11 </meta> 12 13 <id name="id" type="int" column="TRACK_ID"> 14 <meta attribute="scope-set">protected</meta> 15 <generator class="native"/> 16 </id> 17 18 <property name="title" type="string" not-null="true"/> 19 20 <property name="filePath" type="string" not-null="true"/> 21 22 <property name="playTime" type="time"> 23 <meta attribute="field-description">Playing time</meta> 24 </property> 25 26 <property name="added" type="date"> 27 <meta attribute="field-description">When the track was created</meta> 28 </property> 29 30 <property name="volume" type="short"> 31 <meta attribute="field-description">How loud to play the track</meta> 32 </property> 33 34 </class> 35 </hibernate-mapping>
1 <project name="Harnessing Hibernate: The Developer's Notebook"
2 default="db" basedir=".">
3 <!-- Set up properties containing important project directories -->
4 <property name="source.root" value="src"/>
5 <property name="class.root" value="classes"/>
6 <property name="lib.dir" value="lib"/>
7 <property name="data.dir" value="data"/>
8
9 <!-- Set up the class path for compilation and execution -->
10 <path id="project.class.path">
11 <!-- Include our own classes, of course -->
12 <pathelement location="${class.root}" />
13 <!-- Include jars in the project library directory -->
14 <fileset dir="${lib.dir}">
15 <include name="*.jar"/>
16 </fileset>
17 </path>
18
19 <target name="db" description="Runs HSQLDB database management UI
20 against the database file--use when application is not running">
21 <java classname="org.hsqldb.util.DatabaseManager"
22 fork="yes">
23 <classpath refid="project.class.path"/>
24 <arg value="-driver"/>
25 <arg value="org.hsqldb.jdbcDriver"/>
26 <arg value="-url"/>
27 <arg value="jdbc:hsqldb:${data.dir}/music"/>
28 <arg value="-user"/>
29 <arg value="sa"/>
30 </java>
31 </target>
32
33 <!-- Teach Ant how to use Hibernate's code generation tool -->
34 <taskdef name="hbm2java"
35 classname="net.sf.hibernate.tool.hbm2java.Hbm2JavaTask"
36 classpathref="project.class.path"/>
37
38 <!-- Generate the java code for all mapping files in our source tree -->
39 <target name="codegen"
40 description="Generate Java source from the O/R mapping files">
41 <hbm2java output="${source.root}">
42 <fileset dir="${source.root}">
43 <include name="**/*.hbm.xml"/>
44 </fileset>
45 </hbm2java>
46 </target>net.sf.hibernate.dialect. You just need to tell it which one you want to use. (And if you want to work with a database that isn't yet supported "out of the box," you can implement your own dialect.)HSQLDialect. The easiest way to configure Hibernate is to create a properties file named hibernate.properties and put it at the root level some-where in the class path. Create this file at the top level of your src directory, and put the lines shown in into it.hibernate.dialect=net.sf.hibernate.dialect.HSQLDialect hibernate.connection.driver_class=org.hsqldb.jdbcDriver hibernate.connection.url=jdbc:hsqldb:data/music hibernate.connection.username=sa hibernate.connection.password=
You can use an XML format for the configuration information as well, but for the simple needs we have here, it doesn't buy you anything.
This example assumes you've already got a working MySQL instance installed andrunning, since explaining how to do that would be quite a detour.
% mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 764 to server version: 3.23.44-Max-log Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> CREATE DATABASE notebook_db; Query OK, 1 row affected (0.00 sec) mysql> GRANT ALL ON notebook_db.* TO jim IDENTIFIED BY "s3cret"; Query OK, 0 rows affected (0.20 sec) mysql> quit; Bye
Hopefully you'll use a less guessable password than this in your real databases!
In this chapter:
Creating Persistent Objects Finding Persistent Objects Better Ways to Build Queries
Track instances into corresponding rows in the database table.CreateTest, containing the necessary imports and housekeeping code to bring up the Hibernate environment and create some Track instances that can be persisted using the XML mapping document we started with. The source is shown in .The examples in most chapters build on the previous ones, so if you are skipping around, you'll really want to download the sample code.
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.Date;
8
9 /**
10 * Create sample data, letting Hibernate persist it for us.
11 */
12 public class CreateTest {
13
14 public static void main(String args[]) throws Exception {
15 // Create a configuration based on the properties file we've put
16 // in the standard place.
17 Configuration config = new Configuration();
18
19 // Tell it about the classes we want mapped, taking advantage of
20 // the way we've named their mapping documents.
21 config.addClass(Track.class);
22
23 // Get the session factory we can use for persistence
24 SessionFactory sessionFactory = config.buildSessionFactory();
25
26 // Ask for a session using the JDBC information we've configured
27 Session session = sessionFactory.openSession();
28 Transaction tx = null;
29 try {
30 // Create some data and persist it
31 tx = session.beginTransaction();
32
33 Track track = new Track("Russian Trance",
34 "vol2/album610/track02.mp3",
35 Time.valueOf("00:03:30"), new Date(),
36 (short)0);
37 session.save(track);
38
39 track = new Track("Video Killed the Radio Star",
40 "vol2/album611/track12.mp3",
41 Time.valueOf("00:03:49"), new Date(),
42 (short)0);
43 session.save(track);
44
45
46 track = new Track("Gravity's Angel",
47 "vol2/album175/track03.mp3",
48 Time.valueOf("00:06:06"), new Date(),
49 (short)0);
50 session.save(track);
51
52 // We're done; make our changes permanent
53 tx.commit();
54
55 } catch (Exception e) {
56 if (tx != null) {
57 // Something went wrong; discard all partial changes
58 tx.rollback();
59 }
60 throw e;
61 } finally {
62 // No matter what, close the session
63 session.close();
64 }
65
66 // Clean up after ourselves
67 sessionFactory.close();
68 }
69 }
Track instances into corresponding rows in the database table.CreateTest, containing the necessary imports and housekeeping code to bring up the Hibernate environment and create some Track instances that can be persisted using the XML mapping document we started with. The source is shown in .The examples in most chapters build on the previous ones, so if you are skipping around, you'll really want to download the sample code.
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.Date;
8
9 /**
10 * Create sample data, letting Hibernate persist it for us.
11 */
12 public class CreateTest {
13
14 public static void main(String args[]) throws Exception {
15 // Create a configuration based on the properties file we've put
16 // in the standard place.
17 Configuration config = new Configuration();
18
19 // Tell it about the classes we want mapped, taking advantage of
20 // the way we've named their mapping documents.
21 config.addClass(Track.class);
22
23 // Get the session factory we can use for persistence
24 SessionFactory sessionFactory = config.buildSessionFactory();
25
26 // Ask for a session using the JDBC information we've configured
27 Session session = sessionFactory.openSession();
28 Transaction tx = null;
29 try {
30 // Create some data and persist it
31 tx = session.beginTransaction();
32
33 Track track = new Track("Russian Trance",
34 "vol2/album610/track02.mp3",
35 Time.valueOf("00:03:30"), new Date(),
36 (short)0);
37 session.save(track);
38
39 track = new Track("Video Killed the Radio Star",
40 "vol2/album611/track12.mp3",
41 Time.valueOf("00:03:49"), new Date(),
42 (short)0);
43 session.save(track);
44
45
46 track = new Track("Gravity's Angel",
47 "vol2/album175/track03.mp3",
48 Time.valueOf("00:06:06"), new Date(),
49 (short)0);
50 session.save(track);
51
52 // We're done; make our changes permanent
53 tx.commit();
54
55 } catch (Exception e) {
56 if (tx != null) {
57 // Something went wrong; discard all partial changes
58 tx.rollback();
59 }
60 throw e;
61 } finally {
62 // No matter what, close the session
63 session.close();
64 }
65
66 // Clean up after ourselves
67 sessionFactory.close();
68 }
69 }
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 }
If you haven't yet had to deal with this, trust me, it's well worth avoiding.
Query interface. We'll start by changing our query to use a named parameter (). (This isn't nearly as big a deal for a query with a single parameter like this one, but it's worth getting into the habit right away. You'll be very thankful when you start working with the light-dimming queries that power your real projects!)In this chapter:
Mapping Collections Persisting Collections Retrieving Collections Using Bidirectional Associations Working with Simple Collections
Bags are like sets, except that the same value can appear more than once.
Track examples from and group them into albums, but that's not the simplest place to start, because organizing an album involves tracking additional information, like the disc on which the track is found (for multi-disc albums), and other such finicky details. So let's add artist information to our database.As usual, the examples assume you followed the steps in the previous chapters. If not, download the example source as a starting point.
Bags are like sets, except that the same value can appear more than once.
Track examples from and group them into albums, but that's not the simplest place to start, because organizing an album involves tracking additional information, like the disc on which the track is found (for multi-disc albums), and other such finicky details. So let's add artist information to our database.As usual, the examples assume you followed the steps in the previous chapters. If not, download the example source as a starting point.
Artist class doesn't need anything other than a name property (and its key, of course). Setting up a mapping document for it will be easy. Create the file Artist.hbm.xmlCreateTest class to take advantage of the new richness in our schema, creating some artists and associating them with tracks.1 package com.oreilly.hh; 2 3 import net.sf.hibernate.*; 4 5 import net.sf.hibernate.cfg.Configuration; 6 7 import java.sql.Time; 8 import java.util.*; 9 10 /** 11 * Create more sample data, letting Hibernate persist it for us. 12 */ 13 public class CreateTest { 14 15 /** 16 * Look up an artist record given a name. 17 * @param name the name of the artist desired. 18 * @param create controls whether a new record should be created if 19 * the specified artist is not yet in the database. 20 * @param session the Hibernate session that can retrieve data 21 * @return the artist with the specifiedname, or <code>null</code>if no 22 * such artist exists and <code>create</code> is <code>false</code> 23 * @throws HibernateException if there is a problem. 24 */ 25 public static Artist getArtist(String name, boolean create, 26 Session session) 27 throws HibernateException 28 { 29 Query query = session.getNamedQuery( 30 "com.oreilly.hh.artistByName"); 31 query.setString("name", name); 32 Artist found = (Artist)query.uniqueResult(); 33 if (found == null && create) { 34 found = new Artist(name, new HashSet()); 35 session.save(found); 36 } 37 return found; 38 } 39 40 /** 41 * Utility method to associate an artist with a track 42 */ 43 private static void addTrackArtist(Track track, Artist artist) { 44 track.getArtists().add(artist); 45 }
Return to Hibernate: A Developer's Notebook