Working with Hibernate in Eclipse
Working with Hibernate in Eclipseby James Elliott, author of Hibernate: A Developer's Notebook
I recently started using Eclipse as my development environment, in part because of its support for the many platforms on which I develop, and in part because Eclipse is a great example of the power of an open, extensible environment in which people all around the world can contribute. I'm beginning to investigate the extensions people have come up with. For example, I use a little plugin called XMLBuddy to work with XML files, and it's very helpful. So I became curious about whether anyone had written plugins to work with Hibernate, since I've done so much of that recently in putting together the Developer's Notebook. It turns out there are several such efforts underway; in this article we will explore one of them -- the Hibernate Synchronizer.
Of the plugins I've found so far, the Hibernate Synchronizer interested me most because it seems to best support the kind of mapping-centric workflow I adopted throughout my Developer's Notebook. (Hibernate can be used in many different ways, so check out the other plugins available; these may be more helpful if your environment calls for another approach.) In fact, the Hibernate Synchronizer plugin removes the need for you to think about updating your Java code when you change your mapping document. In a very Eclipse-like way, it automatically updates the Java code as you edit the mapping. But it goes even farther than Hibernate's built-in code generation tools by creating a pair of classes for each mapped object. It "owns" a base class, which it rewrites at will as you change the mapping, and gives you a subclass that extends this base class, where you can put business logic and other code, without fear that it will ever get changed out from under you.
As befits an approach centered around the Hibernate mapping document, Hibernate Synchronizer includes a new editor component for Eclipse that provides intelligent assistance and code completion for such documents. A nice DTD-driven XML editor, such as the aforementioned XMLBuddy, can do some of this for you, but Hibernate Synchronizer uses its semantic understanding of the documents to go much further. It also offers a graphical view of the properties and relations in the mapping, "wizard" interfaces for creating new elements, and other such niceties. And, as mentioned, in its default configuration the editor automatically regenerates the data-access classes as you edit their mapping documents.
There are other pieces to Hibernate Synchronizer, too. It adds a section to Eclipse's
New menu that provides wizards for creating Hibernate configuration and mapping files, and adds contextual menu entries in the package explorer and in other appropriate places, providing easy access to relevant Hibernate operations.
OK, enough abstract description, time to get down to the practical stuff! After all, you were already probably interested in this, or you wouldn't have started to read the article. So how do you get and play with Hibernate Synchronizer?
Hibernate Synchronizer is installed using Eclipse's built-in Update Manager. The plugin offers separate update sites for users of Eclipse 2.1 and the forthcoming Eclipse 3. Because I'm using Eclipse for mission-critical work, I'm still using the production release, 2.1. As I write this, Eclipse 3 has entered its "release candidate" phase, and I am very much looking forward to being able to upgrade to a production release of version 3 when I return from JavaOne later this summer. (The main reason I mention this is to emphasize that the following instructions are written from an Eclipse 2 perspective; some commands and screens are undoubtedly different in Eclipse 3, so if you're using it, be sure to apply your own judgment in following these steps! If it helps, my impression is that Hibernate Synchronizer's own install instructions are written for Eclipse 3.)
Fire up Eclipse and open the Update Manager by choosing
Software Updates ->
Update Manager. Once the Install/Update perspective opens up, right-click (or control-click, if you're using a one-button mouse) in the Feature Updates view and choose
Site Bookmark, as shown in Figure 1.
Figure 1. Adding the Hibernate Synchronizer plugin site to the Update Manager
In the resulting dialog, enter the URL for the version of the plugin that you need. The URL to be entered depends on your Eclipse version:
- Eclipse 2.1:
- Eclipse 3:
You also need to assign a name for the new bookmark. "Hibernate Synchronizer" makes a lot of sense. Figure 2 shows the dialog with all required information in my Eclipse 2.1.2 environment. Once you've got it filled in, click
Finish to add the bookmark.
Figure 2. Bookmark for the Hibernate Synchronizer plugin update site
Once you click Finish, the new bookmark will appear in the Feature Updates view, as shown in Figure 3.
Figure 3. The Hibernate Synchronizer site is now available for use
To actually install the plugin, click on the disclosure triangle to the left of the bookmark, and again on the next one that appears inside of it, until you can see the icon for the plugin itself. When you click on that, the Preview view will update to show you an interface that allows you to install the plugin, as shown in Figure 4.
Figure 4. Ready to install the plugin
Install Now to actually install it, and let Eclipse walk you through the process (Figures 5-10).
Figure 5. Installing Hibernate Synchronizer
Figure 6. The plugin license agreement
See Trade-Offs, below, for some discussion about this license agreement. You may wish to read it carefully before deciding to use Hibernate Synchronizer in a project of your own. I think it's probably fine, but it is confusingly based on the GPL without actually being open source.
Figure 7. Choosing where to install the plugin; the default is fine
Figure 8. The standard warning for unsigned plugins
Figure 9. The install is underway
Figure 10. The install has completed
Now that the plugin is installed, you need to quit and relaunch Eclipse for it to take effect. The dialog seems to imply that Eclipse will restart itself, but in my experience, clicking
Yes merely causes the environment to quit, and you have to relaunch it manually. This may be a limitation of Eclipse 2.1's Mac OS X implementation; Eclipse 3 is going to be the first release that promises "first-class" support for OS X. In any case, this is a very minor issue. If you need to restart Eclipse, do so now, because it's time to start configuring the plugin to put it through its paces!
Once Eclipse comes back up, you can close the Install/Update perspective. Open a Java project that uses Hibernate. If you've been going through the examples in the Developer's Notebook, you'll have several directories from which to choose. I'll be looking at the examples as they exist in Chapter 3, which is the sample chapter available online. You can also download the source for all of the examples from the book's site.
If you're creating a new Eclipse project to work with one of the example source directories, just choose
Project, specify that you want to create a Java project and click
Next, give it a name ("Hibernate Ch3" in my case, as shown in Figure 11), uncheck the
Use default checkbox so that you can tell Eclipse where to find the existing project directory, and hit the
Browse button to locate where it exists on your own drive. At this point, you can click
Finish to create the project, but I generally like to click
Next and double-check the decisions Eclipse is making. (Of course, if it gets anything wrong, you can always go back and fix the project properties, but I tend to find it disconcerting to be greeted by a ton of errors and warnings immediately if there is a library missing or something.)
Figure 11. Creating a new project to work with Hibernate
In this case, my caution was unnecessary. Eclipse figured out exactly how the directory was structured and intended to be used, and found all of the third-party libraries I had downloaded and installed in order to enable Hibernate and the HSQLDB database engine to run. (A detailed walkthrough of this process is the bulk of Chapter 1 of my Developer's Notebook.) This kind of smart adaptability is one of the great features of Eclipse. Figure 12 shows our new project open and ready for experimentation. It also shows that Eclipse doesn't like to fit into a window small enough for a reasonable screen shot; I'm going to have to work with partial window captures from this point on.
Figure 12. The Chapter 3 example project
The next thing we need to do is create a Hibernate configuration file that Hibernate Synchronizer can use. There is already a hibernate.properties file in the src directory, which is how the examples in the book work, but Hibernate Synchronizer only works with Hibernate's XML-based configuration approach. So we'll need to replicate the contents of hibernate.properties into a new hibernate.cfg.xml file. On the bright side, this gives us our first opportunity to play with a feature of Hibernate Synchronizer, the configuration file wizard. Choose
Other, click the newly available
Hibernate category, pick
Hibernate Configuration File, and click
Figure 13. Starting the Hibernate Configuration File wizard
When the wizard starts up, the directory it offers to put the file into depends on the file you've currently got selected in Eclipse. Let's be sure to put it at the top-level src directory alongside the properties version, for consistency. Fill in the rest of the information requested by the wizard to match the properties version of the configuration, as shown in Figure 14. Notice that, unlike when using Ant to control the execution of Hibernate (which was the approach used in the Developer's Notebook), we have no way to control the current working directory when Hibernate is invoked, so we need to use a fully qualified path to the database file in the URL. In my case, this takes the (somewhat ungainly) value jdbc:hsqldb:/Users/jim/Documents/Work/OReilly/Hibernate/Examples/ch03/data/music. (If anyone can tell me how to get Eclipse or Hibernate Synchronizer to use a particular working directory for a project, I'd certainly be interested. I'm still a beginner when it comes to Eclipse, so it would not surprise me at all to learn that this is possible and that I simply don't know how to do it.)
Figure 14. Filling in the configuration file details
Filling in the Driver Class is a little strange: You need to click the
Browse button, and start typing the driver name. If you type "jdbcD", the window will present only two choices, and you can easily click the right one. This is illustrated in Figure 15.
Figure 15. Specifying the HSQLDB driver class
Once the wizard is set up to the extent of Figure 14, with values appropriate for your own installation, you can click
Finish to create the configuration file. Hibernate Synchronizer is now ready to use. It opens the file it created so you can see the structure and details of an XML configuration file for Hibernate.
Figure 16. The generated configuration file
A quick way to test that the configuration is working is to play with the other wizard interface. Choose
Other, click the newly available
Hibernate category, pick
Hibernate Mapping File, and click
Next. When the wizard comes up, it should be populated with the settings information we just entered, and you can click the
Refresh button to make sure it can communicate with the database and show you that it found a
TRACK table. The first time you do this, you might have to confirm the location of the .jar file containing the HSQLDB driver, for some reason, but that seems to happen only once. In any case, once you confirm that everything seems to be working, click
Cancel rather than actually creating the mapping, because we want to work with our hand-created mapping file that already exists.
This is probably the part you've been waiting for. What cool stuff can we do? Well, right away there is a new contextual menu entry available for Hibernate mapping documents.
If you right-click (or control-click) on one, you get a number of Hibernate-related choices (Figure 17), including one to synchronize. This is a manual way to ask Hibernate Synchronizer to generate the data access objects associated with the mapping document.
Figure 17. Synchronizer choices for mapping documents
Add Mapping Reference choice is also useful: it adds an entry to the main Hibernate configuration file telling it about this mapping document, so you don't need to put anything in your source code to request that the corresponding mapping gets set up. For now, let's look at the result of choosing
This is where things start to get interesting. We end up with two new sub-packages, one for the "base" data access objects that Hibernate Synchronizer "owns" and can rewrite at any time, and one for our business objects that subclass these DAOs, which will not get overwritten, and give us an opportunity to add business logic to the data class (shown in Figure 18).
Figure 18. The synchronized data access objects, showing our editable subclass
There are many more classes generated this way than by using the normal Hibernate code generation facilities, which has advantages, as well as some potential disadvantages, which I discuss later in the Trade-Offs section. Note also that in the properties configuration for your project, you can choose which of these classes get generated for you, as well as the package structure into which they are generated. I'd demonstrate this, but the current release of the plugin has a bug which blocks access to this configuration interface on Mac OS X. A fix has been made, but not yet released.
Based on the examples on the Hibernate Synchronizer page, I put together the following class to try inserting some data into the music database using these new data access objects. It's quite similar to the version using the standard Hibernate code generator (on pages 39-40 of Hibernate: A Developer's Notebook) and even simpler because the classes generated by Hibernate Synchronizer create and commit a new transaction for each of your database operations, so you don't need any code to set one up in simple situations like this. (There are ways of doing so if you need to have a group of operations operate as a single transaction, of course.) Here's the code for the new version:
Having Eclipse around while I was writing this was very nice. I'd forgotten how much I missed intelligent code completion while I was writing the examples for the book, and there are several other things the JDT helps with too.
To run this simple program within Eclipse, we need to set up a new
Run configuration. Choose
Run... with CreateTest2.java as the currently active editor file. Click on
New and Eclipse figures out that we want to run this class in our current project, because we created it with a
main() method. The default name it assigns,
CreateTest2, is fine. The screen will look something like Figure 19. Click
Run to try creating some data.
Figure 19. Ready to run our creation test in Eclipse
If you've been exactly following along on your own, you'll find that this first attempt at execution fails: Hibernate complains that the configuration file contains no mapping references, and at least one is required. Ah ha! So that's what XMLBuddy was warning about with the yellow underline near the bottom of Figure 16. We can easily fix this by right-clicking on the Track.hbm.xml mapping document in the Package Explorer view and choosing
Add Mapping Reference in the new Hibernate Synchronizer submenu. That makes XMLBuddy happy, and allows the run to get further. Unfortunately, not as far as we might like, though. The next error was a complaint about not being able to find the JTA
UserTransaction initial context in JNDI. It turned out I wasn't the only person having this problem; it was discussed in a forum thread, but no one had yet found a solution.
Since I knew I didn't need to use JTA, I wondered why Hibernate was even trying. I opened up the Hibernate configuration file (Figure 16) and looked for anything suspicious that Hibernate Synchronizer had put there. Sure enough, there were some lines that looked like prime suspects:
Once I tried commenting these out and running again, the third time was indeed the charm. My run completed with no errors, and my data appeared in the database. Hurrah! Running the trusty
db target (explained in Chapter 1 of the Developer's Notebook) reveals the data in all its (admittedly simple) glory, as shown in Figure 20. If you're doing this yourself, be sure to start with an
schema to create the database schema or empty out any test data that may be there from previous experimentation.
Figure 20. The data created by our test program
Note that you can run Ant targets from within Eclipse by right-clicking (or control-clicking) on the build.xml file within the Package Explorer, choosing
Run Ant and picking the target using an Eclipse dialog. Pretty cool.
Figure 21. Running Ant from within Eclipse
Getting data back out using queries is pretty straightforward, although this time it's a lot closer to the same code you'd use with the ordinary Hibernate-generated data access classes. Even though Hibernate Synchronizer generates a number of helper methods for working with named queries, I don't think any of them is particularly useful, because they all insist on running the query and returning the list of results, rather than giving you the
Query object to work with yourself. That prevents you from using any of
Query's convenient type-safe parameter setting methods. Because of that, I decided to stick to having the
_RootDAO object give me a Hibernate
Session to work with the "old fashioned" way. In fairness, I think I could edit the templates used by Hibernate Synchronizer to generate any methods I'd like, and would almost certainly look into doing that if I was going to undertake a project with it.
Actually, on further reflection, because you can only work with a
Query while you've got an active
Session, the methods offered by the DAOs already work the best way they possibly can. You're always going to have to do your own session management if you want to work with the query the way I do in this example. You could embed the session management into the business logic provided in "your" half of the DAO, though, which would give you the best of both worlds. That's another reason the split-class model offered by Hibernate Synchronizer is so useful. I explore this insight a bit below.
Anyway, here's the code I first came up with, morally quite equivalent to that on pages 48-49 of the book:
One nice feature that
TrackDAO does give us is a static constant by which we can request the named query, eliminating any chances of run-time errors due to typos in string literals. I appreciate that! Setting up and executing a
Run configuration for this test class produces the output I'd expect, as shown in Figure 22.
Figure 22. The query results in Eclipse's console view
As I noted above, after getting this class working, I realized there was a better way to approach it, given the model offered by Hibernate Synchronizer. Here's what our
TrackDAO object would look like if we moved the query inside of it, which is where it really belongs, given that the named query is a feature of the mapping file associated with that data access object:
This is nice and clean, and it simplifies the
main() method in
QueryTest3 even more:
Clearly this is the approach to take when working with named queries and Hibernate Synchronizer. A quick test confirms that it produces the same output, and it's much better code.
Whether or not you want to use Hibernate Synchronizer to generate its own style of data access objects, there is one last major feature to explore.
One of the main attractions of Hibernate Synchronizer is its specialized editor for mapping documents. This editor can be configured to automatically regenerate the associated data objects whenever you save files, but that's just a final touch; you might want to use the editor even if you're not using the plugin's code generator. It gives you smart completion of mapping document elements, and a graphical outline view in which you can manipulate them, as well.
There is a trick to getting the editor to work for you, though, at least if you're starting from the downloaded source code from my Developer's Notebook. In the download, the mapping documents are named with the extension ".hbm.xml," and the editor is only invoked for files ending with ".hbm". In theory, you can configure the extension mappings within Eclipse so that both extensions use the plugin's mapping document editor, but I wasn't able to get that to work, and I saw that someone else on the support forum had the same problem. So, at least for now, your best bet may be to rename the files. (If you're going to stick with Ant-based standard code generation, be sure to update the
codegen target in build.xml to use the new extension, too.)
As soon as I renamed Track.hbm.xml to Track.hbm, its icon in the Package Explorer was updated to look like the Hibernate logo, and the default editor became the plugin's, as shown in Figure 23. For whatever reason, the other Hibernate Synchronizer options (as shown in Figure 17) are available with either extension, but the editor is available only with the shorter version.
Figure 23. The contextual menu for a Hibernate mapping document (with the extension ".hbm")
The editor has context-sensitive completion support for all of the elements you're adding within the mapping document. Figure 24 shows a couple of examples, but no screen shots can really capture the depth and usefulness of a feature like this; I'd very much encourage you to install the plugin and play with it yourself for a while. You will quickly see how helpful it can be in working with mapping documents.
Figures 24 and 25. Completion assistance in the mapping document editor
The outline view, shown in Figure 26, gives you a graphical view of the hierarchy of classes, their mapped elements, named queries, and the like that are present in your mapping document, as well as giving you a menu offering a few wizards to help you create new ones.
Figures 26 and 27. The mapping editor's outline view, and the "Add property" wizard
The contextual menu within the editor itself also offers a
Format Source Code option you can use to clean up and re-flow the document. There are already many neat and useful features in this editor, and it'll be interesting to see how it grows in the future. My only complaint (and a minor one at that) is that this editor uses a very different approach to helping you manage quotation marks when you complete XML attributes than the JDT does in Java code. Switching back and forth between them can be somewhat disorienting. (The way the JDT works takes a little getting used to itself, but once you start trusting it, it's almost magical.)
Despite my first impression that everything flowed from the mapping document, Hibernate Synchronizer doesn't currently offer any support for creating or updating a database schema from your mapping documents. There has already been a request posted to the support forum about this, and it wouldn't surprise me if we saw these features in the future; support shouldn't be too difficult. For now, you'll have to stick with an approach like the Ant-driven one in Hibernate: A Developer's Notebook if you're developing your database from your mappings. Alternately, the Hibernator plugin described below does support schema updates from within Eclipse. I may have to investigate whether it's possible to have both of these plugins installed at the same time.
Well, I certainly hope this whirlwind tour has given you an sense of the capabilities offered by the plugin. I haven't covered all of them, by any means, so do download it and explore on your own if anything has intrigued you.
Clearly you can do some neat things with Hibernate Synchronizer. Will I be using it for my own Hibernate projects? There are some pluses and minuses to that idea, and I probably won't decide until I get to the point of actually adopting Hibernate in place of our homebrew (and very simplistic) lightweight O/R tool at work. That is going to be a significant enough change that we are putting it off until we tackle a major architecture shift that's on the horizon for other reasons. Here are some of the factors that will weigh in my decision.
As mentioned in the Installation section, there is a little bit of concern about the license out there. The plugin's forum has a discussion about this. The current license is based on a custom modification of the GNU GPL that removes all the source-sharing provisions, but tries to retain the other aspects of "copyleft" protection. There is some question about the legitimacy of this, and the author is looking for an alternative. It is clear that the intention is to protect the plugin, not to encumber any other project that happens to use the plugin to generate code, but it may be worth carefully reading the current license to see if you believe that intent has been achieved, or if there is too much risk for you.
The same discussion reveals that the author had originally released the plugin as open source, but withdrew it temporarily because he felt it wasn't yet polished enough to serve as a good example to others. He then had some very annoying email interactions with hotheads who, sadly, soured him on the whole idea of sharing the source. It is certainly his prerogative to decide what, if anything, to share with us. The plugin is a gift to the world, and the author doesn't owe us anything. But I hope that enough positive interactions with other users might help convince him to go back to his original plan of sharing the source. I really value having access to the source code of tools that I use, not only because it is a very valuable learning opportunity, but because it means I (or others) can fix little problems immediately if we need to. The author has been very responsive so far in addressing user concerns, but no one person can keep up as well as a community, and we all sometimes get busy, burned out, or otherwise distracted.
The fact that Hibernate Synchronizer uses its own templates and mechanism to generate your data access class is both positive and negative. It's positive in that it gives you more capabilities than Hibernate's "standard" code generation tools. The ability to work with an auto-generated subclass of your data object in which you can embed business logic without fear of it getting overwritten when you regenerate the access code is a big plus. And there are other niceties offered by the plugin's generated classes that make many of the simple cases even simpler.
On the other hand, this also means that Hibernate Synchronizer's generated code can lag behind Hibernate when there are new features added or changes made to the platform. The plugin's code is also more likely to have bugs in its support for Hibernate's less-used modes: it has a much smaller user base, and a single person keeping it updated. You can see evidence of this phenomenon on the discussion forum.
As with so many things, it's up to you to decide whether the potential benefits outweigh the risks. Even if you don't use the code generator, you might find the mapping editor extremely useful. You can turn off automatic synchronization if you want to just use the editor's completion and assistance features.
If you do adopt the plugin and find it useful, I would definitely encourage you to contact the author and thank him, and consider donating some money to help support its further development.
In my hunting so far, I've encountered two more plugins that offer support for Hibernate within Eclipse. (If you know of others, or come across them in the future, I'd be interested in learning about them.) Perhaps I'll write articles about these in the future.
The HiberClipse plugin looks like another very useful tool. It seems geared towards a database-driven workflow, where you've already got a database schema and want to build a Hibernate mapping file and Java classes to work with it. This is a common scenario, and if you find yourself facing such a challenge, I'd definitely recommend checking out this plugin. One really cool feature it offers is a graphical "relationships view" of the database you're working with, right within Eclipse. (I should point out that Hibernate Synchronizer doesn't leave you high and dry if you want to start with an existing database schema, either. Its New Mapping File Wizard can connect to your database and build the mapping file based on what it finds.)
Figure 28. Hibernate Synchronizer's Mapping Wizard
Finally, Hibernator seems to lean in the opposite direction, starting from your Java code to generate a simple Hibernate mapping document, and then from there letting you build (or update) the database schema. It also offers the ability to run database queries within Eclipse. Of the three plugins, it appears to be at the earliest stages of development, but already looks worth keeping an eye on, especially since it cites members of the Hibernate development team as contributors.
If I've managed to pique your interest in this article, there are plenty of resources to help you dig deeper into these topics. In addition to the sites I've linked to throughout the text, there are some books that might interest you. Of course, I have to mention my own, Hibernate: A Developer's Notebook. For in-depth reference material about Hibernate, the online documentation is very useful, especially the reference manual, and there is a forthcoming book by the developers of Hibernate itself, Hibernate in Action. I look forward to reading that myself.
As for Eclipse, I'm currently working through Steve Holzner's Eclipse and looking forward to the Eclipse Cookbook that will be released later this month. My blog discusses my Eclipse "conversion" in more detail in case you're curious about that (or teetering on the edge yourself). If you're just getting started, be sure to explore the "Getting Started" sections of Eclipse's built-in Workbench and Java Development user guides. These show you how the environment is intended to be used, give you some good suggestions, and walk you through processes and features you might not otherwise discover quickly on your own. Choose
Help Contents within Eclipse to find them.