In previous article we have seen, how to configure One to One mapping in JPA in both unidirectional and bidirectional way. In this article we will see how yo configure one to many mappings in JPA using annotations.

In JPA one to many mappings can be achieved in two ways, either using foreign key strategy or using a third table to have both tables primary key mappings. Lets look into these two strategies in details:
 

1) One to many mapping in JPA using foreign key strategy

Here we will try to associate two entities Employee and Address such that, an employee can have any number of addresses. For the purpose two entity classes can be annotated as follows:

Employee.java


package com.tbNext.hb.models;

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.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "TB_EMPLOYEE")
public class Employee {

	@Id
	@Column(name = "ID")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@Column(name = "FIRST_NAME")
	private String firstName;

	@Column(name = "LAST_NAME")
	private String lastName;

	@Column(name = "EMAIL")
	private String email;

	@Column(name = "PHONE")
	private String phone;

	@OneToMany(cascade=CascadeType.ALL)
	@JoinColumn(name = "EMPLOYEE_ID")
	Set<Address> addresses;

	public Employee() {
		super();
	}

	public Employee(String firstName, String lastName, String email,
			String phone) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
		this.phone = phone;
	}

	// getter and setters including addresses 

	public String toString() {
		return "[ID: " + id + " First Name: " + firstName + " Last Name: "
				+ lastName + " Email: " + email + " Phone: " + phone + "]";
	}
}

Address.java


package com.tbNext.hb.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "TB_ADDRESS")
public class Address {

	@Id
	@Column(name = "ID")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@Column(name = "CITY")
	private String city;

	@Column(name = "STATE")
	private String state;

	@Column(name = "COUNTRY")
	private String country;

	@ManyToOne
	private Employee employee;

	public Address() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Address(String city, String state, String country) {
		super();
		this.city = city;
		this.state = state;
		this.country = country;
	}

	// getters and setters including employee
	
	
}

Implementation

Now lets see things in action, we will have two addresses for an Employee and we will save them and than retrieve them.


package com.tbNext.java;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.EntityManager;

import com.tbNext.hb.Utils.PersistenceManager;
import com.tbNext.hb.models.Address;
import com.tbNext.hb.models.Employee;

/**
 * Hello world!
 *
 */
public class App {
	public static void main(String[] args) {
		// getting instance of EntityManagerFactory
		EntityManager em = PersistenceManager.INSTANCE.getEntityManager();

		// create an instance of Employee
		Employee employee = new Employee("Tech", "Burps", "info@tbNext.com",
				"8765234599");

		// create an instance of Info
		Address address1 = new Address("Delhi", "Delhi", "India");
		Address address2 = new Address("New Delhi", "Delhi", "India");

		Set<Address> employeeAddresses = new HashSet<Address>();
		employeeAddresses.add(address1);
		employeeAddresses.add(address2);

		employee.setAddresses(employeeAddresses);

		// Adding entities to db
		em.getTransaction().begin();
		em.persist(employee);
		em.getTransaction().commit();

		// try to fetch employee
		Employee emFromDb = em.find(Employee.class, employee.getId());
		System.out.println("Employee Id: " + emFromDb.getId()
				+ " No of Employee Addresses : "
				+ emFromDb.getAddresses().size());

	}
}
Output: We can see from the output, while saving employee its also inserting two addresses associated with it. This is because of using Cascade type all. Failing so we have to persist addresses individually first and than Employee object.

Hibernate: insert into TB_EMPLOYEE (EMAIL, FIRST_NAME, LAST_NAME, PHONE) values (?, ?, ?, ?)
Hibernate: insert into TB_ADDRESS (CITY, COUNTRY, employee_ID, STATE) values (?, ?, ?, ?)
Hibernate: insert into TB_ADDRESS (CITY, COUNTRY, employee_ID, STATE) values (?, ?, ?, ?)
Hibernate: update TB_ADDRESS set EMPLOYEE_ID=? where ID=?
Hibernate: update TB_ADDRESS set EMPLOYEE_ID=? where ID=?
Employee Id: 1 No of Employee Addresses : 2


We have also used, @JoinColumn annotation that will define the joining foreign key column name in Address entity table.

2) One to many mapping in JPA using third table

In this strategy we will configure one to many mapping between Employee and Address using a third table, that table will hold primary keys of these two to define association. We have annotate the two entity classes as follows:

Employee.java


package com.tbNext.hb.models;

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.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "TB_EMPLOYEE")
public class Employee {

	@Id
	@Column(name = "ID")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@Column(name = "FIRST_NAME")
	private String firstName;

	@Column(name = "LAST_NAME")
	private String lastName;

	@Column(name = "EMAIL")
	private String email;

	@Column(name = "PHONE")
	private String phone;

	@OneToMany(cascade = CascadeType.ALL)
	@JoinTable(name = "EMPLOYEE_ADDRESSES", joinColumns = { @JoinColumn(name = "EMPLOYEE_ID", referencedColumnName = "ID") }, inverseJoinColumns = { @JoinColumn(name = "ADDRESS_ID", referencedColumnName = "ID") })
	Set<Address> addresses;

	public Employee() {
		super();
	}

