{{announcement.body}}
{{announcement.title}}

Introduction to Spring Data JPA Part 8: Many-to-Many Bidirectional

DZone 's Guide to

Introduction to Spring Data JPA Part 8: Many-to-Many Bidirectional

In this article, we discuss how to model many-to-many, bidirectional relationships with Spring Data JPA.

· Java Zone ·
Free Resource

In this article, we will discuss the following:

  • Many to Many Bidirectional.
  • @JsonIdentityInfo.
  • @JoinTable with JoinColumns and InverseJoins.
  • Bad Effects of CascadeType.ALL, Refactoring the code.
  • Using Model Classes.
  • Replacing Annotations like @JsonIdentityInfo, @JsonManagedReference, @JsonIgnore - Refactoring the code for GET Requests.

Let us start by modeling the entities:
Modeling entitiesYou can see the definition of Role in the User entity and the definition of User in Role entity. Hence, we can call it bidirectional. The requirement is that one User can have many Roles, and one Role can be associated with many Users. Hence, it is a Many-to-Many relationship. Let us see what Hibernate gives us by default.
Hibernate generated relationshipA mapping table with users_id and roles_id. Now, let us look at the entities. 

Please find the source code at https://github.com/gudpick/jpa-demo/tree/many-to-many-bidirectional-starter

Let us look at the main changes we have made.

The Role Entity

Java


The User Entity

Java


One major change we can find here is the definition of relations.

Java


Here, Role will be the parent entity, and we are using mappedBy="role" in the role entity. The @ManytoMany annotation shows that it is a Many to Many relationship, and using @ManytoMany annotations at both sides of the relation (i.e.: in Role Entity and User Entity) shows that it is a bidirectional relation.

Now, we are using a new annotation @JsonIdentityInfo. In the previous aricles, we have StackOverflow errors due to circular references. We have been using @JsonIgnore, @JsonManagedReference, and @JsonBackReference to take care of the error. This new annotation, @JsonIdentityInfo, will handle the circular reference errors for us.

We are having the User Controller as follows.

Java


RoleController

Java


Now, let us run the application. Let us create a user with two roles using the following JSON object.

POST : localhost:2003/user/create

JSON


This will give a success message. Now, let us create a Role with two users.

POST: localhost:2003/role/create

JSON


It should return a success message. Let us see the @JsonIdentityInfo impact on the GET requests. You can try removing this annotation and get the circular reference errors. 

GET: localhost:2003/user/all

You should get the result as follows.

JSON


and let us check for the user with id as 1.

GET: localhost:2003/user/details/1

The result will be:

JSON


Now, let us try the update.

PUT: localhost:2003/user/update/3

JSON


It should give a success message. Now, let us try the delete method. We are using the Cascade type as ALL.

Now, if you check the database, we find four roles and three users. Let us try deleting the role with ID 1 and role with ID 4.

localhost:2003/role/delete/1

DELETE : localhost:2003/role/delete/4

Now, if you check the database you will find that there are no users present in the database. Now, let us run the Application again. Assuming that you have the following settings in the application.properties file.

Properties files


POST: localhost:2003/role/create

POST: localhost:2003/user/create

DELETE: localhost:2003/user/delete/1

DELETE: localhost:2003/user/delete/3

Now, you will see that all the data in the database is deleted. Well, this is not what is intended. Let us refactor our code and make sure we get the desired result.

Java


I am removing CascadeType.ALL and add making changes as above. The intention is to remove CascadeType.Remove, which was causing havoc in the delete operation. Now, when we run the application and try the following. Please use the JSON objects as given before.

POST: localhost:2003/role/create

POST:localhost:2003/user/create

Now, let us try deleting a role as below.

DELETE: localhost:2003/role/delete/1

We will get an exception as below.

 could not execute statement; SQL [n/a];

 constraint [fkj47yp3hhtsoajht9793tbdrp4]; 

nested exception is org.hibernate.exception.ConstraintViolationException:

 could not execute statement 

Well, let us correct this by making a small change in the deleteRole method. Consider this as a starting point. You can move on from here. Keep me posted in the comment section for better ways of doing it. Will discuss and debate. 

Java


Let us change this method as follows

Java


I have introduced a check to find whether there are any users associated with the role.

Java


If true, we are not allowing to delete the role, so an exception will not be thrown. Instead, we get a custom message as below.

Plain Text


Please find source code at https://github.com/gudpick/jpa-demo/tree/many-to-many-bidirectional-refactor-delete-cascade.

Let us discuss one more way of defining the Mappings.

User Entity

Java


Role Entity

Java


Here, we are explicitly defining the mapping table. The name refers to the name of the table, which can be changed. Now, we have joinColumns, where we are defining the columns that are to be joined. Here, we are joining user_id and role_id, where user_id refers to the Id of the user table, and role_id refers to Id of the Role table. So, @JoinColumn will refer to the User table, and inverseJoinColumns will refer to the Role table.

We can also do as below.

Java


We will get a mapping table as below.Example mapping table

Please find source code at https://github.com/gudpick/jpa-demo/tree/many-to-many-bidirectional-define-mapping.

Now, my issue is regarding the get methods. If you see, the get methods give a result, as below, which we surely cannot appreciate.

JSON


Let us get it better. I am just introducing two model classes and making some changes in the code so that it gives me a better result.

UserModel

Java


RoleModel

Java


Now, let us see the following methods in the UserService class.

Java


I am setting all the values to the model and returning the model. Have a look at the controller

UserController GET methods

Java


Now, let us run the application and see all the GET methods.

POST: localhost:2003/role/create

POST: localhost:2003/user/create

Now, let us run the GET: localhost:2003/user/details/1

You get the result as below:

JSON


GET: localhost:2003/user/all

JSON


We have many options now. @JsonIgnore, @JsonManageReference, @JsonIdentityInfo, and writing code to format the JSON. You are a better judge for what suits you and what your requirements are.

Please find source code at https://github.com/gudpick/jpa-demo/tree/many-to-many-bidirectional-getters.

Please find the video tutorials at:

Topics:
jpa ,microservices ,oauth ,spring boot ,spring data jpa ,spring security

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}