O'Reilly logo

Harnessing Hibernate by James Elliott, Ryan Fowler, Timothy M. O'Brien

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Using Bidirectional Associations

In our creation code, we established links from tracks to artists, simply by adding Java objects to appropriate collections. Hibernate did the work of translating these associations and groupings into the necessary cryptic entries in a join table it created for that purpose. It allowed us with easy, readable code to establish and probe these relationships. But, remember that we made this association bidirectional—the Artist class has a collection of Track associations, too. We didn’t bother to store anything in there.

The great news is that we don’t have to. Because we marked this as an inverse mapping in the Artist mapping document, Hibernate understands that when we add an Artist association to a Track, we’re implicitly adding that Track as an association to the Artist at the same time.


This convenience works only when you make changes to the “primary” mapping, in which case they propagate to the inverse mapping. If you make changes only to the inverse mapping—in our case, the Set of tracks in the Artist object—they will not be persisted. This unfortunately means your code must be sensitive to which mapping is the inverse.

Let’s build a simple interactive graphical application that can help us check whether the artist-to-track links really show up. When you type in an artist’s name, it will show you all the tracks associated with that artist. A lot of the code is very similar to our first query test. Create the file QueryTest2.java and enter the code shown in Example 4-12.

Example 4-12. Source for QueryTest2.java

package com.oreilly.hh;

import org.hibernate.*;
import org.hibernate.cfg.Configuration;

import com.oreilly.hh.data.*;

import java.sql.Time;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

 * Provide a user interface to enter artist names and see their tracks.
public class QueryTest2 extends JPanel {

    JList list;  // Will contain tracks associated with current artist
    DefaultListModel model; // Lets us manipulate the list contents

     * Build the panel containing UI elements
    public QueryTest2() {
        setLayout(new BorderLayout());
        model = new DefaultListModel();
        list = new JList(model);
        add(new JScrollPane(list), BorderLayout.SOUTH);

        final JTextField artistField = new JTextField(28);
        artistField.addKeyListener(new KeyAdapter() { 1
                public void keyTyped(KeyEvent e) { 2
                    SwingUtilities.invokeLater(new Runnable() { 3
                            public void run() {
        add(artistField, BorderLayout.EAST);
        add(new JLabel("Artist: "), BorderLayout.WEST);

     * Update the list to contain the tracks associated with an artist
    private void updateTracks(String name) { 4
        model.removeAllElements();  // Clear out previous tracks
        if (name.length() < 1) return;   // Nothing to do
        try {
            // Ask for a session using the JDBC information we've configured
            Session session = sessionFactory.openSession(); 5
            try {
                Artist artist = CreateTest.getArtist(name, false, session);
                if (artist == null) {  // Unknown artist
                    model.addElement("Artist not found");
                // List the tracks associated with the artist
                for (Track aTrack : artist.getTracks()) { 6
                    model.addElement("Track: \"" + aTrack.getTitle() +
                                     "\", " + aTrack.getPlayTime());
            } finally { 7
                // No matter what, close the session
        } catch (Exception e) {
            System.err.println("Problem updating tracks:" + e);

    private static SessionFactory sessionFactory;  // Used to talk to Hibernate

     * Set up Hibernate, then build and display the user interface.
    public static void main(String args[]) throws Exception {
        // Load configuration properties, read mappings for persistent classes
        Configuration config = new Configuration(); 8

        // Get the session factory we can use for persistence
        sessionFactory = config.buildSessionFactory();

        // Set up the UI
        JFrame frame = new JFrame("Artist Track Lookup"); 9
        frame.setContentPane(new QueryTest2());
        frame.setSize(400, 180);


Yes, this is a shameless plug.

The bulk of the novel code in this example deals with setting up a Swing user interface. It’s actually a rather primitive interface, and won’t resize nicely, but dealing with such details would make the code larger, and really falls outside the scope of this book. If you want examples of how to build rich, quality, Swing interfaces, check out our Java Swing, Second Edition (O’Reilly). It’s much thicker so it has room for all that good stuff.


The only item I want to highlight in the constructor is the KeyListener that gets added to artistField. This rather tricky bit of code creates an anonymous class whose keyTyped() method is invoked whenever the user types in the artist text field.


That method tries to update the track display by checking whether the field now contains a recognized artist name.


Unfortunately, at the time the method gets invoked, the text field has not yet been updated to reflect the latest keystroke, so we’re forced to defer the actual display update to a second anonymous class (this Runnable instance) via the invokeLater() method of SwingUtilities. This technique causes the update to happen when Swing “gets around to it,” which in our case means the text field will have finished updating itself.


The updateTracks() method that gets called at that point is where the interesting Hibernate stuff happens. It starts by clearing the list, discarding any tracks it might have previously been displaying. If the artist name is empty, that’s all it does.


Otherwise, it opens a Hibernate session and tries to look up the artist using the getArtist() method we wrote in CreateTest. This time we tell it not to create an artist if it can’t find the one for which we asked, so we’ll get back a null if the user hasn’t typed the name of a known artist. If that’s the case, we just display a message to that effect.


If we do find an Artist record, on the other hand,we iterate over any Track records found in the artist’s set of associated tracks, and display information about each one. All this will test whether the inverse association has worked the way we’d like it to.


Finally (no pun intended), we make sure to close the session when we’re leaving the method, even through an exception. You don’t want to leak sessions—that’s a good way to bog down and crash your whole database environment.


The main() method starts out with the same Hibernate configuration steps we’ve seen before.


It then creates and displays the user interface frame, and sets the interface up to end the program when it’s closed. After displaying the frame, main() returns. From that point on, the Swing event loop is in control.

Once you’ve created (or downloaded) this source file, you also need to add a new target, shown in Example 4-13, to the end of build.xml (the Ant build file) to invoke this new class.


This is very similar to the existing qtest target; copy and tweak that.

Example 4-13. Ant target for running the new query test

<target name="qtest2" description="Run a simple Artist exploration GUI"
  <java classname="com.oreilly.hh.QueryTest2" fork="true">
    <classpath refid="project.class.path"/>

Now you can fire it up by typing ant qtest2 and playing with it yourself. Figure 4-3 shows the program in action, displaying tracks for one of the artists in our sample data.

A very simple artist tracks browser

Figure 4-3. A very simple artist tracks browser

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required