Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Entity Relations and Cascade Types | Fundamentals of Hibernate
course content

Зміст курсу

Java Data Manipulation with Hibernate

Entity Relations and Cascade TypesEntity Relations and Cascade Types

Entity Relationships

As you may know from the Relational Databases and Normalization course, in SQL, there are certain relationships between tables. Similarly, in Hibernate, one entity can reference another and vice versa. These are called Entity Relationships. There are a total of 4 types of such relationships.

One-to-One

This type of relationship indicates that one entity is associated with only one other entity. For example, a country may have only one capital. If we represent this in terms of tables and entities, there will be a One-to-One relationship between them. In Hibernate, to specify such a relationship, we use the @OneToOne annotation:

If this annotation is present above an entity field, then in the corresponding table, a foreign key column will be created, which will contain a reference to the identifier of the associated entity.

It will look like this:

id name capital_id
1 USA 11
2 Nicaragua 4
3 United Kingdom 17
4 Austria 8

Obviously, in the "cities" table, capitals will be stored with their IDs. For example, under ID 4, there will be "Managua," under 11, "Washington," and so on.

One-to-Many

In such a relationship, one entity will be associated with multiple other entities. It's easy to understand with the example of a director and their movies. One director can have many different movies, but each movie should have only one director.

Note

Yes, I know that there are cases where multiple directors work on a movie, but for our example now, that's not important.

To denote such a relationship, the @OneToMany annotation is placed above the field in the entity class. The field must be a list:

If such an annotation is present above the field, then a separate table will be created in the database, which will contain the identifiers of this entity and the associated entity.

It will look like this:

director_id movie_id
1 4
2 6
1 8
6 1

Such IDs will reference both entities, showing the relationships between them.

Many-to-One

In this type, many entities are related to one. You might think of it as the reverse of One-to-Many, and you would be correct. For a better understanding, consider the example of student-university relationships. Many students can be associated with one university.

To establish such a relationship, you need to specify the @ManyToOne annotation above the field that is not a collection:

If this annotation is present above the entity field, the corresponding table will receive a foreign key column containing a reference to the identifier of the associated entity.

It will look like this:

id name university_id
1 John 1
2 Bob 1
3 Alice 6
4 Daniel 3

This resembles One-to-One, but here, different students may have the same reference to the university ID.

Many-to-Many

In this type of relationship, many entities are associated with many entities. You can think of this relationship in terms of a driver and a car. One driver can have many cars, and one car can have many drivers (for example, in a taxi service).

In the code, you need to place the @ManyToMany annotation above the field in the entity:

If this annotation is present above a field of the entity (which must be a collection), then a separate table will be created in the database, which contains identifiers of the current entity and the associated entity.

It will look like this:

driver_id car_id
1 3
1 4
2 3
3 3

As you can see, here, a driver can have multiple cars, and likewise, a car can have multiple drivers.

You may have noticed that in some cases, we only need to specify the annotation in one class, while in other cases, it is necessary to specify it in both entity classes simultaneously. Let's discuss this in more detail.

Unidirectional and Bidirectional Relationships

Unidirectional and bidirectional relationships between entities indicate how they interact.

  • Unidirectional Relationships: These are the relationships we have discussed before. In such a relationship, the relationship annotation is specified only in one entity class. Simply put, the class that contains the annotated field knows that it is related to another class, while the other class, which does not contain the annotation, is unaware of the relationship. It's not complicated, but we're more interested in the second case;
  • Bidirectional Relationships: In this type of relationship, Class A is aware of its relationship with Class B, just as Class B is aware of its relationship with Class A. The relationship annotation will be specified in both entity classes, and it will look like this:
java

A.java

java

B.java

Interaction between these two entities occurs in both directions.

To ensure consistency during data operations, we must define the "owning side" and the "inverse side." The owning side is the side that controls the relationship, and updates to the relationship in the database occur only after changes are made on the owning side.

To determine the owning side, we use a parameter in the relationship annotation. The mappedBy parameter uses the name of the field in the entity we want to make the owning side. In other words, we use this annotation in the entity we want to make the inverse side.

For a clearer example, let's make class B the owning side:

Now entity B has become the owning side in these bidirectional relationships. The data will be written to the database only when changes are made in class B.

