6.2 Working with Persistent Enumerations


If you were thinking about it, you may have noticed that we never defined a persistence mapping for the SourceMedia class in the first part of this chapter. That's because our persistent enumerated type is a value that gets persisted as part of one or more entities, rather than being an entity unto itself.

In that light, it's not surprising that we've not yet done any mapping. That happens when it's time to actually use the persistent enumeration.

6.2.1 How do I do that?

Recall that we wanted to keep track of the source media for the music tracks in our jukebox system. That means we want to use the SourceMedia enumeration in our Track mapping. We can simply add a new property tag to the class definition in Track.hbm.xml , as shown in Example 6-3.

Example 6-3. Adding the sourceMedia property to the Track mapping document
 ... <property name="volume" type="short">   <meta attribute="field-description">How loud to play the track</meta> </property>  <property name="sourceMedia" type="com.oreilly.hh.SourceMedia">   <meta attribute="field-description">Media on which track was obtained</meta>  <meta attribute="use-in-tostring">true</meta>  </property>  </class> ... 

Because the type of our sourceMedia property names a class that implements the PersistentEnum interface, Hibernate knows to persist it using its built-in enumeration support.

With this addition in place, running ant codegen updates our Track class to include the new property. The signature of the full-blown Track constructor now looks like this:

 public Track(String title, String filePath, Date playTime, Date added,                   short volume,  com.oreilly.hh.SourceMedia sourceMedia,  Set artists, Set comments) { ... } 

We need to make corresponding changes in CreateTest.java :

 Track track = new Track("Russian Trance",                              "vol2/album610/track02.mp3",                              Time.valueOf("00:03:30"), new Date(),                              (short)0,  SourceMedia.CD,  new HashSet(), new HashSet());          ...                  track = new Track("Video Killed the Radio Star",                                    "vol2/album611/track12.mp3",                                    Time.valueOf("00:03:49"), new Date(),                                    (short)0,  SourceMedia.VHS,  new HashSet(), new HashSet()); 

And so on. To match the results shown later, mark the rest as coming from CDs, except for "The World '99" which comes from a stream and give "Test Tone 1" a null sourceMedia value. At this point, run ant schema to rebuild the database schema with support for the new property, and run ant ctest to create the sample data.

6.2.2 What just happened ?

Our TRACK table now contains an integer column to store the sourceMedia property. We can see its values by looking at the contents of the table after creating the sample data (the easiest way is to run a query within ant db , as shown in Figure 6-1).

We can verify that the values persisted to the database are correct by cross-checking the codes assigned to our persistent enumeration. Alternately, we can see a more meaningful version of the information by slightly enhancing the query test to print this property for the tracks it retrieves. The necessary changes are in bold in Example 6-4.

Figure 6-1. Source media information in the TRACK table

Example 6-4. Displaying source media in QueryTest.java
 ... // Print the tracks that will fit in seven minutes List tracks = tracksNoLongerThan(Time.valueOf("00:07:00"),                                  session); for (ListIterator iter = tracks.listIterator() ;      iter.hasNext() ; ) {     Track aTrack = (Track)iter.next();  String mediaInfo = "";     if (aTrack.getSourceMedia() != null) {         mediaInfo = ", from " +             aTrack.getSourceMedia().getDescription();     }  System.out.println("Track: \"" + aTrack.getTitle() + "\" " +                        listArtistNames(aTrack.getArtists()) +                        aTrack.getPlayTime()  + mediaInfo  ); 

With these enhancements, running ant qtest yields the output shown in Example 6-5. Tracks with non- null source media values now have "from" and the appropriate media description displayed at the end.