	public Employee(String firstName, String lastName, String email,
			String phone) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
		this.phone = phone;
	}

	// getters and setters including addresses

	public String toString() {
		return "[ID: " + id + " First Name: " + firstName + " Last Name: "
				+ lastName + " Email: " + email + " Phone: " + phone + "]";
	}
}

Address.java


package com.tbNext.hb.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "TB_ADDRESS")
public class Address {

	@Id
	@Column(name = "ID")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@Column(name = "CITY")
	private String city;

	@Column(name = "STATE")
	private String state;

	@Column(name = "COUNTRY")
	private String country;

	public Address() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Address(String city, String state, String country) {
		super();
		this.city = city;
		this.state = state;
		this.country = country;
	}

	// getter and setters
	
}
Here we have defined all the mappings related stuff in Employee entity only and no need to do anything in Employee, infact Address entity do not know anything about this relationship. Employee entity is refining a new table name “EMPLOYEE_ADDRESSES” using @JoinTable annotation, joinColumns attribute is refining two columns in EMPLOYEE_ADDRESSES table, along with their name and respective primary key columns name in Employee and addresses.

Implementation

Now lets see things in action, we will have two addresses for an Employee and we will save them and than retrieve them using a thir table.

package com.tbNext.java;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.EntityManager;

import com.tbNext.hb.Utils.PersistenceManager;
import com.tbNext.hb.models.Address;
import com.tbNext.hb.models.Employee;

/**
 * Hello world!
 *
 */
public class App {
	public static void main(String[] args) {
		// getting instance of EntityManagerFactory
		EntityManager em = PersistenceManager.INSTANCE.getEntityManager();

		// create an instance of Employee
		Employee employee = new Employee("Tech", "Burps", "info@tbNext.com",
				"8765234599");

		// create an instance of Info
		Address address1 = new Address("Delhi", "Delhi", "India");
		Address address2 = new Address("New Delhi", "Delhi", "India");

		Set<Address> employeeAddresses = new HashSet<Address>();
		employeeAddresses.add(address1);
		employeeAddresses.add(address2);

		employee.setAddresses(employeeAddresses);

		// Adding entities to db
		em.getTransaction().begin();
		em.persist(employee);
		em.getTransaction().commit();

		// try to fetch employee
		Employee emFromDb = em.find(Employee.class, employee.getId());
		System.out.println("Employee Id: " + emFromDb.getId()
				+ " No of Employee Addresses : "
				+ emFromDb.getAddresses().size());

		// try to fetch address
		Address addressFromDb = em.find(Address.class, address1.getId());
		System.out.println("Address id: " + addressFromDb.getId());

	}
}
Output: We can see from the output, there is one more helping table created as passed in @JoinTable annotation, this table will have association mapping details:

Hibernate: create table EMPLOYEE_ADDRESSES (EMPLOYEE_ID bigint not null, ADDRESS_ID bigint not null, primary key (EMPLOYEE_ID, ADDRESS_ID)) ENGINE=InnoDB
Hibernate: create table TB_ADDRESS (ID bigint not null auto_increment, CITY varchar(255), COUNTRY varchar(255), STATE varchar(255), primary key (ID)) ENGINE=InnoDB
Hibernate: create table TB_EMPLOYEE (ID bigint not null auto_increment, EMAIL varchar(255), FIRST_NAME varchar(255), LAST_NAME varchar(255), PHONE varchar(255), primary key (ID)) ENGINE=InnoDB
Hibernate: alter table EMPLOYEE_ADDRESSES add constraint UK_k9s6p1jw3w9sfqua92in6cuwj unique (ADDRESS_ID)
Hibernate: alter table EMPLOYEE_ADDRESSES add constraint FK8mxdv4m31ndunjjfpy3nes496 foreign key (ADDRESS_ID) references TB_ADDRESS (ID)
Hibernate: alter table EMPLOYEE_ADDRESSES add constraint FK4c14pftyfvfpe73yu9vndfyrr foreign key (EMPLOYEE_ID) references TB_EMPLOYEE (ID)


Hibernate: insert into TB_EMPLOYEE (EMAIL, FIRST_NAME, LAST_NAME, PHONE) values (?, ?, ?, ?)
Hibernate: insert into TB_ADDRESS (CITY, COUNTRY, STATE) values (?, ?, ?)
Hibernate: insert into TB_ADDRESS (CITY, COUNTRY, STATE) values (?, ?, ?)
Hibernate: insert into EMPLOYEE_ADDRESSES (EMPLOYEE_ID, ADDRESS_ID) values (?, ?)
Hibernate: insert into EMPLOYEE_ADDRESSES (EMPLOYEE_ID, ADDRESS_ID) values (?, ?)
Employee Id: 1 No of Employee Addresses : 2
Address id: 2


That's it for this discussion, we are done with configuring one to many mappings using annotations both with foreign key strategy and using a third mapping table. In upcoming article we will many to many mappings and other stuff related to Hibernate.

In this article we have seen, one to many mapping in JPA, in upcoming articles we will see One to Many mapping in JPA and more about JPA and other opensource technologies.
  • By Techburps.com
  • Oct 1, 2015
  • JPA