≡ Menu

Hibernate @OneToMany Bidirectional Relationship

Java-PodcastSince 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 Employees, 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:

  1. Instantiate/Load parent object
  2. Instantiate/Load child objects
  3. Set the parent object in the child objects
  4. Create a collection of child objects
  5. Set the collection of child objects on the parent
  6. 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!

{ 34 comments… add one }
  • Tardzenyuy July 28, 2014, 4:41 pm

    great

  • Daniel July 29, 2014, 8:09 am

    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

    • Trevor Page July 29, 2014, 10:43 am

      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);

    • Supriya June 16, 2017, 2:13 am

      Can you please explain why we need to use set instead of list in OneToMany mapping?
      Thanks in advance

      • Satish October 8, 2018, 12:24 pm

        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

  • Watchara Yarnu August 6, 2014, 11:36 am

    Thank you very much! Mr.Trevor. It’s a good lesson to learn and practice.

  • Carlos de Luna Saenz October 8, 2014, 5:54 pm

    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

    • Trevor Page October 13, 2014, 10:54 am

      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.

  • Poorva January 24, 2015, 6:20 am

    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 ?

    • Trevor Page February 7, 2015, 11:00 am

      You only need to add the “mapped-by” code to one side of the relationship (the parent side)

  • samir April 17, 2015, 4:05 am

    Hi,
    Can you give the same example for oracle DB.

  • nikhil July 13, 2015, 8:19 am

    Hyey can you please explain the above using XML needless to say the explanation is wonderful

    • Trevor Page July 14, 2015, 9:40 am

      I don’t support the XML approach as it’s being deprecated. I choose to teach the latest approaches only 🙂

  • Deyve Vieira August 25, 2015, 3:26 pm

    Thank you! Good lesson to learn and practice.

  • Ankur September 14, 2015, 5:19 am

    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 ??

    • Trevor Page September 15, 2015, 3:52 pm

      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)

  • Yi December 27, 2015, 8:45 pm

    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

  • RAJESH December 30, 2015, 11:54 pm

    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.

  • Ramya January 5, 2016, 3:19 am

    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

  • Federica March 16, 2016, 6:53 am

    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.

  • Meenatchi Sunar May 24, 2016, 6:28 am

    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

  • Kiran November 15, 2016, 5:40 am

    Nice and crisp – thanks

  • Ravi Soni November 29, 2016, 9:22 am

    Super

  • Germán December 3, 2016, 9:35 pm

    Great. This is ORM for dummies, worked for me. I have a hard time trying to understand how bidirectional relationship works. Thanks !

  • Heather Frank December 20, 2016, 12:15 am

    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!

  • LeonidasK January 2, 2017, 7:57 am

    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!

  • LeonidasK January 2, 2017, 8:00 am

    To correct myself, “Delete a child entry (Employee) without deleting the parent Employer entry”

  • chenzero January 27, 2017, 9:23 pm

    Great tutorial, thank you very much!

  • Anonymous February 1, 2017, 4:36 pm

    FYI, Your generics contain a compiler error. It should be not

  • Mehraj May 2, 2017, 2:59 am

    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

  • Sanjiv September 12, 2017, 4:57 pm

    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!

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.