14.1 Overview
In earlier chapters we discussed persisting components using <composite-element tag where the component is persist as an value type whose life span is dependent on the parent object. We may have a collection of objects which are not value type instead are shared objects and in such case their life span cannot be dependent on the parent object. In such cases, the contained objects will have their own identifier and hence their own lifecycle.
In this chapter we will discuss following types of association mapping.
- Many to One
- One to Many
- Many to Many
- One to One
We will use below hibernate.cfg.xml in this entire chapter
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.url"> jdbc:mysql://localhost:3306/tutorial </property> <property name="hibernate.connection.username"> root </property> <property name="hibernate.connection.password"> password </property> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.format_sql">true</property> <property name="show_sql">true</property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <mapping resource="association-mapping.hbm.xml" /> </session-factory> </hibernate-configuration>
14.2 Many to One –
Think of the Cricket match and Stadium relationship where a multiple matches can be played at a single stadium. This is a Many to One association from Match to Stadium. In this scenario right now we are thinking from Match to stadium, which means we can navigate to stadium through match so this is a unidirectional association.
Hibernate does provide a <many-to-one> tag which can be used to map Many to One association.
From database design perspective, association is implemented by having a column in table representing “many” part of the relationship to hold the primary key of another table. This way this column is a foreign key.
In our example, Cricket_Match table will have an extra column to hold the stadium_ids
Let's design our Entity classes.
Stadium.java
package com.tutorial.hibernate; public class Stadium { private int id; private String name; private String country; private int capacity; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public int getCapacity() { return capacity; } public void setCapacity(int capacity) { this.capacity = capacity; } @Override public String toString() { return "Stadium [id=" + id + ", name=" + name + ", country=" + country + ", capacity=" + capacity + "]"; } }
CricketMatch.java
package com.tutorial.hibernate; public class CricketMatch { private int id; private String team1; private String team2; private String umpire; private String type; private Stadium stadium; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTeam1() { return team1; } public void setTeam1(String team1) { this.team1 = team1; } public String getTeam2() { return team2; } public void setTeam2(String team2) { this.team2 = team2; } public String getUmpire() { return umpire; } public void setUmpire(String umpire) { this.umpire = umpire; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Stadium getStadium() { return stadium; } public void setStadium(Stadium stadium) { this.stadium = stadium; } @Override public String toString() { return "CricketMatch [id=" + id + ", team1=" + team1 + ", team2=" + team2 + ", umpire=" + umpire + ", type=" + type + ", stadium=" + stadium + "]"; } }
association-mapping.xml - <many-to-one> tag is used in Cricket Match mapping and its column attribute specifies the column name to hold the stadium ids. At the application end, we need not define an additional property corresponding to this column, hibernate does manage it internally.
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.tutorial.hibernate.CricketMatch" table="Cricket_Match"> <id name="id" type="int" column="match_id"> <generator class="native" /> </id> <property name="team1" column="team1" type="string" /> <property name="team2" column="team2" type="string" /> <property name="type" column="type" type="string" /> <property name="umpire" column="umpire" type="string" /> <many-to-one name="stadium" class="com.tutorial.hibernate.Stadium" column="stadium_id"> </many-to-one> </class> <class name="com.tutorial.hibernate.Stadium" table="Stadium"> <id name="id" type="int" column="stadium_id"> <generator class="native" /> </id> <property name="name" type="string" column="stadium_name" /> <property name="capacity" type="int" column="capacity" /> <property name="country" type="string" column="country" /> </class> </hibernate-mapping>
Create Table Scripts
CREATE TABLE 'stadium' ( 'stadium_id' int(11) NOT NULL AUTO_INCREMENT, 'stadium_name' varchar(255) DEFAULT NULL, 'capacity' int(11) DEFAULT NULL, 'country' varchar(255) DEFAULT NULL, PRIMARY KEY ('stadium_id') ); CREATE TABLE 'cricket_match' ( 'match_id' int(11) NOT NULL AUTO_INCREMENT, 'team1' varchar(255) DEFAULT NULL, 'team2' varchar(255) DEFAULT NULL, 'type' varchar(255) DEFAULT NULL, 'umpire' varchar(255) DEFAULT NULL, 'stadium_id' int(11) DEFAULT NULL, PRIMARY KEY ('match_id'), KEY 'FK' ('stadium_id'), CONSTRAINT 'FK_70fxuvas8xraq7mo6rpmsekxx' FOREIGN KEY ('stadium_id') REFERENCES 'stadium' ('stadium_id') );
Test Program – Below is the test program to insert and retrieve the data. In below program I have highlighted two statements and they are for saving Stadium and Cricket Match. Don’t you think that save call for stadium is unnecessary? Ideally it should be managed by Cricket save call only. To avoid unnecessary calls, there are cascade operations which we will discuss in upcoming chapters.
import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import com.tutorial.hibernate.CricketMatch; import com.tutorial.hibernate.Stadium; public class Test { private static SessionFactory factory; public static void main(String args[]) { Configuration cfg = new Configuration().configure(); factory = cfg.buildSessionFactory(); Session session = factory.openSession(); Transaction tx = session.beginTransaction(); Stadium stadium = new Stadium(); stadium.setCapacity(2300); stadium.setCountry("Australia"); stadium.setName("Sydney"); CricketMatch match = new CricketMatch(); match.setTeam1("India"); match.setTeam2("Australia"); match.setType("Test Match"); match.setUmpire("Billy Bowden"); match.setStadium(stadium); session.save(stadium); int matchId = (Integer)session.save(match); tx.commit(); session.close(); getMatchDetails(matchId); factory.close(); } private static void getMatchDetails(int id) { Session session = factory.openSession(); CricketMatch match = (CricketMatch)session.get(CricketMatch.class,id); Stadium stadium = match.getStadium(); System.out.println(match); System.out.println(stadium); } }
Run Test Program – On running test program, console output and database states are as follows
Cricket_Match table
Stadium Table