Google


Hibernate / Middlegen - Inheritance and Many To Many v1.0

Part 2 in the Warfrog.com tutorial series

written by Tyler Pitchford
http://www.warfrog.com


Before we begin


What this tutorial covers

This tutorial covers how to modify the hbm.xml files generated by Middlegen to correctly produce Inheritance and Many to Many relationships when processed by HBM2JAVA. Furthermore, this tutorial assumes you have a working knowledge of these technologies and their related terminology.

What this tutorial doesn't cover

This tutorial does not cover the basics of Hibernate, Middlegen, etc.    If you're new to the world of Hibernate and Middlegen I'd suggest you read the first part of this tutorial series located here.


Downloading / Installing optional software


Install ANT

Get newest version
URL: http://ant.apache.org/manual/index.html
Install: Follow instructions at the URL above


Setting up the project


Create the staging folder

Note: I picked C:\hibernatetutorial2, you can use whatever you like, but I'll be assuming C:\hibernatetutorial2 for the rest of this document.

Extract the hibernatetutorial2.zip to C:\hibernatetutorial2.  After that you'll need to open a command prompt to c:\hibernatetutorial2 and type "ant setup-dirs"

OR if you've decided not to use ANT

Create the following directories:


Time to install some software


Note: Every "extract the zip file..." line below means extract the zip file directly to the folder.  Do not change the root dir and/or "extract to FILENAME directory."  There may be some repetitive folder names, just ignore them (ex. C:\hibernatetutorial\apps\hsqldb\hsqldb ). If you change the root directory you'll need to manually edit the build.xml, I've provided, to point to the correct application roots.  Additionally, if any of the versions of the software listed below change you'll need to update the build.xml.

Install HSQLDB

Download: hsqldb_1_7_X.zip
File used in this tutorial: hsqldb_1_7_1.zip
URL: http://sourceforge.net/project/showfiles.php?group_id=23316&release_id=117043
Install: Extract the zip file to the hsqldb directory within the apps directory

Install Hibernate

Download: hibernate-2.1.X.zip
File used in this tutorial: hibernate-2.1.3.zip
URL: http://prdownloads.sourceforge.net/hibernate/?sort_by=date&sort=desc
Install: Extract to the hibernate directory inside the apps directory

Install Hibernate extensions

Download: hibernate-extensions-2.1.X.zip
File used in this tutorial: hibernate-extensions-2.1.2.zip
URL: http://prdownloads.sourceforge.net/hibernate/?sort_by=date&sort=desc 
Install: Extract to the hibernate-ext directory inside the apps directory

Install Middlegen

Download: Middlegen-Hibernate-rX.zip
File used in this tutorial: Middlegen-Hibernate-r4.zip
URL: http://prdownloads.sourceforge.net/hibernate/?sort_by=date&sort=desc 
Install: Extract to the middlegen directory inside the apps directory

Open a command prompt to c:\hibernatetutorial2 and type "ant setup-middlegen"

OR if you've decided not to use ANT

Copy the following files:

to the C:\hibernatetutorial2\apps\middlegen\lib directory

Copy the following files:

to the C:\hibernatetutorial2\apps\middlegen\middlegen-lib directory.

Delete the following directories / files:

Copy the build.xml, provided in the hibernatetutorial2.zip, to C:\hibernatetutorial2\apps\middlegen\.  Additionally, copy the hsqldb.xml to the C:\hibernatetutorial2\apps\middlegen\config\database\hsqldb.xml.
 


Setup the HSQLDB database


Launch the HSQLDB GUI
 


Launch a command prompt to C:\hibernatetutorial2\apps\hsqldb\hsqldb\lib directory.
Launch the HSQLDB GUI by typing "java -cp hsqldb.jar org.hsqldb.util.DatabaseManager"
    Set the type to: HSQL Database Engine Standalone
    Set the URL to: jdbc:hsqldb:../../../../hsqldb/hibernatetutorial2
    Leave the rest of the settings alone
    Click Ok

Two files were just created that make up the hibernatetutorial2 database

Create the PERSONS Table

Paste the following code into the text box next to the execute button


CREATE TABLE PERSONS (
person_id int NOT NULL IDENTITY,
first_name LONGVARCHAR(255) NOT NULL,
last_name LONGVARCHAR(255) NOT NULL,
dob DATE NOT NULL,
ssn LONGVARCHAR(20) NOT NULL,
address_1 LONGVARCHAR(255) NOT NULL,
address_2 LONGVARCHAR(255),
phone LONGVARCHAR(50) NOT NULL,
active bit DEFAULT 'true' NOT NULL
)

Hit execute

Create the EMPLOYEES Table

Paste the following code into the text box next to the execute button


