Monday, June 11, 2007

Eclipse/Java: Using Hibernate for Data Access

I have been wanting to do a little work with Hibernate for some time now. I finally have a project that has come up where I can get the excuse to do so. The first step, however, was to get myself familiar with how Hibernate works.

For the better part of a few hours I have been messing around with getting Hibernate Synchronizer to work with Eclipse. There are quite a few annoying bugs, but so far it has been the best of the Hibernate Eclipse plugins. And I also used Hibernate version 3 as my library in my project. Since my test database is a SQL Server database, I also used jTDS JDBC Driver for Sql Server. I used this instead of the Microsoft driver since the Microsoft driver just plain didn’t work with Hibernate. For the record, I am using Eclipse 3.3 since the project I am working with uses BIRT 2.2 M6 reports, and I wanted my development under one roof, and its using JDK 1.5 R11.

The first step of my process was to create a regular Java project under Eclipse. I am sure to include the Hibernate Jar file and the jTDS Jar file in my classpath/included libraries.

Once created, I go to New, Other, and select Hibernate Configuration File. This will bring up the Hibernate Configuration File Utility as shown in Figure 1. Be sure you get your configuration right since this is the last time you will see this screen once you Finish. I have yet to figure out how to bring this back up. I enter the relevant information as shown.

Figure 1. The Hibernate Config Wizard

Next, I need to create the mapping file so Hibernate can generate my proxy classes to handle communication with the database and provide general getters, setters, and other classes. So I go to File/New/Other/Hibernate/Hibernate Mapping File. Assuming everything is OK with my connection, on the next screen I can click on the refresh button to get a list of the tables available for mapping. In my case, I ran into a bug using the Microsoft JDBC drivers here that wouldn’t let me connect. From my list of tables, I want a class to handle Opportunities, so I select the appropriate table from the list. Once done, I move over to the properties tab. Since this is SQL Server, I need to change the ID generator over from Sequences to Identity. Not sure exactly why this is necessary, but I’m not going to argue the point either. With Derby I was able to use Sequences, and I’d imagine Oracle would work just fine with this as well. If you use the wrong type, you will get an error when you run your resulting program complaining that the generator type is not supported in that database.

Figure 2. The Mapping Screen

Figure 3. The Mapping Properties

With the mapping file complete, now I can go through the motions of finishing up the configuration and generating the classes. First, I right mouse click on .hbm.xml file that gets creating with the mapping, and I select Add Mapping Reference. I encountered a bug here where when adding the mapping, it deletes the XML version and DOCTYPE tags from the hibernate.cfg.xml file, so be sure you have those handy just in case. If not, you will encounter an error when your finished program tries to access the hibernate configuration. Next, I open up the hbm.xml file, and change the value of the meta attribute=”sync-DAO” tag to true. This will generate the DAO objects for my test program. I am not sure why this defaults to false. Once done, I can save the file, then right mouse click on the hbm.xml file, and under the Hibernate Synchronizer menu, I select Synchronize Files. This will generate the needed classes.

That’s pretty much it. Now, I can use Hibernate to access my database. This saves me a lot of time. Instead of having to generate all the ORM classes myself, I can let Hibernate do the leg work for me. There is also a version of Hibernate for .Net. A few notes of bugs I came across. Like I mentioned above, the add Mapping Reference wipes out the XML header from the hibernate.cfg.xml file. I didn’t realize this until I went to run my program and Hibernate couldn’t recognize the format. The error I got was:

