Could not write JSON: Infinite recursion (StackOverflowError); nested exception spring boot - json

This is my District Controller, when I try to fetch data after saving I get the error, even when I try get object form getDistrict(Long id) the same strikes please suggest some way, am very new at spring environment:
package com.gad.services;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.gad.repositories.DistrictMasterRepositories;
import com.gad.rmodels.Districtmaster;
import com.gad.rmodels.Statemaster;
#Service
public class DistricMasterServices {
#Autowired
DistrictMasterRepositories districtMasterRepositories;
#Autowired
StateMasterServices stateMasterServices;
List<Districtmaster> districtmaster;
public Iterable<Districtmaster> savenewdistrict(Long id,Districtmaster districtmaster_rec){
System.out.println(id);
Statemaster statemaster=null;
statemaster = stateMasterServices.getStateById(id);
System.out.println("savenewdistrict");
districtmaster_rec.setStatemaster(statemaster);
districtMasterRepositories.save(districtmaster_rec);
Iterable<Districtmaster>districtmaster2 = districtMasterRepositories.findAll();
return districtmaster2;
}
public Districtmaster getDistrict(Long id){
Districtmaster districtmaster = districtMasterRepositories.findOne(id);
return districtmaster;
}
}
The model class for state:
package com.gad.rmodels;
import static javax.persistence.GenerationType.SEQUENCE;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
/**
* Statemaster generated by hbm2java
*/
#Entity
#Table(name="statemaster"
,schema="aop_gad_v1"
)
public class Statemaster implements java.io.Serializable {
private long id;
private String stateName;
private Set<Districtmaster> districtmasters = new HashSet<Districtmaster>(0);
public Statemaster() {
}
public Statemaster(long id) {
this.id = id;
}
public Statemaster(long id, String stateName, Set<Districtmaster> districtmasters) {
this.id = id;
this.stateName = stateName;
this.districtmasters = districtmasters;
}
#SequenceGenerator(name="generator_statemasterid", sequenceName="aop_gad_v1.gad_statemaster_seq")
#Id
#GeneratedValue(strategy=SEQUENCE, generator="generator_statemasterid")
#Column(name="id", unique=true, nullable=false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
#Column(name="state_name", length=20)
public String getStateName() {
return this.stateName;
}
public void setStateName(String stateName) {
this.stateName = stateName;
}
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
return this.districtmasters;
}
public void setDistrictmasters(Set<Districtmaster> districtmasters) {
this.districtmasters = districtmasters;
}
}
Distric model:
package com.gad.rmodels;
import static javax.persistence.GenerationType.SEQUENCE;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
/**
* Districtmaster generated by hbm2java
*/
#SuppressWarnings("serial")
#Entity
#Table(name="districtmaster",schema="aop_gad_v1")
public class Districtmaster implements java.io.Serializable {
private long id;
private Statemaster statemaster;
private String districtName;
private Set<GadGuestHouseMaster> gadGuestHouseMasters = new HashSet<GadGuestHouseMaster>(0);
public Districtmaster() {
}
public Districtmaster(long id) {
this.id = id;
}
public Districtmaster(long id, Statemaster statemaster, String districtName, Set<GadGuestHouseMaster> gadGuestHouseMasters) {
this.id = id;
this.statemaster = statemaster;
this.districtName = districtName;
this.gadGuestHouseMasters = gadGuestHouseMasters;
}
#SequenceGenerator(name="generator_districtmasterid", sequenceName="aop_gad_v1.gad_districtmasterid_seq")
#Id
#GeneratedValue(strategy=SEQUENCE, generator="generator_districtmasterid")
#Column(name="id", unique=true, nullable=false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="district_of_state")
public Statemaster getStatemaster() {
return this.statemaster;
}
public void setStatemaster(Statemaster statemaster) {
this.statemaster = statemaster;
}
#Column(name="district_name", length=20)
public String getDistrictName() {
return this.districtName;
}
public void setDistrictName(String districtName) {
this.districtName = districtName;
}
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="districtmaster")
public Set<GadGuestHouseMaster> getGadGuestHouseMasters() {
return this.gadGuestHouseMasters;
}
public void setGadGuestHouseMasters(Set<GadGuestHouseMaster> gadGuestHouseMasters) {
this.gadGuestHouseMasters = gadGuestHouseMasters;
}
}
The Error I get:
[{"timestamp":1512641978311,"status":200,"error":"OK","exception":"org.springframework.http.converter.HttpMessageNotWritableException","message":"Could
not write JSON: Infinite recursion (StackOverflowError); nested
exception is com.fasterxml.jackson.databind.JsonMappingException:
Infinite recursion (StackOverflowError) (through reference chain:
com.gad.rmodels.Statemaster[\"districtmasters\"]->org.hibernate.collection.internal.PersistentSet[0]-

You are facing this issue because the Statemaster model contains the object of Districtmaster model, which itself contains the object of Statemaster model. This causes an infinite json recursion.
You can solve this issue by 3 methods.
1 - Create a DTO and include only the fields that you want to display in the response.
2 - You can use the #JsonManagedReference and #JsonBackReference annotations.
E.g. Add the #JsonManagedReference annotation to the Statemaster model.
#JsonManagedReference
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
return this.districtmasters;
}
Add the #JsonBackReference annotation to the Districtmaster model.
#JsonBackReference
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="district_of_state")
public Statemaster getStatemaster() {
return this.statemaster;
}
3 - You can use the #JsonIgnore annotation on the getter or setter method.
#JsonIgnore
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
return this.districtmasters;
}
However, this approach will omit the set of Districtmaster from the response.

I've been struggling with the same problem for days, I tried #JsonIgnore, #JsonManagedReference and #JsonBackReference annotation, and even #JsonIdentityInfo annotation and none of them have worked.
If you (the future readers ) are in the same situation, the solution is easier than you expected, simply put #JsonIgnore or #JsonManagedReference / #JsonBackReference on the attribute's getter and not on the attribute itself. And that will do.
Here's a simple example how to do so :
Say we have two classes, Order and Product, and OneToMany relation between them.
Order class
public class Order{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id_order;
private double price;
#OneToMany(mappedBy = "order")
#LazyCollection(LazyCollectionOption.FALSE)
private List<Product> products
//constructor, getters & setter
}
Product Class:
public class Product{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id_product;
private String name;
#ManyToOne
#JoinColumn(name = "id_order")
private Order order;
//consturctor, getters & setters
}
So in order to use #JsonManagedReference and #JsonBackReference, just add them to the getters as the following :
public class Order{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id_order;
private double price;
#OneToMany(mappedBy = "order")
#LazyCollection(LazyCollectionOption.FALSE)
private List<Product> products
//constructor, getters & setter
#JsonManagedReference
public List<Product> getProducts(){
return products;
}
Product class:
public class Product{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id_product;
private String name;
#ManyToOne
#JoinColumn(name = "id_order")
private Order order;
//consturctor, getters & setters
#JsonBackReference
public Order getOrder(){
return order;
}
}

#JsonBackReference and #JsonManagedReference didn't work for me since I was using a class that represented a join table and the only way to get it to work was to put the #JsonBackReference above the fields representing the linked classes. The net effect was that if I was pulling a record from the join class using JSON, none of the join fields would show up, making the returned information useless.
I'd give examples but I like to avoid long replies. #JsonIdentityInfo explained in the article below provides a short, simple but highly efficient solution.
Bidirectional relationships and InfiniteRecursion

That's because for Statemaster in json the set of Districtmaster's is put.
And each Districtmaster has the Statemaster in itself, so it's also put into the json. So that you get the infinite recursion
#JsonIgnore
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY,
mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
return this.districtmasters;
}
Adding #JsonIgnore annotation on Set<Districtmaster> will prevent that recursion.
You can put the #JsonIgnore at public Statemaster getStatemaster() either.

This issue occurs when dealing with bi-directional mapping. Use #JsonManagedReferenc and #JsonBackReference in DAO/Entity class
#JsonManagedReference is the forward part of reference – the one that gets serialized normally.
#JsonBackReference is the back part of reference – it will be omitted from serialization.
(Read More)

In the Statemaster class make districtmasters a List<Districtmaster> instead of Set<Districtmaster> and change the getter method accordingly: public List<Districtmaster> getDistrictmasters().
Unfortunately I can not explain why, but this worked for me.

Use List instead of Set. This is how I solved my issue.

If you are using Lombok:
The problem may be due to using Lombok and Set together.
Lombok creates an equals method that is used by Set in Java to determine whether two objects are the same or not. The equals method that Lombok generates calls the equals method of another class, which in turn calls back the equals method of the first class, thus generating infinite recursion.
To solve the problem, you need either to use List instead of Set, or to override the equals and hashCode methods (so Lombok will not generate them).
I've found out this trough another StackOverflow question.

Looks like your problem is Hibernate relations. When you try to serialize the entity Statemaster the serializer calls serialization of the Districtmaster set which in turn somehow reference the Statemaster again.
There are two possible ways to solve:
Unproxy object
Create DTO (Data Transfer Object) - kind of copy of your entity
where all necessary fields should be assigned and return the DTO.

There are 2 different ways to solve the problem:
You need to implement Serializable on all the #Data classes/DTOs being used, and make sure to generate a private static final long serialVersionUID for the class. Advantage: you may use "lombok" #Data/#Getter/#Setter annotations this way.
Put #JsonIgnore on every getter/setter in all the classes/DTOs being used. Disadvantage: using "lombok" #Data/#Getter/#Setter annotations may be problematic.

Error message i got in my project
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion
In my case, i also faced this same problem to solve this I just use #JsonIgnore, and it's working fine. Then I replace #JsonIgnore with #JsonBackReference also worked perfectly.
To understand the scenario please check the Project, Spouse, Address, and Employee Class
Address Class
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
#Getter
#Setter
#ToString
#NoArgsConstructor
#EqualsAndHashCode
#Entity
#Table(name = "address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String line1;
private String line2;
private String zipCode;
private String city;
private String state;
private String country;
#JsonIgnore
//#JsonBackReference
#ManyToOne
private Employee employee;
public Address(String line1, String line2, String zipCode, String city, String state, String country, Employee employee) {
this.line1 = line1;
this.line2 = line2;
this.zipCode = zipCode;
this.city = city;
this.state = state;
this.country = country;
this.employee = employee;
}
}
Employee Class
#Getter
#Setter
#NoArgsConstructor
#ToString
#EqualsAndHashCode
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
private Integer employeeId;
private String employeeName;
private String employeeCity;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "fk_spouse")
private Spouse spouse;
#OneToMany(cascade = CascadeType.ALL)
private List<Address> addresses;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "employee_project",
joinColumns = #JoinColumn(name = "fk_employee"),
inverseJoinColumns = #JoinColumn(name = "fk_project"))
private List<Project> projects;
public Employee(String employeeName, String employeeCity, Spouse spouse, List<Address> addresses, List<Project> projects) {
this.employeeName = employeeName;
this.employeeCity = employeeCity;
this.spouse = spouse;
this.addresses = addresses;
this.projects = projects;
}
public void removeProject(Project project){
this.projects.remove(project);
project.getEmployees().remove(project);
}
public void addProject(Project project){
this.projects.add(project);
project.getEmployees().add(this);
}
}
Project class
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import javax.persistence.*;
import java.util.List;
#Getter
#Setter
#ToString
#EqualsAndHashCode
#NoArgsConstructor
#Entity
#Table(name = "project")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String clientName;
#JsonIgnore
// #JsonBackReference
#ManyToMany(mappedBy = "projects")
private List<Employee> employees;
public Project(String name, String clientName, List<Employee> employees) {
this.name = name;
this.clientName = clientName;
this.employees = employees;
}
}
Spouse Class
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import javax.persistence.*;
#Getter
#Setter
#NoArgsConstructor
#ToString
#EqualsAndHashCode
#Entity
#Table(name = "spouse")
public class Spouse {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String mobileNo;
private Long age;
// #JsonBackReference
#JsonIgnore
#OneToOne(mappedBy = "spouse")
private Employee employee;
public Spouse(String name, String mobileNo, Long age, Employee employee) {
this.name = name;
this.mobileNo = mobileNo;
this.age = age;
this.employee = employee;
}
}
I know both #JsonIgnore and #JsonManagedReference, #JsonBackReference are used to solve the Infinite recursion (StackOverflowError)
Answer
These are Jackson Annotations.
#JsonIgnore is not created to resolve the Infinite recursion (StackOverflowError) problem, only ignores the property of serialization and deserialization, Just avoid the infinite recursion.
Moreover, #JsonManagedReference and #JsonBackReference are created to handle two ways linkage between fields, if you don't need those properties in the serialization or deserialization process, you can use #JsonIgnore. Otherwise, using the #JsonManagedReference /#JsonBackReference pair is the way to go.