CREATE TABLE EMPLOYEES (
employee_id int NOT NULL PRIMARY KEY,
salary decimal(20,4) NOT NULL,
foreign key (employee_id) references persons (person_id)
)

Create the DEPARTMENTS Table

Paste the following code into the text box next to the execute button


CREATE TABLE DEPARTMENTS (
department_id int NOT NULL IDENTITY,
name LONGVARCHAR(50) NOT NULL,
description LONGVARCHAR(255)
)

Create the EMPLOYEES_DEPARTMENTS Table

Paste the following code into the text box next to the execute button


CREATE TABLE EMPLOYEES_DEPARTMENTS(
employee_id int NOT NULL,
department_id int NOT NULL,
foreign key (employee_id) references employees (employee_id),
foreign key (department_id) references departments (department_id),
primary key(employee_id, department_id)
)

Hit execute
Close the GUI but leave the console open, we'll be using it in a minute.

Launch the HSQLDB Server

You've just created the groups and users tables we'll be using in our tutorial.  Now all we have to do is launch the HSQLDB Server so we can access the database easily from our application. Open another command prompt to the C:\hibernatetutorial2\hsqldb directory.  Make sure you don't use the command prompt we launched the HSQLDB GUI from, create another command prompt, because we'll be using it in a minute to test our database. Launch the HSQLDB server by typing "java -cp ../apps/hsqldb/hsqldb/lib/hsqldb.jar org.hsqldb.Server -database hibernatetutorial2" at the command prompt.  If all went as planned you should see something similar to "Sat May 15 12:01:41 EDT 2004 Listening for connections ..." at the bottom of the command prompt.  However, if you see "java.sql.SQLException: The database is already in use by another process", make sure you've closed the HSQLDB GUI.

Test the HIBERNATETUTORIAL2 Database

Relaunch the HSQLDB GUI. 
    Set the type to: HSQL Database Engine Server
    Leave the rest of the settings alone
    Click Ok

Assuming everything has gone smoothly you should see the PERSONS, EMPLOYEES, DEPARTMENTS, and EMPLOYEES_DEPARTMENTS tables in the left-hand tree.  Congratulations, you're HSQLDB is now fully operation and you're ready to move on to generating you first Hibernate Mapping files. Once again, leave the HSQLDB GUI open.


Using Middlegen


Launching Middlegen

