A Guide to JPA with Hibernate (Relationship Mappings) — OneToOne , OneToMany , ManyToOne , ManyToMany


In this article, we’ll dive into Relationship Mapping with JPA and Hibernate in Java.

JPA is the persistence standard of the Java ecosystem. It allows us to map our domain model directly to the database structure and then gives us the flexibility of manipulating objects in our code — instead of messing with cumbersome JDBC components like , , etc.

We’ll be making a comprehensive guide to using JPA with Hibernate as its vendor. In this article, we’ll be covering relationship mappings. It will going to be a bit longer article but you will get overall clarity, so be focused.

What we will cover here:

Many-to-One, One-to-Many, Many-to-Many and One-to-One relationships. Additionally, we’ll also cover cascading operations, bidirectionality, optionality and eager/lazy loading fetch-types

Our Example

Before getting started, let’s remind us of the example we used in the previous part of this series. The idea was to map the model of a school with students taking courses given by teachers.

Here is what this model looks like:

As we can see, there are a few classes with certain properties. These classes have relationships between them. By the end of this article, we’ll have mapped all those classes to database tables, perserving their relationships.

Furthermore, we’ll be able to retrieve them and manipulate them as objects, without the hassle of JDBC.


First of all, let’s define a relationship. If we look at our class diagram we can see a few relationships:

Teachers and courses — students and courses — courses and course materials.

There are also connections between students and addresses, but they aren’t considered relationships. This is because an is not an entity (i.e. it is not mapped to a table of its own). So, as far as JPA's concerned, it's not a relationship.

There are a few types of relationships:

  • One-to-Many
  • Many-to-One
  • One-to-One
  • Many-to-Many

Let’s tackle these relationships one by one.


We’ll get started with the One-to-Many and Many-to-One relationships, which are closely related. You could go ahead and say that they’re the opposite sides of the same coin.

What’s a One-to-Many relationship?

As its name implies, it’s a relationship that links one entity to many other entities.

