Since we've already learned about the unidirectional @ManyToOne relationship, we can now move on to talking about what a bidirectional relationship is like, when using Hibernate.
The term “bidirectional” literally means “functioning in two directions”, which is the concept that we will apply in our relationships between two Java objects. When we have a bidirectional relationship between objects, it means that we are able to access Object A from Object B, and Object B from Object A.
We can apply this logic to our real world coding example that we saw in the last post. The example we will use is the relationship between an Employer
and an Employee
. Previously, we only defined a unidirectional relationship, so we could only access the Employer
from the Employee
object and not vice-versa.
Now let's take a look at how to transform our existing unidirectional relationship into a bidirectional one.
Bidirectional @OneToMany Relationship – Employer/Employee
The first step in transforming our unidirectional relationship into a bidirectional relationship is to add the missing reference from the Employer
to the Employee
.
One critical thing to remember here is that there's a difference between a reference from the One-to-Many side and the Many-to-One side.
When you traverse from the “Many” side to the “One” side, you only need to make reference to one object, which is why the Employee
class holds a single reference to an Employer
class via the private Employer employer
instance variable.
However, when you traverse from the “One” to the “Many” side, you will need to hold a reference to MANY objects.
Does that make sense? Many-to-One equals one reference, One-to-Many equals many references.
So what will this look like? Well, it means that the Employer
will need to hold many references to its Employee
s, and we accomplish this by storing them as a Collection
.
In this example I will use a Set
of Employee
objects in my Employer
class file like so:
Employer.java
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Employer { private Long id; private String employerName; private Set employees; @Id @GeneratedValue(strategy=GenerationType.AUTO) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getEmployerName() { return employerName; } public void setEmployerName(String employerName) { this.employerName = employerName; } @OneToMany(cascade=CascadeType.ALL, mappedBy="employer") public Set getEmployees() { return employees; } public void setEmployees(Set employees) { this.employees = employees; } }
What you may have also noticed was that we added the @OneToMany
annotation to the getEmployees()
method. This is needed so that Hibernate understands that this is the second part of the relationship/link between the Employer
and Employee
.
So just like we have the @ManyToOne
annotation in the Employee
class, we also have the @OneToMany
annotation in the Employer
class… you see? Bidirectional!
Now, I haven't yet explained what that pesky mappedBy="employer"
means…
Using the mappedBy
Property on Bidirectional One-to-Many Relationship
The funny thing is that you don't actually need to use the mappedBy
property, but your database will be quite a mess if you don't.
So I like to say that it's mandatory to use the mappedBy
property when creating a bidirectional relationship in Hibernate. And yes, I did mean to refer to ALL bidirectional relationships with that previous statement.
The mappedBy
property is what we use to tell Hibernate which variable we are using to represent the parent class in our child class.
If you were to look at the class definition for the child class (Employee
) you will see that we have declared an instance variable called employer
, which is what we used to establish our relationship. All we are doing here is we are telling the Employer
class which variable the Employee
class is using to create the relationship.
So! Since we declared the Employer
variable with the name employer
, this is what we need to put in the mappedBy
property of the @OneToMany
annotation.
For those of you who haven't already seen it, here's what the Employee
class looks like (note that we haven't made any changes to this class when we had initially created our unidirectional relationship in the last post):
import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToOne; @Entity public class Employee { private Long id; private String employeeName; private Employer employer; @Id @GeneratedValue(strategy=GenerationType.AUTO) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } @ManyToOne(cascade=CascadeType.ALL) public Employer getEmployer() { return employer; } public void setEmployer(Employer employer) { this.employer = employer; } }
Putting our Bidirectional Relationship to Use
Okay, so now that we've created the blueprint for our bidirectional relationship, now we need to put in the supporting code to actually test this stuff out!
In the previous post we had created an EmployeeDao
object which we used to save our employee1
and employee2
objects into the database in a unidirectional manner… but… now that we've changed our code around to use a bidirectional relationship, we need to change the way we persist our data.
As I mentioned, we previously used the EmployeeDao
to persist our data, but now that we've switched to bidirectional we will need to use an EmployerDao
to carry out our wishes! The EmployerDao
will be identical to the EmployeeDao
except that it will persist an Employer
object. Here's what it looks like:
import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import com.howtoprogramwithjava.example.persistence.Employer; @Transactional @Repository public class EmployerDao { @Autowired SessionFactory sessionFactory; public void save (Employer employer) { Session session = sessionFactory.getCurrentSession(); session.save(employer); } }
Now the only missing piece is to show you how to actually persist data using this new bidirectional One-to-Many mapping.
I've set up the test code in a Controller class, I'll include all of the code for the sake of completion, but all you really need to be concerned with is how the code inside the test
method works.
import java.util.HashSet; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import com.howtoprogramwithjava.example.dao.EmployerDao; import com.howtoprogramwithjava.example.persistence.Employee; import com.howtoprogramwithjava.example.persistence.Employer; @Controller public class HomeController { @Autowired EmployerDao employerDao; @RequestMapping("test") public String test (ModelMap model) { Employer employer = new Employer(); employer.setEmployerName("Employer 1"); Set employees = new HashSet(); Employee employee1 = new Employee(); employee1.setEmployeeName("Trevor Page"); employee1.setEmployer(employer); employees.add(employee1); Employee employee2 = new Employee(); employee2.setEmployeeName("John Doe"); employee2.setEmployer(employer); employees.add(employee2); employer.setEmployees(employees); employerDao.save(employer); return "testPage"; } }
Inside of the test
method is where all the magic happens.
What you need to know is that since we have a bidirectional relationship, this means we need to assign objects to EACH side of the relationship. This means that we need to populate both the employee.setEmployer()
as well as the employer.setEmployees()
. It's absolutely critical that this be done correctly.
To persist all the objects correctly you'll need to follow these generic steps:
- Instantiate/Load parent object
- Instantiate/Load child objects
- Set the parent object in the child objects
- Create a collection of child objects
- Set the collection of child objects on the parent
- Save the parent
Note: To observe best coding practices, the code I've put into this Controller class is usually best placed in a Service class, but to keep things as simple as possible for this tutorial I've just placed all the code directly into the Controller.
Alright! We're done! You now know how to create a One-to-Many bidirectional relationship with Hibernate. Congrats!
As always, if you want to be on the cutting edge of these Java tutorials and receive updates on when I'm putting on free webinars and doing give-aways please join my mailing list by putting in your email address in the popup below. When you do you'll instantly receive one free gift from me (and plenty more in the future). I look forward to seeing you again soon!