Example 6-5. Human-oriented display of source media information
 ... qtest:      [java] Track: "Russian Trance" (PPK) 00:03:30, from Compact Disc      [java] Track: "Video Killed the Radio Star" (The Buggles) 00:03:49, from VHS Videocassette Tape      [java] Track: "Gravity's Angel" (Laurie Anderson) 00:06:06, from Compact Disc      [java] Track: "Adagio for Strings (Ferry Corsten Remix)" (Ferry Corsten, William Orbit, Samuel Barber) 00:06:35, from Compact Disc      [java] Track: "Test Tone 1" 00:00:10      [java]   Comment: Pink noise to test equalization 

Note that if we hadn't decided to do our own fancy formatting of a subset of the tracks' properties in QueryTest and instead relied on the toString() method in Track , we'd not have needed to make any changes to QueryTest to see this new information. Our mapping document specified that the sourceMedia property should be included in the toString() result, which would have taken care of it. You can inspect the generated toString() source to check this, or write a simple test program to see what the toString() output looks like. An excellent candidate would be to fix AlbumTest.java so it will compile and run after our changes to Track . The easiest fix is to simply hardcode the addAlbumTrack() method to assume everything comes from CDs, as in Example 6-5 (the JavaDoc already excuses such shameful rigidity).

Example 6-6. Fixing AlbumTest.java to support source media
 /**  * Quick and dirty helper method to handle repetitive portion of creating  * album tracks. A real implementation would have much more flexibility.  */ private static void addAlbumTrack(Album album, String title, String file,                                   Time length, Artist artist, int disc,                                   int positionOnDisc, Session session)     throws HibernateException {     Track track = new Track(title, file, length, new Date(), (short)0,  SourceMedia.CD,  new HashSet(), new HashSet());     track.getArtists().add(artist);     //        session.save(track);     album.getTracks().add(new AlbumTrack(disc, positionOnDisc, track)); } 

With this fix in place, running ant atest shows that the source media information propagates all the way up to Album 's own toString() method:

 [java] com.oreilly.hh.Album@e0f945[id=0,title=Counterfeit e.p.,     tracks=[com.oreilly.hh.AlbumTrack@1370ab[track=com.oreilly.hh.     Track@49f9fa[id=<null>,title=Compulsion,sourceMedia=Compact Disc]], com.     oreilly.hh.AlbumTrack@ba936a[track=com.oreilly.hh.Track@2421db[id=<null>,     title=In a Manner of Speaking,sourceMedia=Compact Disc]], com.oreilly.hh.     AlbumTrack@2ad974[track=com.oreilly.hh.Track@2a7640[id=<null>,title=Smile in     the Crowd,sourceMedia=Compact Disc]], com.oreilly.hh.     AlbumTrack@b9808e[track=com.oreilly.hh.Track@a721e2[id=<null>,     title=Gone,sourceMedia=Compact Disc]], com.oreilly.hh.     AlbumTrack@a1ad7d[track=com.oreilly.hh.Track@851576[id=<null>,title=Never     Turn Your Back on Mother Earth,sourceMedia=Compact Disc]], com.oreilly.hh.     AlbumTrack@442c19[track=com.oreilly.hh.Track@ab2ddb[id=<null>,     title=Motherless Child,sourceMedia=Compact Disc]]]] 

With a little work, Hibernate lets you extend your typesafe enumerations to support persistence. And once you've invested that effort, you can persist them as easily as any other value type for which native support exists.

It will be interesting to see how Hibernate evolves to take advantage of the exciting enum keyword support in Java 1.5 once that's been out for a while. The need to implement PersistentEnum will probably disappear, since all real enum s will already extend java.lang.Enum and will have interesting ways to obtain specific members . I hope that as Hibernate evolves to support these new first-class enumerations, it will also allow their symbolic enumeration constants to be stored in the database, rather than requiring the use of a cryptic integer column as it does today. In an ideal world, it will even be able to take advantage of the native support for enumerations provided by some databases.

If you're interested in an alternate approach to persisting typesafe enumerations that can achieve some of these goals today, read on to Chapter 7 in which the mysteries of custom type mapping are explored!

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

Similar book on Amazon

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