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 Setemployees; @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"); Setemployees = 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!
great
Hi Trevor,
I’ve been listening to your podcast for quite a while now and also got your book last year because I always wanted to learn programming. Also I’m an research engineer, but I work in an IT deparment.
I’ve been working on a small project for the last weeks and ran into problems with bidirectional relations using @OneToMany and @ManyToMany.
I’ve used Lists instead of Sets which created very strange duplicates or better said x-plicates when pulling information from the DB, while the data in the mySQL DB looked just fine.
I knew it had something to do with the bidirectional relation and I wrote a ton of different helper methods to fumble around with lists, iterators and whatnot.
For some reason I didn’t think about using Sets instead of Lists, so when I saw your latest Podcast about bidirectional relations I thought: “Nice, maybe Trevor will point out some of the problems with bidirectional relations.”
I was actually expecting to hear that it’s all messed up in general, because my colleagues told me that I just shouldn’t use bidirectional relations, which struck me as kinda strange, because I simple wanted them in my app.
And it was just that… I simply changed the Lists in my entities to Sets and the problems are gone (after a few minor adjustments).
So special thanks for this episode which helped me to overcome a problem I’ve been kinda stuck with for days.
BR
Daniel
Amazing Daniel! I’m so glad I could help you out in that manner, I love when that happens.
I’ll be talking about a tweak you can do in a later episode if you NEED to use a List. It’s called
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
Can you please explain why we need to use set instead of list in OneToMany mapping?
Thanks in advance
Because in one to many one one object refrence associated with many references so whenever we get data list shows some ambiguity data so to avoid this we use set and set don’t allow duplicate so to retrieve data is easy
Thank you very much! Mr.Trevor. It’s a good lesson to learn and practice.
I have the following error when updating a parent…
Caused by: org.hibernate.HibernateException: identifier of an instance of com.avon.mx.reaco.persistence.domain.reaco.TransportType was altered from 1 to 2
at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:82)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:194)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:156)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:228)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:100)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
The “relevant code” should be:
@Entity
@Table(name = "R3_Transport_Type")
@NamedQueries({
@NamedQuery(name = TransportType.QRY_NAME_FIND_ALL , query = TransportType.QRY_FIND_ALL ),
...})
public class TransportType implements Serializable {
.
.
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
....
@OneToMany(mappedBy = "idTransportType", cascade=CascadeType.ALL)
private Collection shippingZoneCostCollection;
...
}
@Entity
@Table(name = "R3_Shipping_Zone_Cost")
@NamedQueries({
@NamedQuery(name = ShippingZoneCost.QRY_NAME_FIND_ALL, query = ShippingZoneCost.QRY_FIND_ALL ),
...
),
})
public class ShippingZoneCost implements Serializable {
....
@JoinColumn(name = "id_transport_type", referencedColumnName = "id")
@ManyToOne
private TransportType idTransportType;
}
¿Any idea?
thanks in advance
It sounds like the “ID” instance variable of your
TransportType
object is being changed.You should never EVER change the ID value of an object once it’s set and Hibernate is tracking it.
Here we can see u have added the “mapped-by” in the Employer class.. so the question is can we add the “mapped-by” in Employee class ?
You only need to add the “mapped-by” code to one side of the relationship (the parent side)
Hi,
Can you give the same example for oracle DB.
Hyey can you please explain the above using XML needless to say the explanation is wonderful
I don’t support the XML approach as it’s being deprecated. I choose to teach the latest approaches only 🙂
Thank you! Good lesson to learn and practice.
Hi Trevor ,
Very nicely explained tutorial . Really helpful ,
I had one confusion though , In the unidirectional tutorial (https://howtoprogramwithjava.com/hibernate-manytoone-unidirectional-tutorial/) you used EmployeeDao to save the details to the database . This also saves the details f0r the employer looking at ManyToOne annotation .
Here , for saving the employer details , you used EmployerDao to persist the Employer Details to the database . So I assume , it will also save the employee details in the database . Is my understanding correct ?? or for saving the employee details , one still needs to use the EmployeeDao ??
It was a while ago that I wrote these tutorials, but if my memory serves me well, I believe I just created a Dao based on the “parent” side of the relationship.
So, to answer you question, I believe the answer is YES, it will save the employee details as well (as that’s what relationships are all about… it should save into both related tables)
Hi Trevor,
Thank you so much for this post. Lots of help.
I’m currently working on a Spring + Hibernate project. I can hardly find bidirectional relationship with get method? Specifically, how to get the parent object if LAZY loading is used. It really bothered me a lot. Every time I want to return the parent object, ‘no session’ exception is thrown, so I have to create a new object and retrieve some values from the parent object.
BR,
Yi
Very nice tutorials.
suggestions:
if you can provide a download option for each , it will be helpful to learners.Learners can go through the programming and at the same time going through your explanation.
I went through the article and i have a question.
I am struck with a scenario where i am trying to delete the parent and in the child the parent reference should be marked as null , but instead of that i see the exception ” no row with the given identifier exists” and infact there is a stale reference in DB in child table.
Am i missing something here ? Please help
Dear Trevor, sincerely thanks, thanks, thanks. You solved my problem.
Very clear and to the point. I like this kind of explanations. Best wishes for your career.
PS I have read also the introduction to your website. Lovely! “when a 10 year old boy is bored in a car ride, he’ll likely reach out to annoy whomever is closest to him in an attempt to pass the time” ah ah ah, also girls, or at least I was doing the same.
i think cascade option should not be there in @Manytoone
@ManyToOne(cascade=CascadeType.ALL)
public Employer getEmployer()
{
return employer;
}
bcoz if we delete employee,it will delete employer also
plz comment on this
Nice and crisp – thanks
Super
Great. This is ORM for dummies, worked for me. I have a hard time trying to understand how bidirectional relationship works. Thanks !
I’m pretty sure you just solved my problem by explaining how mappedBy works. Every other tutorial just kinda skipped that part, so I didn’t realize that this should be the variable name and not the table name! My variable is User owner, but the table of users is called “user” and I’ve been getting so many errors I didn’t understand. Thank you for being so clear!
Hey, great tutorial. The only aspect that I think that is missing is the delete operation. Can you please add the following? Delete a child entry (Employer) without deleting the Employ entry (since the cascadeType is ALL we will have a problem there I guess). Otherwise the tutorial is perfect and thanks for this!
To correct myself, “Delete a child entry (Employee) without deleting the parent Employer entry”
Great tutorial, thank you very much!
FYI, Your generics contain a compiler error. It should be not
There is a typo error :
at line 11 of Employer class – This should be : private Set employees;
at line 33 of Employer class – This should be : public Set getEmployees
I’ve read through many blog posts and tutorials explaining hibernate relationships especially the uni and bi directional but this is by far the BEST!