This is done to avoid creating a new table containing the identifiers of these two entities. Now, instead, a separate column, "A_id" will be added to the entity B table, where the relationship IDs will be specified.

It is considered good practice to make the "ManyToOne" side the owning side. For bidirectional relationships "Many-to-Many," either side can be designated as the owning side.

Later, we'll use this in practice, and you'll better understand how it works and why it's necessary.

Fetch Types

In entity relationships, different fetch types are used. There are two of them, let's discuss each:

  • EAGER means that all necessary data is fetched in a single query. When we retrieve an entity from the database that has @OneToOne or @ManyToOne fields, we also get complete information about these related entities. This means that with one query to the database, we immediately retrieve information from two tables, which consumes resources, memory, and execution time. Such a fetch type significantly impacts optimization;
  • LAZY means that data is fetched from the database only when it is actually needed for a specific operation. When we retrieve an entity from the database that has @OneToMany or @ManyToMany fields, we do not get information about these related entities. This is a significant advantage because we do not fetch unnecessary information from the database, only the data we need. This means that only the table we are referring to is affected, without touching the tables associated with this entity.

Note

Remembering this is quite simple: Relationships ending in Many will have a fetch type of LAZY; the rest will be EAGER.

It is recommended to always use LAZY fetch type, as it makes the code more optimized. You can do this using the command fetch = FetchType.LAZY in the annotation parameters.

Here's how it looks in the code:

Now, when executing a query for this entity, we will only affect it, and only access other data if necessary.

Cascade Types

Now that we've learned how to interact with only the entity we're working with, let's learn how to specify specific fields on which interaction should propagate when relating entities in any case. Cascades will help us with this.

Cascade types indicate which actions should automatically propagate from one entity to other related entities when certain data operations are performed, such as saving, updating, or deleting.

Note

Such cascades are part of the JPA ( Jakarta Persistence API ) library, which is the parent class of Hibernate. So in Hibernate, we can also use such classes.

There are a total of 4 types:

  • PERSIST — invoking the persist() method is propagated to related entities;
  • MERGE — invoking the merge() method is propagated to related entities;
  • REMOVE — invoking the remove() method is propagated to related entities;
  • ALL - all the above-mentioned operations are propagated to related entities.

Each of these cascade types serves its purpose.

Let's imagine a situation where we are saving a new student, and we want changes to also be made to the "universities" table, for example, increasing the number of students.

Here's how we can implement this:

In this case, when making changes using the persist() method on the Student Entity, changes will also be made to the University Entity.

Assigning a Department to an Employee

For now, the scary theory in this chapter is finished.

Now, let's recall the employee management project we've been working on and think about the relationship between the Employee and Department entities. It's obvious that an employee can only work in one department, while many employees can work in one department. In this case, we can conclude that a One-to-Many relationship will be established from the side of the Employee and a Many-to-One relationship from the side of the Department.

Let's establish a unidirectional relationship in these tables.

To achieve this, we'll add the annotation @ManyToOne(fetch = FetchType.LAZY), and also use the new annotation JoinColumn(name = "department_id"), to avoid creating a new table.

Yes, this approach also has its place, as bidirectional relationships are quite complex to understand and implement, and this way we'll get the benefits of bidirectional relationships without unnecessary hassle.

The Employee entity will look like this:

No other changes are needed, as Hibernate will handle everything for us. Now, let's use the getAll() method implemented in the previous chapter to see the changes:

1. What type of relationship is best represented by a director and their movies?
2. In a Many-to-One relationship, where is the foreign key column created?
3. What does the `@ManyToMany` annotation signify in Hibernate?
4. Which fetch type is recommended for optimizing code in Hibernate?
5. When defining a Many-to-One relationship in Hibernate, which annotation is used to specify the column name in the database?

What type of relationship is best represented by a director and their movies?

Виберіть правильну відповідь

In a Many-to-One relationship, where is the foreign key column created?

Виберіть правильну відповідь

What does the @ManyToMany annotation signify in Hibernate?

Виберіть правильну відповідь

Which fetch type is recommended for optimizing code in Hibernate?

Виберіть правильну відповідь

When defining a Many-to-One relationship in Hibernate, which annotation is used to specify the column name in the database?

Виберіть правильну відповідь

Все було зрозуміло?