Related

How to save a raw JSON in the MySQL using JPA

I need to save this JSON in MySQL database, and later recover it and display in the frontend
I'm using Spring boot with JPA, but i have been facing errors about serialization when the Request comes from frontend, like this:
I send the json from the frontend to my controller:
#RestController
#RequestMapping("/theoriclesson")
#CrossOrigin
public class TheoricLessonController implements ITheoricLessonController {
#Autowired
private TheoricLessonRepository theoricLessonRepository;
#Autowired
private ModuleRepository moduleRepository;
#Override
#PostMapping("/")
public void add(#RequestBody TheoricLesson theoricLesson) {
Module module = new Module();
module.setName("Modulo de teste");
TheoricLesson tc = new TheoricLesson();
Module mP = moduleRepository.save(module);
tc.setModule(mP);
theoricLessonRepository.save(tc);
}
}
and my entities:
#Data
#Entity
#Builder
public class TheoricLesson {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long xpEarned;
private String lessonName;
#Embedded
private DraftJsText draftJsText;
#ManyToOne
#JoinColumn(name = "module_id")
#JsonIgnore
private Module module;
}
#Getter
#Setter
#ToString
#Embeddable
public class DraftJsText {
public DraftJsText() {
}
public DraftJsText(String blocks, String entityMap) {
this.blocks = blocks;
this.entityMap = entityMap;
}
#Column(columnDefinition = "JSON")
private String blocks;
#Column(columnDefinition = "JSON")
private String entityMap;
}