Open a command prompt to C:\hibernatetutorial2\apps\middlegen\Middlegen-Hibernate-r4 and type "ant middlegen." You should now see a screen containing your PERSONS, EMPLOYEES, DEPARTMENTS, and EMPLOYEES_DEPARTMENTS tables. Click on the title bar of the PERSONS table and change the Key Generator type to "Native." Repeat the same process for DEPARTMENTS table. Leave the EMPLOYEES_DEPARTMENTS and EMPLOYEES tables alone. Additionally, click on the PERSONS table and change the "Active" field to Boolean, just to be consistent with the other fields (they're all Objects). Click the Generate button and wait for it to turn back to light grey. Close the Middlegen GUI.

The middlegen plugin is still a work in progess. It greatly reduces the work load required to generate the hbm.xml files that both hbm2java and hibernate require, but it still requires a little manual labor. Middlegen attempts to automatically adjust the table names to proper class names (ex. table: users to class: User). Generally, middlegen does a great job at this, but in certain cases it makes a mistake or two. In our example Middlegen has converted employees_departments to EmployeesDepartment, because it removes the "s" it detected on the "departments" portion of the table name. Unfortunately, it didn't remove the "s" from employees for us. To fix this we'll need to make a few modifications to the EmployeesDepartment.hbm.xml file. First, rename the file to EmployeesDepartment.hbm.xml to EmployeeDepartment.hbm.xml. Next, open EmployeeDepartment.hbm.xml and modify the lines:


<class 
	name="com.warfrog.hibernatetutorial2.hibernate.EmployeesDepartment" 

to


<class 
	name="com.warfrog.hibernatetutorial2.hibernate.EmployeeDepartment" 

Save the file and exit the editor. Hopefully, in the near future the Middlegen-Hibernate plugin will allow users to edit the names used to generate the hbm.xml files, but until then we'll just have to modify them after generation.

Now that we have all of our files correctly named, we need to make a few more modifications to them. Middlegen currectly doesn't correctly generate the many-to-many relationships we want nor does it generate the inheritance mappings we're trying to achieve. Fortunately, the modifications we need to make are simple and Middlegen has handled a large part of the grunt work for us. Let's start with the inheritance modifications.
 


Inheritance


Setting up Inheritance

Since persons and employees have a one-to-one relationship, we want the generated java classes to have a relationship of Person (superclass) to Employee (subclass). To create the inheritance we're looking for we need to utilize hibernate's <joined-subclass> element. Open then Employee.hbm.xml and change:


<class
...
</class>

to


<joined-subclass
...
</joined-subclass>

Now, hightlight from <joined-subclass> to </joined-subclass> and copy it to your clipboard. Open the Person.hbm.xml and paste the <joined-subclass> information right before the </class> element at the bottom of the Person.hbm.xml.

<PASTE HERE>
</class>
</hibernate-mapping>

We'll also need to remove the <one-to-one> mapping to Person, that Middlegen added to the Employee element. If you don't delete the <one-to-one> mapping it'll cause Hibernate to throw an exception informing you that you haven't supplied a required element, the Person object. Next, find the <id> element of our newly pasted <joined-subclass> element and delete from <id> to </id>. Now paste the following code in it's place:


<key column="employee_id"/>

We've just told hibernate that we want Employee to be a subclass of Person and it should use employee_id as it's id column. We only need to make a few more modifications and we'll be all done with this section. Find the <one-to-one> association inside User.hbm.xml. Below outer-join="auto" add "cascade="delete". This informs hibernate that if we delete a Person object we also want to delete the Employee object associated to it (if there is one). Save the file and exit your editor. Finally, we need to delete the Employee.hbm.xml file, so we don't double generate the Employee class (once from the <joined-subclass> element inside Person.hbm.xml and once from Employee.hbm.xml). We're now done with our inheritance setup, so now lets move on to setting up proper many-to-many relationships.

Note: Once we've run hbm2java you may wish to remove the generated getBrokers() and setBrokers() accessor/mutator methods hbm2java generates. I personally don't like my Parent class having any knowledge of it's subclasses, but that's a personal choice.


Many to Many


Setting up Many to Many associations

Currently, the Employee and Department hbm.xml files Middlegen generated for us both have a one-to-many relationship to the EmployeeDepartment.hbm.xml file. As the relationships are defined now we would have to call code similar to this:


//retrieve the EmployeeDepartment objects
Set employeesDepartments = employee.getEmployeesDepartments();
//now we need to move through the set, retrieve the EmployeeDepartment object
//then each EmployeeDepartment for it's associated Department object.
//now we can work with the Department

While this is usable in an application, it's much cleaner to simply call employee.getDepartments(); and directly receive a Set of Department objects. So, lets get to it.

First, open Person.hbm.xml in your favorite editor and find the <set> element related to EmployeesDepartments (it's inside the Employee </joined-subclass>). Highlight from the comment above the <set> element all the way to the closing </set> element and delete it. Now copy the following code in it's place:


<!-- bi-directional many-to-many association to Department -->
<set name="departments" table="employees_departments" inverse="true" cascade="all">
<key column="employee_id"/>
<many-to-many column="department_id" class="com.warfrog.hibernatetutorial2.hibernate.Department"/>
</set>
Now open Department and repeat the procedure pasting this code instead:

<!-- bi-directional many-to-many association to Employee -->
<set name="employees" table="employees_departments" cascade="all">
<key column="department_id"/>
<many-to-many column="employee_id" class="com.warfrog.hibernatetutorial2.hibernate.Employee"/>
</set>
Note: If you look closely you'll notice that the Deparment code doesn't contain an inverse="true" element. Adding this element to both ends of the relationship results in some odd/undesirable behavior. For more information please consult this url ( http://www.hibernate.org/155.html ).

Save the file and exit the editor. We now have a correctly formed many-to-many relationship between Employee and Department utilizing the employee_department association table. Next, we'll generate our java files and use them in a simple application.
 


HBM2JAVA


Generating our Java files

Open a command prompt to you middlegen directory and execute "ant hbm2java". Once the ANT task has completed you should have 4 java files in your gen-src/com/warfrog/hibernatetutorial2/hibernate directory. You should now have a total of 7 files:

Department.hbm.xml
EmployeeDepartment.hbm.xml
Person.hbm.xml
Department.java
Employee.java
EmployeeDepartment.java
Person.java

If you take the time to examine the the java files hbm2java has generated you'll noticed that Employee contains a getDepartments() method, Department contains a getEmployees() method, and the Employee extends Person. At this point you can utilize the afforementioned files to properly insert, update, delete Person, Department, and Employee objects. Below I've provided some sample code on how to insert a new Employee with a new Department associated to it.


try{
	//notice we don't specify the Employee.class below
	Configuration cfg = new Configuration()
		.addClass(Department.class)
		.addClass(Person.class)
		.addClass(EmployeeDepartment.class);
	SessionFactory factory = cfg.buildSessionFactory();

	Session session = factory.openSession();
	Transaction tx = session.beginTransaction();

	//create an employee, take notice that we set both Person and Employee info
	Employee employee = new Employee();
	employee.setActive(new Boolean(true));
	employee.setAddress1("TEST ADDRESS 1");
	employee.setAddress2("TEST ADDRESS 2");
	employee.setDob(new Date());
	employee.setFirstName("TEST_FIRST_NAME");
	employee.setLastName("TEST_LAST_NAME");
	employee.setPhone("18005557500");
	employee.setSalary(new BigDecimal(10000000));
	employee.setSsn("000-00-0000");

	//create a Department
	Department department = new Department();
	department.setName("TEST_DEPARTMENT");
	//not the we've left out Description, which we can do since we marked it's not marked as "NOT NULL"

	//link the department to the employee
	employee.setDepartments(new HashSet());
	employee.getDepartments().add(department);

	//link the employee to the department
	department.setEmployees(new HashSet());
	department.getEmployees().add(employee);

	//now we need to save the objects to the database
	session.save(employee);
	//you could have called this instead since we defined cascade="all"
	//Session.save(department);
	tx.commit();

}catch(Exception e){
	e.printStackTrace();
}

executing the code above should result in output similar to this:

Hibernate: insert into PERSONS (FIRST_NAME, LAST_NAME, DOB, SSN, ADDRESS_1, ADDRESS_2, PHONE, ACTIVE, PERSON_ID) values (?, ?, ?, ?, ?, ?, ?, ?, null)
Hibernate: CALL IDENTITY()
Hibernate: insert into EMPLOYEES (SALARY, employee_id) values (?, ?)
Hibernate: insert into DEPARTMENTS (NAME, DESCRIPTION, DEPARTMENT_ID) values (?, ?, null)
Hibernate: CALL IDENTITY()
Hibernate: insert into employees_departments (department_id, employee_id) values (?, ?)
If you need further proof that everything worked, launch the HSQLDB GUI and execute the following statements:

SELECT * FROM PERSONS
SELECT * FROM EMPLOYEES
SELECT * FROM DEPARTMENTS
SELECT * FROM EMPLOYEES_DEPARTMENTS

and you'll see that all of the information is in the database and linked up correctly.



Note: A good excercise is to compare the original, unmodified files Middlegen produced to the files that you've modified. Compare both the hbm.xml and the resulting java files generated by hbm2java.
 


Conclusion


Conclusion

I hope this tutorial has given you a better understanding of how to accomplish some fairly common tasks with some great tools. If you're new to Hibernate and Middlegen then I'd suggest you read my first tutorial and some of the resources listed below. Doing so, should give you a richer and deeper understanding of how to utilize Hibernate in real world projects. As always, if you have any questions or comments don't hesitate to contact me either through IRC or email.

Cheers,
  Tyler Pitchford
  Senior Developer - Warfrog.com


Resources


Other tutorials

Warfrog Hibernate tutorial series part 1 - Hibernate/Spring/Middlegen/Xdoclet: http://www.warfrog.com/hibernatetutorial
Introduction to Hibernate: http://www.systemmobile.com/articles/IntroductionToHibernate.html
MiddleGen-Hibernate Example: http://www.systemmobile.com/articles/IntroductionToHibernate.html
Object-relation mapping without the container: http://www-106.ibm.com/developerworks/library/j-hibern/?ca=dnt-515
Object to Relational Mapping and Relationships with Hibernate: www.meagle.com:8080/hibernate.jsp

Links

Ant: http://ant.apache.org/
Ant Documentation: http://ant.apache.org/manual/index.html
Eclipse: http://www.eclipse.org/
Eclipse Documentation: http://www.eclipse.org/
Hibernate: http://www.hibernate.org
Hibernate Documentation: http://www.hibernate.org/hib_docs/reference/en/html/
Spring: http://www.springframework.org/
Spring Documentation: http://www.springframework.org/documentation.html
Middlegen: http://boss.bekk.no/boss/middlegen/
Middlegen Documenation: http://boss.bekk.no/boss/middlegen/project-info.html
XDoclet: http://xdoclet.sourceforge.net/
XDoclet Documenation: http://xdoclet.sourceforge.net/project-info.html
Hibernate - Xdoclet: http://www.hibernate.org/72.html
Hibernate - Spring: http://hibernate.bluemars.net/110.html
Hibernate - Inverse information: http://www.hibernate.org/155.html

IRC Help

#hibernate on irc.freenode.org
#spring on irc.freenode.org
#java on irc.freenode.org

Contact Info

IRC Handle: TdC_VgA
tyler_pitchford <remove>at<remove> yahoo.com

Downloads

Tutorial Files: hibernatetutorial2.zip


Change Log


Revisions

v1.0 - Inital Release  


Google