In this tutorial we will write a simple Java project to demonstrate Hibernate Many to Many Mapping using XML mapping. We will first create a Java project using Maven and then will add Hibernate on it. If you have any questions, please post it in the comments section. If you are interested in receiving the future articles on Java topics, please subscribe here. If you are beginner for hibernate, please read the articles about introduction to hibernate, interceptors in hibernate and spring & hibernate integration. We also recommend good reference books for learning hibernate in our post about hibernate books.
Many To Many Relationship
A ManyToMany relationship in Java is where the source object has an attribute that stores a collection of target objects and (if) those target objects had the inverse relationship back to the source object it would also be a ManyToMany relationship.
All ManyToMany relationships require a JoinTable. The JoinTable is defined using the @JoinTable annotation and XML element. The JoinTable defines a foreign key to the source object’s primary key (joinColumns), and a foreign key to the target object’s primary key (inverseJoinColumns). Normally the primary key of the JoinTable is the combination of both foreign keys. Let’s see an example of person and phone relationship as in the below structure:
Each person can have more than one phone and each phone can have more than one person associated with it.
Technologies Used:
Following are the tools and technologies required for this project:
- Java JDK 5 or above
- Eclipse IDE 3.2 or above
- Maven 3.0 or above
- Hibernate 3.0 or above (Hibernate Downloads)
- MySQL 5 above (MySql Downloads)
1.Environment Setup
Refer post Hibernate, Maven and HSQL – Example Project (XML Mapping) for environment setup (follow steps 1,2 and 3). Pass the following command in the step.1, to create a java project using Maven:
mvn archetype:generate -DgroupId=net.javabeat.hibernate -DartifactId=HibernateManyToManyXML -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
2. Adding dependencies to pom.xml
We need to add the Hibernate, MYSQL and some other dependencies to the Maven pom.xml as shown below:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.javabeat.hibernate</groupId> <artifactId>HibernateManyToManyXML</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>HibernateManyToManyXML</name> <url>http://maven.apache.org</url> <dependencies> <!-- Hibernate library dependency start --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> <version>4.1.9.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.10</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> <version>3.3.1.GA</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm</artifactId> <version>4.0</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>ehcache</groupId> <artifactId>ehcache</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>hibernate-tools</groupId> <artifactId>hibernate-tools</artifactId> <version>3.2.3.GA</version> </dependency> <dependency> <groupId>jta</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.11</version> </dependency> <dependency> <groupId>oscache</groupId> <artifactId>oscache</artifactId> <version>2.1</version> </dependency> <dependency> <groupId>persistence-api</groupId> <artifactId>persistence-api</artifactId> <version>1.0</version> </dependency> <!-- Hibernate library dependecy end --> </dependencies> </project>
As a next step, let’s execute the following command so that maven will download all the required JARs and add the same to eclipse classpath. The command line point to the directory C:\HibernateProject\HibernateManyToManyXML and execute the following command:
mvn eclipse:eclipse
3. Create Model Classes and hibernate mapping files
Next let’s write Hibernate XML Mapping files to the directory: /src/main/resources . Create the resources directory as follows: Right click on Project > New > Source Folder > Give folder name “/src/main/resources/” and click Finish.
Write the file src\main\resources\net\javabeat\hibernate\Person.hbm.xml for the model Person and src\main\resources\net\javabeat\hibernate\Phone.hbm.xml for the model class Phone. The contents of these files are as below:
Person.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="net.javabeat.hibernate.Person" table="person" > <id name="id" type="java.lang.Long"> <column name="ID" unique="true"/> <generator class="native" /> </id> <property name="name" type="string"> <column name="NAME" length="10" not-null="true" unique="true" /> </property> <set name="personPhoneNumbers" table="person_phone" cascade="all"> <key column="PERSONID" not-null="true" /> <many-to-many column="PHONEID" class="net.javabeat.hibernate.Phone" /> </set> </class> </hibernate-mapping>
Phone.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="net.javabeat.hibernate.Phone" table="phone"> <id name="id" type="java.lang.Long"> <column name="ID" unique="true"/> <generator class="native" /> </id> <property name="contactnumber" type="string"> <column name="CONTACTNUMBER" /> </property> <property name="phonetype" type="string"> <column name="PHONETYPE" /> </property> <set name="persons" table="person_phone"> <key column="PHONEID" /> <many-to-many column="PERSONID" class="net.javabeat.hibernate.Person" /> </set> </class> </hibernate-mapping>
Model classes
As a next step let’s create the model classes src\main\java\net\javabeat\hibernate\Person.java and src\main\java\net\javabeat\hibernate\Phone.java. The contents of the model classes are as below
Person.java
package net.javabeat.hibernate; import java.io.Serializable; import java.util.HashSet; import java.util.Set; /** * Model class for Person */ public class Person implements Serializable { private Long id; private String name; private Set<Phone> personPhoneNumbers = new HashSet<Phone>(); public Person(String name, Set<Phone> personPhoneNumbers) { super(); this.name = name; this.personPhoneNumbers = personPhoneNumbers; } public Person() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Phone> getPersonPhoneNumbers() { return personPhoneNumbers; } public void setPersonPhoneNumbers(Set<Phone> personPhoneNumbers) { this.personPhoneNumbers = personPhoneNumbers; } public void addPhone(Phone phone) { this.personPhoneNumbers.add(phone); } }
Phone.java
package net.javabeat.hibernate; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Phone implements Serializable { private Long id; private String contactnumber; private String phonetype; private Set<Person> persons = new HashSet<Person>(); public Phone() { } public Phone(String contactnumber, String phonetype) { super(); this.contactnumber = contactnumber; this.phonetype = phonetype; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getContactnumber() { return contactnumber; } public void setContactnumber(String contactnumber) { this.contactnumber = contactnumber; } public String getPhonetype() { return phonetype; } public void setPhonetype(String phonetype) { this.phonetype = phonetype; } public Set<Person> getPersons() { return persons; } public void setPersons(Set<Person> persons) { this.persons = persons; } }
Details of the above files are as below:
- The many-to-many relationship definition in Person.hbm.xml, the set attribute is defined as follows:
<set name="personPhoneNumbers" table="person_phone" cascade="all"> <key column="PERSONID" not-null="true" /> <many-to-many column="PHONEID" class="net.javabeat.hibernate.Phone" /> </set>
The cascade=”all” ensures that the INSERT order is appropriate. i.e. the insertion order is in the following fashion PERSON, PHONE and PERSON_PHONE.
- The following code in the Phone.hbm.xml, makes the many-to-many association bi-directional.
<set name="persons" table="person_phone"> <key column="PHONEID" /> <many-to-many column="PERSONID" class="net.javabeat.hibernate.Person" /> </set>
Corresponding code in the Phone.java class is as follows:
private Set<Person> persons = new HashSet<Person>(); public Set<Person> getPersons() { return persons; } public void setPersons(Set<Person> persons) { this.persons = persons; }
4. Adding Hibernate Configuration file
As a next step let’s add the hibernate.cfg.xml to the directory:/src/main/resources . Write the new file hibernate.cfg.xml in this directory. The hibernate.cfg.xml is as follows:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Database connection settings, Connect to MYSQL --> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/manisha</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">1</property> <property name="show_sql">true</property><property name="format_sql">true</property> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!--create the database schema on startup if required --> <property name="hbm2ddl.auto">create</property> <mapping resource="net/javabeat/hibernate/Person.hbm.xml"></mapping> <mapping resource="net/javabeat/hibernate/Phone.hbm.xml"></mapping> </session-factory> </hibernate-configuration>
In the above file we have set the database connection to MYSQL database . The show_sql option, if set to true will display all the executed SQL queries on the console. The property hbm2ddl.auto , if set to create, creates the schema, destroying the previous data. At the end of the file we add the Person.hbm.xml and Phone.hbm.xml files to the configuration.
Note : In case you want to use any other database then, you need to change these properties – “dialect”, “connection.driver_class”, “connection.url”, “connection.username”, and “connection.password”.
5. Create Utility class
Next, we will write a utility class to take care of Hibernate start up and retrieve the session easily. We will write the file src\main\java\net\javabeat\hibernate\HibernateUtil.java as below:
package net.javabeat.hibernate; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure() .buildSessionFactory(); } catch (Throwable ex) { System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
6. Revise the App class
Next we will revise the App.java (generated by Maven). This class tests the many-to-many relationship by creating and listing the person names and corresponding phone numbers as below:
package net.javabeat.hibernate; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; /** * Main class * */ public class App { public static void main(String[] args) { System.out.println("Maven + Hibernate + SQL Many to Many XML Mapping "); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); App app = new App(); app.savePersonInfo("Manisha"); app.listPersonInfo(); } public Long savePersonInfo(String personName) { Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); Long personId = null; Transaction transaction = null; try { transaction = session.beginTransaction(); Person person = new Person(); person.setName(personName); Phone ph1 = new Phone(); Phone ph2 = new Phone(); ph1.setContactnumber("4100000"); ph1.setPhonetype("Landline"); ph2.setContactnumber("988000045"); ph2.setPhonetype("Mobile"); person.addPhone(ph1); person.addPhone(ph2); session.save(person); transaction.commit(); } catch (HibernateException e) { transaction.rollback(); e.printStackTrace(); } finally { session.close(); } return personId; } /* * Lists the person's from database table */ public void listPersonInfo() { Session session = HibernateUtil.getSessionFactory().openSession(); Transaction transaction = null; try { transaction = session.beginTransaction(); @SuppressWarnings("unchecked") List<Person> personList = session.createQuery("FROM Person").list(); System.out.println("List size: " + (personList).size()); for (Iterator iterator = personList.iterator(); iterator.hasNext();) { Person person = (Person) iterator.next(); Set<Phone> ph = new HashSet<Phone>(); ph = person.getPersonPhoneNumbers(); for (Phone p : ph) { System.out.println("***************************"); System.out.println(person.getName()); System.out.println(" Phone Type: " + p.getPhonetype()); System.out.println(" Phone Number: " + p.getContactnumber()); System.out.println("***************************"); } } transaction.commit(); } catch (HibernateException e) { transaction.rollback(); e.printStackTrace(); } finally { session.close(); } } }
Here the savePersonInfo() method is used to save a new Person object and Phone object to the database.
The listPersonInfo() method is used to list name of each person in PERSON table and their corresponding phone numbers and type from PHONE table. Here we use Hibernate Query Language (HQL). The query “from Person” returns a list of all the data in the PERSON table and their corresponding phone numbers from PHONE table through the table PERSON_PHONE(check the select query in the output section). Note that in the HQL we only specify the java class names and not the table names. Later, using the for loop we iterate the list the data from Person and corresponding Phone table and hence display them on the console.
8. Final project structure
Once you have created all these source files, your project structure should look like following:
9. Execution of the above code
Now let us execute the code we created above. Let’s run the App class. Right click on App.java >Run As > Java Application. On start of each thread, a database schema will be created and the following actions will happen.
Output on the console:
Following output is displayed on the console:
Maven + Hibernate + SQL Many to Many XML Mapping log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: insert into person (NAME) values (?) Hibernate: insert into phone (CONTACTNUMBER, PHONETYPE) values (?, ?) Hibernate: insert into phone (CONTACTNUMBER, PHONETYPE) values (?, ?) Hibernate: insert into person_phone (PERSONID, PHONEID) values (?, ?) Hibernate: insert into person_phone (PERSONID, PHONEID) values (?, ?) Hibernate: select person0_.ID as ID0_, person0_.NAME as NAME0_ from person person0_ List size: 1 Hibernate: select personphon0_.PERSONID as PERSONID1_, personphon0_.PHONEID as PHONEID1_, phone1_.ID as ID2_0_, phone1_.CONTACTNUMBER as CONTACTN2_2_0_, phone1_.PHONETYPE as PHONETYPE2_0_ from person_phone personphon0_ left outer join phone phone1_ on personphon0_.PHONEID=phone1_.ID where personphon0_.PERSONID=? *************************** Manisha Phone Type: Landline Phone Number: 4100000 *************************** *************************** Manisha Phone Type: Mobile Phone Number: 988000045 ***************************
Summary
In this post we demonstrated Many-to-Many relation using hibernate mapping files. We initially created a Java project using Maven, made it compatible with eclipse. Then we created hibernate mapping files and created the corresponding model classes (using join table association). We then set the values in our main class(App.java), which in turn added the values to respective tables. We also listed the data from the tables. In the next post we shall see an example of Hibernate Many-to-Many mapping(using Annotations).If you have any questions, please post it in the comments section. If you are interested in receiving the future articles on Java topics, please subscribe here