Секція 2. Розділ 9
course content

Зміст курсу

Java Data Manipulation with Hibernate

Entity Relations and Cascade TypesEntity Relations and Cascade Types

Entity Relationships

As you may know from the Relational Databases and Normalization course, in SQL, there are certain relationships between tables. Similarly, in Hibernate, one entity can reference another and vice versa. These are called Entity Relationships. There are a total of 4 types of such relationships.

One-to-One

This type of relationship indicates that one entity is associated with only one other entity. For example, a country may have only one capital. If we represent this in terms of tables and entities, there will be a One-to-One relationship between them. In Hibernate, to specify such a relationship, we use the @OneToOne annotation:

If this annotation is present above an entity field, then in the corresponding table, a foreign key column will be created, which will contain a reference to the identifier of the associated entity.

It will look like this:

id name capital_id
1 USA 11
2 Nicaragua 4
3 United Kingdom 17
4 Austria 8

Obviously, in the "cities" table, capitals will be stored with their IDs. For example, under ID 4, there will be "Managua," under 11, "Washington," and so on.

One-to-Many

In such a relationship, one entity will be associated with multiple other entities. It's easy to understand with the example of a director and their movies. One director can have many different movies, but each movie should have only one director.

Note

Yes, I know that there are cases where multiple directors work on a movie, but for our example now, that's not important.

To denote such a relationship, the @OneToMany annotation is placed above the field in the entity class. The field must be a list:

If such an annotation is present above the field, then a separate table will be created in the database, which will contain the identifiers of this entity and the associated entity.

It will look like this:

director_id movie_id
1 4
2 6
1 8
6 1

Such IDs will reference both entities, showing the relationships between them.

Many-to-One

In this type, many entities are related to one. You might think of it as the reverse of One-to-Many, and you would be correct. For a better understanding, consider the example of student-university relationships. Many students can be associated with one university.

To establish such a relationship, you need to specify the @ManyToOne annotation above the field that is not a collection:

If this annotation is present above the entity field, the corresponding table will receive a foreign key column containing a reference to the identifier of the associated entity.

It will look like this:

id name university_id
1 John 1
2 Bob 1
3 Alice 6
4 Daniel 3

This resembles One-to-One, but here, different students may have the same reference to the university ID.

Many-to-Many

In this type of relationship, many entities are associated with many entities. You can think of this relationship in terms of a driver and a car. One driver can have many cars, and one car can have many drivers (for example, in a taxi service).

In the code, you need to place the @ManyToMany annotation above the field in the entity:

If this annotation is present above a field of the entity (which must be a collection), then a separate table will be created in the database, which contains identifiers of the current entity and the associated entity.

It will look like this:

driver_id car_id
1 3
1 4
2 3
3 3

As you can see, here, a driver can have multiple cars, and likewise, a car can have multiple drivers.

You may have noticed that in some cases, we only need to specify the annotation in one class, while in other cases, it is necessary to specify it in both entity classes simultaneously. Let's discuss this in more detail.

Unidirectional and Bidirectional Relationships

Unidirectional and bidirectional relationships between entities indicate how they interact.

  • Unidirectional Relationships: These are the relationships we have discussed before. In such a relationship, the relationship annotation is specified only in one entity class. Simply put, the class that contains the annotated field knows that it is related to another class, while the other class, which does not contain the annotation, is unaware of the relationship. It's not complicated, but we're more interested in the second case;
  • Bidirectional Relationships: In this type of relationship, Class A is aware of its relationship with Class B, just as Class B is aware of its relationship with Class A. The relationship annotation will be specified in both entity classes, and it will look like this:
java

A.java

java

B.java

Interaction between these two entities occurs in both directions.

To ensure consistency during data operations, we must define the "owning side" and the "inverse side." The owning side is the side that controls the relationship, and updates to the relationship in the database occur only after changes are made on the owning side.

To determine the owning side, we use a parameter in the relationship annotation. The mappedBy parameter uses the name of the field in the entity we want to make the owning side. In other words, we use this annotation in the entity we want to make the inverse side.

For a clearer example, let's make class B the owning side:

Now entity B has become the owning side in these bidirectional relationships. The data will be written to the database only when changes are made in class B.