JPA: Many to many relationship - JsonMappingException: Infinite recursion

I'm having trouble with a many to many relation with JPA.
My code looks as follows:
The Sensor class:
#Entity
#Table(name = "sensor")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Sensor {
#Id
private long chipId;
#OneToMany(mappedBy = "sensor")
#JsonBackReference
private Set<Link> userLinks;
private String firmwareVersion;
private long creationTimestamp;
private String notes;
private long lastMeasurementTimestamp;
private long lastEditTimestamp;
private double gpsLatitude;
private double gpsLongitude;
private double gpsAltitude;
private String country;
private String city;
private boolean indoor;
private boolean published;
}
The user class:
#Entity
#Table(name = "user")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonManagedReference
private int id;
private String firstName;
private String lastName;
private String email;
private String password;
#OneToMany(mappedBy = "user")
private Set<Link> sensorLinks;
private int role;
private int status;
private long creationTimestamp;
private long lastEditTimestamp;
}
And the Link class (relation class):
#Entity
#Table(name = "link")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Link {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "user_id")
#MapsId("user_id")
private User user;
#ManyToOne
#JoinColumn(name = "sensor_id")
#MapsId("sensor_id")
private Sensor sensor;
private boolean owner;
private String name;
private int color;
private long creationTimestamp;
}
The controller:
...
#RequestMapping(method = RequestMethod.GET, path = "/user/{email}", produces = MediaType.APPLICATION_JSON_VALUE)
#ApiOperation(value = "Returns details for one specific user")
public User getUserByEmail(#PathVariable("email") String email) {
return userRepository.findByEmail(email).orElse(null);
}
...
The UserRepository:
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByEmail(String email);
#Modifying
#Query("UPDATE User u SET u.firstName = ?2, u.lastName = ?3, u.password = ?4, u.role = ?5, u.status = ?6 WHERE u.id = ?1")
Integer updateUser(int id, String firstName, String lastName, String password, int role, int status);
}
I want to achieve, that the user endpoint shows all linked sensors with that particular user.
What I get is only an error message:
JSON mapping problem:
com.chillibits.particulatematterapi.model.db.main.User["sensorLinks"];
nested exception is
com.fasterxml.jackson.databind.JsonMappingException: Infinite
recursion (StackOverflowError) (through reference chain:
com.chillibits.particulatematterapi.model.db.main.User["sensorLinks"])
How can I fix this issue?
Thanks in advance
Marc
------------------------------------ Edit -----------------------------------
According to Abinash Ghosh's answer, I added following DTOs:
UserDto:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class UserDto {
private int id;
private String firstName;
private String lastName;
private Set<LinkDto> sensorLinks;
private int role;
private int status;
private long creationTimestamp;
private long lastEditTimestamp;
}
LinkDto:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class LinkDto {
private Integer id;
private SensorDto sensor;
private boolean owner;
private String name;
private int color;
private long creationTimestamp;
}
And the mapper (I realized it a bit different, but it should be the same):
public UserDto getUserByEmail(#PathVariable("email") String email) {
User user = userRepository.findByEmail(email).orElse(null);
return convertToDto(user);
}
private UserDto convertToDto(User user) {
return mapper.map(user, UserDto.class);
}
This leads to following Exception:
2020-04-13 14:22:24.383 WARN 8176 --- [nio-8080-exec-2] o.h.e.loading.internal.LoadContexts : HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext#68ab57c7<rs=HikariProxyResultSet#2017009664 wrapping Result set representing update count of -1>
1) Error mapping com.chillibits.particulatematterapi.model.db.main.User to com.chillibits.particulatematterapi.model.io.UserDto
1 error] with root cause
java.lang.StackOverflowError: null
at com.mysql.cj.NativeSession.execSQL(NativeSession.java:1109) ~[mysql-connector-java-8.0.19.jar:8.0.19]
...
It's working!
This post helped: https://stackoverflow.com/a/57111004/6296634
Seems that you should not use Lombok #Data in such cases.
When User serialized for the response, all getter methods of User's fields are called.
So, User relational field sensorLinks's getter are also called to set value. This happened recursively. That's cause of infinite recursion.
It's better to not use Entity as a response. Create a DTO class for User then map User entity value into DTO then send response. Don't use any Enity class again into DTO then it will result same problem
For dynamically map one model to another you can use ModleMapper
public class UserDTO {
//Fields you want to show in response & don't use enity class
private Set<LinkDTO> sensorLinks;
}
public class LinkDTO{
//Fields you want to show in response &don't use enity class
}
public User getUserByEmail(#PathVariable("email") String email) {
User user = userRepository.findByEmail(email).orElse(null);
UserDTO userDto = merge(user,UserDTO.class)
return userDto;
}
public static <T> void merge(T source, T target) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
modelMapper.map(source, target);
}