In our example, this would be a and their . A teacher can give multiple courses, but a course is given by only one teacher (that's the Many-to-One perspective - many courses to one teacher).

Another example could be on social media — a photo can have many comments, but each of those comments belongs to that one photo.

Before diving into the details of how to map this relationship, let’s create our entities:

Now, the fields of the class should include a list of courses. Since we'd like to map this relationship in a database, which can't include a list of entities within another entity - we'll annotate it with a annotation:

We’ve used a as the field type here, but we could've gone for a or a (though this one requires a bit more configuration).

How does JPA reflect this relationship in the database? Generally, for this type of relationship, we must use a foreign key in a table.

JPA does this for us, given our input on how it should handle the relationship. This is done via the annotation:

Using this annotation will tell JPA that the table must have a foreign key column that references the table's column.

Let’s add some data to those tables:

And now let’s check if the relationship works as expected:

We can see that the teacher’s courses are gathered automatically, when we retrieve the instance.

Owning Side and Bidirectionality

In the previous example, the class is called the owning side of the One-To-Many relationship. This is because it defines the join column between the two tables.

The is called the referencing side in that relationship.

We could’ve made the owning side of the relationship by mapping the field with in the class instead:

There’s no need to have a list of courses in the class now. The relationship would've worked the opposite way:

This time, we used the annotation, in the same way we used .

Note: It’s a good practice to put the owning side of a relationship in the class/table where the foreign key will be held.

So, in our case this second version of the code is better. But, what if we still want our class to offer access to its list?

We can do that by defining a bidirectional relationship:

We keep our mapping on the entity. However, we also map a list of s to the entity.

What’s important to note here is the use of the flag in the annotation on the referencing side.

Without it, we wouldn’t have a two-way relationship. We’d have two one-way relationships. Both entities would be mapping foreign keys for the other entity.

With it, we’re telling JPA that the field is already mapped by another entity. It’s mapped by the field of the entity.

Eager vs Lazy Loading

Another thing worth noting is eager and lazy loading. With all our relationships mapped, it’s wise to avoid impacting the software’s memory by putting too many entities in it if unnecessary.

Imagine that is a heavy object, and we load all objects from the database for some operation. We don't need to retrieve or use the courses for this operation, but they're still being loaded alongside the objects.

This can be devastating for the application’s performance. Technically, this can be solved by using the Data Transfer Object Design Pattern and retrieving information without the courses.

However, this can be a massive overkill if all we’re gaining from the pattern is excluding the courses.

Thankfully, JPA thought ahead and made One-to-Many relationships load lazily by default.

This means that the relationship won’t be loaded right away, but only when and if actually needed.

In our example, that would mean until we call on the method, the courses are not being fetched from the database.

By contrast, Many-to-One relationships are eager by default, meaning the relationship is loaded at the same time the entity is.

We can change these characteristics by setting the argument of both annotations:

That would inverse the way it worked initially. Courses would be loaded eagerly, as soon as we load a object. By contrast, the wouldn't be loaded when we fetch if it's unneeded at the time.


Now, let’s talk about optionality.

A relationship may be optional or mandatory.

Considering the One-to-Many side — it is always optional, and we can’t do anything about it. The Many-to-One side, on the other hand, offers us the option of making it mandatory.

By default, the relationship is optional, meaning we can save a without assigning it a teacher:

Now, let’s make this relationship mandatory. To do that, we’ll use the argument of the annotation and set it to (it's by default):

Thus, we can no longer save a course without assigning a teacher to it:

But if we give it a teacher, it works fine again:

Well, at least, it would seem so. If we had run the code, an exception would’ve been thrown:

Why is this? We’ve set a valid object in the object we're trying to persist. However, we haven't persisted the object before trying to persist the object.

Thus, the object isn't a managed entity. Let's fix that and try again:

Running this code will persist both entities and perserve the relationship between them.

Cascading Operations

However, we could’ve done another thing — we could’ve cascaded, and thus propagated the persistence of the object when we persist the object.

This makes more sense and works the way we’d expect it to like in the first example which threw an exception.

To do this, we’ll modify the flag of the annotation:

This way, Hibernate knows to persist the needed object in this relationship as well.

There are multiple types of cascading operations: , , , , , and (that combines all the previous ones).

We can also put the cascade argument on the One-to-Many side of the relationship, so that operations are cascaded from teachers to their courses as well.


Now that we’ve set up the foundations of relationship mapping in JPA through One-to-Many/Many-to-One relationships and their settings, we can move on to One-to-One relationships.

This time, instead of having a relationship between one entity on one side and a bunch of entities on the other, we’ll have a maximum of one entity on each side.

This is, for example, the relationship between a and its . Let's first map , which we haven't done yet:

The annotation for mapping a single entity to a single other entity is, unshockingly, .

Before setting it up in our model, let’s remember that a relationship has an owning side — preferably the side which will hold the foreign key in the database.

In our example, that would be as it makes sense that it references a (though we could go the other way around):

There is no point in having material without a course to encompass it. That’s why the relationship is not in that direction.

Speaking of direction, let’s make the relationship bidirectional, so we can access the material of a course if it has one. In the class, let's add:

Here, we’re telling Hibernate that the material within a is already mapped by the field of the entity.

Also, there’s no attribute here as it's by default, and we could imagine a course without material (from a very lazy teacher).

In addition to making the relationship bidirectional, we could also add cascading operations or make entities load eagerly or lazily.


Now, last but not least: Many-to-Many relationships. We kept these for the end because they require a bit more work than the previous ones.

Effectively, in a database, a Many-to-Many relationship involves a middle table referencing both other tables.

Luckily for us, JPA does most of the work, we just have to throw a few annotations out there, and it handles the rest for us.

So, for our example, the Many-to-Many relationship will be the one between and instances as a student can attend multiple courses, and a course can be followed by multiple students.

In order to map a Many-to-Many relationship we’ll use the annotation. However, this time around, we'll also be using a annotation to set up the table that represents the relationship:

Now, go over what’s going on here. The annotation takes a few parameters. First of all, we must give the table a name. We’ve chosen it to be .

After that, we’ll need to tell Hibernate which columns to join in order to populate . The first parameter, defines how to configure the join column (foreign key) of the owning side of the relationship in the table. In this case, the owning side is a .

On the other hand, the parameter does the same, but for the referencing side ().

Let’s set up a data set with students and courses:

Of course, this won’t work out of the box. We’ll have to add a method that allows us to add students to a course. Let’s modify the class a bit:

Now, we can complete our dataset:

Once this code has ran, it’ll persist our , and instances as well as their relationships. For example, let's retrieve a student from a persisted course and check if everything's fine:

Of course, we can still map the relationship as bidirectional the same way we did for the previous relationships.

We can also cascade operations as well as define if entities should load lazily or eagerly (Many-to-Many relationships are lazy by default).


That concludes this article about relationships of mapped entities with JPA. We’ve covered Many-to-One, One-to-Many, Many-to-Many and One-to-One relationships. Additionally, we’ve explored cascading operations, bidirectionality, optionality and eager/lazy loading fetch-types.

Software Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store