This is done to avoid creating a new table containing the identifiers of these two entities. Now, instead, a separate column, "A_id" will be added to the entity B table, where the relationship IDs will be specified.

It is considered good practice to make the "ManyToOne" side the owning side. For bidirectional relationships "Many-to-Many," either side can be designated as the owning side.

Later, we'll use this in practice, and you'll better understand how it works and why it's necessary.

Fetch Types

In entity relationships, different fetch types are used. There are two of them, let's discuss each:

  • EAGER means that all necessary data is fetched in a single query. When we retrieve an entity from the database that has @OneToOne or @ManyToOne fields, we also get complete information about these related entities. This means that with one query to the database, we immediately retrieve information from two tables, which consumes resources, memory, and execution time. Such a fetch type significantly impacts optimization;
  • LAZY means that data is fetched from the database only when it is actually needed for a specific operation. When we retrieve an entity from the database that has @OneToMany or @ManyToMany fields, we do not get information about these related entities. This is a significant advantage because we do not fetch unnecessary information from the database, only the data we need. This means that only the table we are referring to is affected, without touching the tables associated with this entity.

Note

Remembering this is quite simple: Relationships ending in Many will have a fetch type of LAZY; the rest will be EAGER.

It is recommended to always use LAZY fetch type, as it makes the code more optimized. You can do this using the command fetch = FetchType.LAZY in the annotation parameters.

Here's how it looks in the code:

Now, when executing a query for this entity, we will only affect it, and only access other data if necessary.

Cascade Types

Now that we've learned how to interact with only the entity we're working with, let's learn how to specify specific fields on which interaction should propagate when relating entities in any case. Cascades will help us with this.

Cascade types indicate which actions should automatically propagate from one entity to other related entities when certain data operations are performed, such as saving, updating, or deleting.

Note

Such cascades are part of the JPA ( Jakarta Persistence API ) library, which is the parent class of Hibernate. So in Hibernate, we can also use such classes.

There are a total of 4 types:

  • PERSIST — invoking the persist() method is propagated to related entities;
  • MERGE — invoking the merge() method is propagated to related entities;
  • REMOVE — invoking the remove() method is propagated to related entities;
  • ALL - all the above-mentioned operations are propagated to related entities.

Each of these cascade types serves its purpose.

Let's imagine a situation where we are saving a new student, and we want changes to also be made to the "universities" table, for example, increasing the number of students.

Here's how we can implement this:

In this case, when making changes using the persist() method on the Student Entity, changes will also be made to the University Entity.

Assigning a Department to an Employee

For now, the scary theory in this chapter is finished.

Now, let's recall the employee management project we've been working on and think about the relationship between the Employee and Department entities. It's obvious that an employee can only work in one department, while many employees can work in one department. In this case, we can conclude that a One-to-Many relationship will be established from the side of the Employee and a Many-to-One relationship from the side of the Department.

Let's establish a unidirectional relationship in these tables.

To achieve this, we'll add the annotation @ManyToOne(fetch = FetchType.LAZY), and also use the new annotation JoinColumn(name = "department_id"), to avoid creating a new table.

Yes, this approach also has its place, as bidirectional relationships are quite complex to understand and implement, and this way we'll get the benefits of bidirectional relationships without unnecessary hassle.

The Employee entity will look like this:

No other changes are needed, as Hibernate will handle everything for us. Now, let's use the getAll() method implemented in the previous chapter to see the changes:

1. What type of relationship is best represented by a director and their movies?
2. In a Many-to-One relationship, where is the foreign key column created?
3. What does the `@ManyToMany` annotation signify in Hibernate?
4. Which fetch type is recommended for optimizing code in Hibernate?
5. When defining a Many-to-One relationship in Hibernate, which annotation is used to specify the column name in the database?

What type of relationship is best represented by a director and their movies?

Виберіть правильну відповідь

In a Many-to-One relationship, where is the foreign key column created?

Виберіть правильну відповідь

What does the @ManyToMany annotation signify in Hibernate?

Виберіть правильну відповідь

Which fetch type is recommended for optimizing code in Hibernate?

Виберіть правильну відповідь

When defining a Many-to-One relationship in Hibernate, which annotation is used to specify the column name in the database?

Виберіть правильну відповідь

Все було зрозуміло?

Секція 2. Розділ 9
some-alt