How to correct sql statement to give correct result

I'm running a local MySQL Server on port 3306 with a schema "sys" featuring a table "users"
Now I have a small spring boot application to query all entries from that table.
Model for that table is:
package com.example.databaseneu.model;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Users {
#Id
// #Column(name = "id")
private int id;
// #Column(name = "name")
private String name;
// #Column(name = "salary")
private int salary;
// #Column(name = "team_name")
private String team_name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public String getTeam_name() {
return team_name;
}
public void setTeam_name(String team_name) {
this.team_name = team_name;
}}
The connection works, but the query doesnt seem to deliver the right result as I get the Whitelabel Error Page.
Query to get all elements from the table (autogenerated by repository)
Hibernate:
select
users0_.id as id1_0_,
users0_.name as name2_0_,
users0_.salary as salary3_0_,
users0_.team_name as team_nam4_0_
from
users users0_
So I'm uncertain if i defined the Entity wrong or something else alltogether
#Column Tag doesnt do the trick.
---Edit---
package com.example.databaseneu.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.databaseneu.model.Users;
import com.example.databaseneu.repository.UserRepository;
#Controller // This means that this class is a Controller
#RequestMapping(path = "/demo") // This means URL's start with /demo (after Application path)
public class MainController {
#Autowired
private UserRepository userRepository;
#GetMapping(path = "/add")
public #ResponseBody String addNewUser(#RequestParam String name, #RequestParam int salary,
#RequestParam String team_name) {
Users n = new Users();
n.setName(name);
n.setSalary(salary);
n.setTeam_name(team_name);
userRepository.save(n);
return "Saved";
}
#GetMapping(path = "/all")
public Iterable<Users> getAllUsers() {
return userRepository.findAll();
}}
So id navigate to localhost:8080/demo/all
You have written all correct expect for one thing. Mark your return-type with #ResponseBody annotation -- similar to your addNewUser method.
#GetMapping(path = "/all")
public #ResponseBody Iterable<Users> getAllUsers() {
return userRepository.findAll();
}}
Hopefully this should work. If you still face issues, post it here.

