In this tutorial we will write a simple Java project to demonstrate Hibernate Many to Many Mapping using Java Annotations. 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 assoiated 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=HibernateManyToManyAnnotations -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> HibernateManyToManyAnnotations</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name> HibernateManyToManyAnnotations</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\ HibernateManyToManyAnnotations and execute the following command:
mvn eclipse:eclipse
3. Create Model Classes
As we should be using annotations, we need to only write 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; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; /** * Model class for Person */ @Entity @Table(name = "Person", uniqueConstraints = { @UniqueConstraint(columnNames = "ID"), @UniqueConstraint(columnNames = "NAME") }) public class Person implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID", unique = true, nullable = false) private Long id; @Column(name = "NAME") private String name; @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "PERSON_PHONE", joinColumns = { @JoinColumn(referencedColumnName = "ID") }, inverseJoinColumns = { @JoinColumn(referencedColumnName = "ID") }) 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; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; @Entity @Table(name = "PHONE", uniqueConstraints = { @UniqueConstraint(columnNames = "ID") }) public class Phone implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID", unique = true, nullable = false) private Long id; @Column(name = "CONTACTNUMBER", unique = true, nullable = false, length = 100) private String contactnumber; @Column(name = "PHONETYPE") private String phonetype; @ManyToMany(mappedBy = "personPhoneNumbers") 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:
- @JoinTable annotation has been used to make the making the association between the tables PERSON and PHONE through PERSON_PHONE table as follows:
@ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "PERSON_PHONE", joinColumns = { @JoinColumn(referencedColumnName = "ID") }, inverseJoinColumns = { @JoinColumn(referencedColumnName = "ID") }) private Set<Phone> personPhoneNumbers = new HashSet<Phone>();
- As mentioned in the section Many to Many Relationship 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.
- We then map the Phone entity to the Person entity using the mappedBy attribute in Phone.java:
@ManyToMany(mappedBy = "personPhoneNumbers") private Set<Person> persons = new HashSet<Person>();
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 class="net.javabeat.hibernate.Person"></mapping> <mapping class="net.javabeat.hibernate.Phone"></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 model classes, Person and Phone 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.AnnotationConfiguration; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { sessionFactory = new AnnotationConfiguration().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 Using Annotations "); 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 PERSON_PHONE table (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 Using Annotations log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version). 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 (persons_ID, personPhoneNumbers_ID) values (?, ?) Hibernate: insert into PERSON_PHONE (persons_ID, personPhoneNumbers_ID) values (?, ?) Hibernate: select person0_.ID as ID0_, person0_.NAME as NAME0_ from Person person0_ List size: 1 Hibernate: select personphon0_.persons_ID as persons1_1_, personphon0_.personPhoneNumbers_ID as personPh2_1_, phone1_.ID as ID1_0_, phone1_.CONTACTNUMBER as CONTACTN2_1_0_, phone1_.PHONETYPE as PHONETYPE1_0_ from PERSON_PHONE personphon0_ left outer join PHONE phone1_ on personphon0_.personPhoneNumbers_ID=phone1_.ID where personphon0_.persons_ID=? *************************** Manisha Phone Type: Mobile Phone Number: 988000045 *************************** *************************** Manisha Phone Type: Landline Phone Number: 4100000 ***************************
Summary
In this post we demonstrated many-to-many relation using Java annotation. We initially created a Java project using Maven, made it compatible with eclipse. Then we created model classes using annotations. 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. If you are interested in receiving the future articles on Java topics, please subscribe here