Introduction to Spring Data JPA Part 8: Many-to-Many Bidirectional
Join the DZone community and get the full member experience.Join For Free
In this article, we will discuss the following:
- Many to Many Bidirectional.
- @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:
You 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.
A mapping table with
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
The User Entity
One major change we can find here is the definition of relations.
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.
Now, let us run the application. Let us create a user with two roles using the following JSON object.
POST : localhost:2003/user/create
This will give a success message. Now, let us create a Role with two users.
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.
You should get the result as follows.
and let us check for the user with id as 1.
The result will be:
Now, let us try the update.
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.
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.
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.
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.
Now, let us try deleting a role as below.
We will get an exception as below.
could not execute statement; SQL [n/a];
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.
Let us change this method as follows
I have introduced a check to find whether there are any users associated with the role.
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.
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.
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 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.
We will get a mapping table as below.
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.
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.
Now, let us see the following methods in the UserService class.
I am setting all the values to the model and returning the model. Have a look at the controller
UserController GET methods
Now, let us run the application and see all the GET methods.
Now, let us run the GET: localhost:2003/user/details/1
You get the result as below:
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:
Opinions expressed by DZone contributors are their own.