Exception in thread "main" org.hibernate.InvalidMappingException: Could not parse mapping document from resource TableOpportunity.hbm.xml
at org.hibernate.cfg.Configuration.addResource(Configuration.java:569)
at org.hibernate.cfg.Configuration.parseMappingElement(Configuration.java:1587)
at org.hibernate.cfg.Configuration.parseSessionFactory(Configuration.java:1555)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1534)
at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1508)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1428)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1414)
at com.digiassn.blogspot.HibernateClasses.base._BaseRootDAO.initialize(_BaseRootDAO.java:98)
at com.digiassn.blogspot.HibernateClasses.base._BaseRootDAO.initialize(_BaseRootDAO.java:88)
at com.digiassn.blogspot.HibernateClasses.base._BaseRootDAO.initialize(_BaseRootDAO.java:79)
at com.digiassn.blogspot.TestHibernate.main(TestHibernate.java:13)
Caused by: org.hibernate.InvalidMappingException: Could not parse mapping document from invalid mapping
at org.hibernate.cfg.Configuration.addInputStream(Configuration.java:502)
at org.hibernate.cfg.Configuration.addResource(Configuration.java:566)
... 10 more
Caused by: org.xml.sax.SAXParseException: Document is invalid: no grammar found.
at org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
at org.apache.xerces.util.ErrorHandlerWrapper.error(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl$NSContentDispatcher.scanRootElementHook(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
at org.dom4j.io.SAXReader.read(SAXReader.java:465)
at org.hibernate.cfg.Configuration.addInputStream(Configuration.java:499)
... 11 more


I also got errors when I didn’t include all of the necessary LIB files for Hibernate. This one confused me, since I thought the necessary files would be in the Hibernate3.jar file. However, I was mistaken. So be sure to include those. The exact error I got was:

Exception in thread "main" java.lang.NoClassDefFoundError: org/dom4j/DocumentException
at com.digiassn.blogspot.HibernateClasses.base._BaseRootDAO.getNewConfiguration(_BaseRootDAO.java:192)
at com.digiassn.blogspot.HibernateClasses.base._BaseRootDAO.initialize(_BaseRootDAO.java:90)
at com.digiassn.blogspot.HibernateClasses.base._BaseRootDAO.initialize(_BaseRootDAO.java:79)
at com.digiassn.blogspot.TestHibernate.main(TestHibernate.java:13)



The third main issue I ran into was the fact that Hibernate refused to find my hibernate.cfg.xml file. This was pretty annoying. The error looked like so:


Exception in thread "main" org.hibernate.HibernateException: /hibernate.cfg.xml not found
at org.hibernate.util.ConfigHelper.getResourceAsStream(ConfigHelper.java:147)
at org.hibernate.cfg.Configuration.getConfigurationInputStream(Configuration.java:1405)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1427)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1414)
at com.digiassn.blogspot.HibernateClasses.base._BaseRootDAO.initialize(_BaseRootDAO.java:98)
at com.digiassn.blogspot.HibernateClasses.base._BaseRootDAO.initialize(_BaseRootDAO.java:88)
at com.digiassn.blogspot.HibernateClasses.base._BaseRootDAO.initialize(_BaseRootDAO.java:79)
at com.digiassn.blogspot.TestHibernate.main(TestHibernate.java:13)

What I ended up having to do was under the run configuration for my project, I had to specifically add the Project folders root, where my hibernate.cfg.xml file was located, to my classpath. I did this under the Classpath tab, clicking on User Entries, then advanced, Add Folder, and pointed to the root of my project.

Figure 4. Adding the path for the hibernate.cfg.xml

Now, for the example program I used to test the configuration. Basically I just want to to get all Opportunities from my table and display a couple of fields for them. My test program ends up looking like so:

package com.digiassn.blogspot;

import java.util.Iterator;
import java.util.List;

import com.digiassn.blogspot.HibernateClasses.TableOpportunity;
import com.digiassn.blogspot.HibernateClasses.dao.TableOpportunityDAO;
import com.digiassn.blogspot.HibernateClasses.dao._RootDAO;

public class TestHibernate {
public static void main(String[] args) {
_RootDAO.initialize();

TableOpportunityDAO td = new TableOpportunityDAO();
List opps = td.findAll();

for (Iterator i = opps.listIterator(); i.hasNext();) {
TableOpportunity t = (TableOpportunity) i.next();
System.out.println(t.getName() + " - " + t.getFrcstClsAmount()
+ " - " + t.getServiceStartDate() + " - "
+ t.getServiceBranch());
}

}
}

1 comment:

Brandon said...

Thanks for taking the time to write this up - it has been a great help! I haven't used synchronizer for a couple of years now and kinda forgot its little quirks. The meta attribute "sync-DAO" trick saved me from pulling out ALL of my hair.