Spring Data JPA #OneToMany infinite loop exception

OneToMany relationship causing infinite loop using Spring Data JPA with hibernate as provider
The problem here is not the type of exception but the infinite loop that causes this exception
I tried #JsonIgnoreProperties which gives me another error => 'Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer'
The post referencing the solution does not have a solution that adresses my problem.
One says use #JsonManagedReference and #JsonBackReference that does stop the recursion but excludes the object (UserGroup in 'myUser' entity) from the result which I need when I want an object of 'myUser' entity.
The other one says about overriding ToString method which I don't do.
Another one explains why there is an infinite loop and suggest as solution to not do that way. I quote "Try to create DTO or Value Object (simple POJO) without cycles from returned model and then return it."
And this one Difference between #JsonIgnore and #JsonBackReference, #JsonManagedReference explains the difference but doing so I will have the same problem as the first one
'myUser' entity
#Entity
public class MyUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String email;
private Integer age;
//#JsonIgnoreProperties({"myUsers"})
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userGroupId")
private UserGroup userGroup;
'UserGroup' entity
#Entity
public class UserGroup {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Integer groupOrder;
#OneToMany
(
mappedBy = "userGroup",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<MyUser> myUsers;
change the getUserGroup() method in your MyUser class as follows.
#Entity
public class MyUser
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String email;
private Integer age;
//#JsonIgnoreProperties({"myUsers"})
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userGroupId")
private UserGroup userGroup;
public UserGroup getUserGroup()
{
userGroup.setMyUsers(null);
return userGroup;
}
}
you need to add #JsonIgnore annotation at #OneToMany
like this
#JsonIgnore
#OneToMany
(
mappedBy = "userGroup",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<MyUser> myUsers;
I think I'm getting the point of your problem. You want to fetch MyUser including the userGroup data without the circular reference.
Based from the solutions you enumerated, I suggest you should still use the #JsonBackReference and #JsonManagedReference to prevent recursion on your entities and for the solution on your problem, you can try to use a mapper (MapStruck) and map the userGroup details to a DTO during the retrieval of data from the service.
DTOs:
public class MyUserDto {
private Long id;
private String firstName;
private String lastName;
private String email;
private Integer age;
private UserGroupDto userGroupDto;
}
public class UserGroupDto {
private Long id;
private Integer groupOrder;
}
Mapper (MapStruck):
#Mapper(componentModel = "spring")
public interface MyUserMapper {
MyUserMapper INSTANCE = Mappers.getMapper(MyUserMapper.class);
UserGroupDto userGroupToDto(UserGroup userGroup);
#Mapping(source = "myUser.userGroup", target = "userGroupDto")
MyUserDto myUserToDto(MyUser myUser);
}
After retrieving the data from your repository, you may then call the myUserToDto method to map the entity to a DTO.
This is just one way of solving your problem.

Orika Bean Mapper Spring Data ManyToMany join table overriding

In my Spring project I have domain and model layer separately. Domain layer are just entities, that maps to tables in MySQL db. Models are used in service layer. I have two tables: User and Roles, that have ManyToMany relationship.
#Entity
#Table(name = "user")
public class UserEntity {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToMany(mappedBy="users", cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private List<RoleEntity> roles;
//..Getters Setters
}
#Entity
#Table(name = "role")
public class RoleEntity {
#Id
#GeneratedValue
private Long id;
private String value;
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "role_id") , inverseJoinColumns = #JoinColumn(name = "user_id") )
private List<UserEntity> users;
}
Model
public class User {
private Long id;
private String name;
private List<Role> roles;
}
public class Role {
private Long id;
private String value;
private List<User> users;
}
Repository Layer
#Repository
public interface UserRepo extends JpaRepository<UserEntity, Long>{
}
Service
#Service
public class UserService {
#Autowired
private UserRepo userRepo;
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
MapperFacade mapper = mapperFactory.getMapperFacade();
public void save(User user) {
userRepo.save(mapper.map(user, UserEntity.class));
}
public User getUser(Long id) {
return mapper.map(userRepo.findOne(id), User.class);
}
}
The problem is - each time I retrieve user, and update it properties (name for example), and than save back it into table, row in the join table user_role, overrides too. For example if it was
id=1, user_id=1, role_id=1
, than after update it becomes
id=2, user_id=1, role_id=1
.
#Test
public void contextLoads() {
User user = userService.getUser(1L);
user.setName("kyk");
userService.save(user);
}
It works correct without mapper, so mapper is the reason. And I can't get any workaround. Has any one faced the same problem? Will be thankful for any help.