Infinite Recursion with JSON and Hibernate JPA - java

Using spring Boot , When trying to get JPA object into JSON, I keep getting this error :
nested exception is
`org.springframework.http.converter.HttpMessageNotWritableException:
Could not write JSON: Infinite recursion (StackOverflowError);
nested exception is com.fasterxml.jackson.databind.
JsonMappingException: Infinite recursion (StackOverflowError)
(through reference chain: interv.Entities.AppUser["projects"]->org.hibernate.collection.internal.PersistentBag[0]->interv.Entities.Project["appUser"]->interv.Entities.AppUser["projects"]->org.hibernate.collection.internal.Persis`
following some solution in stack overflow i ended by adding the annotation
#JsonIgnoreProperties , so my entity Project looks like this :
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Project implements Serializable{
#Id #GeneratedValue
private long id;
private String intitule;
private String description;
#OneToMany(mappedBy = "project" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#JsonIgnoreProperties("contrats")
private Collection<Contrat> contrats;
#ManyToOne
#JoinColumn(name = "Id_appUser")
#JsonIgnoreProperties("appUser")
private AppUser appUser;
}
the api restful looks like this :
import java.util.List;
#RestController
public class ProjectsController {
#Autowired
private ProjectRepo projectRepo;
#RequestMapping(path = "/ListProjects", method = RequestMethod.GET)
public List<Project> getProjects(){
return projectRepo.findAll();
}
i tried other annotations but i keep getting the same error :
in ARC extention i get :
200 OK
There was an error parsing JSON data
Unexpected end of JSON input
thank you in advance for your help :) .
EDIT :
the file AppUser.java
#Entity
#Data
#AllArgsConstructor #NoArgsConstructor
public class AppUser implements Serializable {
#Id #GeneratedValue
private Long id;
#Column(unique = true)
private String username;
private String password;
#ManyToMany(fetch = FetchType.EAGER)
private Collection<AppRole> roles = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Project> projects = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Intervention> interventions = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Contrat> contrats = new ArrayList<>();
}

The problems occurs because there is an infinite loop when generating the JSON. You can use #JsonManagedReference and #JsonBackReference to solve the Infinite recursion.
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Project implements Serializable{
#Id #GeneratedValue
private long id;
private String intitule;
private String description;
#OneToMany(mappedBy = "project" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#JsonIgnoreProperties("contrats")
private Collection<Contrat> contrats;
#ManyToOne
#JoinColumn(name = "Id_appUser")
#JsonBackReference
private AppUser appUser;
}
AppUser
#Entity
#Data
#AllArgsConstructor #NoArgsConstructor
public class AppUser implements Serializable {
#Id #GeneratedValue
private Long id;
#Column(unique = true)
private String username;
private String password;
#ManyToMany(fetch = FetchType.EAGER)
private Collection<AppRole> roles = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#JsonManagedReference
private Collection<Project> projects = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Intervention> interventions = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Contrat> contrats = new ArrayList<>();
}

Related

Spring can't detect an entity to import

I created new entity and while trying to make relation beetwen some other entity, I got an error because it can't find this class.
#Entity(name = "users")
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
private String username;
private String password;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "user_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id")
)
private Collection<Role> roles = new ArrayList<>();
#OneToMany(mappedBy = "author")
List<Post> posts = new ArrayList<>();
}
I'm talking about that OneToMany List posts, I can't import Post class
#Data
#Entity
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int Long;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "users_id")
private User author;
}
I have no idea why it's happening and how to solve it.
Just to let other knows if someone gonna have the same problem. Changing name of the entity helped and then changing it back.

How do I retrieve parent object with child object while having OneToMany bidriectional relationship in spring boot?

I am new to Spring boot. please help me with the below issue:
I am getting only child object data while retrieving using join query..
Below is my child entity class:
#Entity
#Table(name = "tenant_user_configuration")
public class TenantUserConfiguration {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "config_key")
private String configKey;
#Column(name = "config_value")
private String configValue;
private String system;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="tenant_user_id",referencedColumnName = "tenant_user_id")
#JsonBackReference
private TenantUser tenantUser;
This is my parent entity class:
#Entity
#Table(name = "tenant_user")
public class TenantUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "tenant_user_id")
private int tenantUserId;
#OneToOne
#JoinColumn(name = "tenant_id",referencedColumnName = "tenant_id")
private Tenant tenant;
#Column(name = "user_name")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "enabled")
private boolean enabled;
#OneToMany(mappedBy = "tenantUser",fetch = FetchType.EAGER)
#JsonManagedReference
private Set<TenantUserConfiguration> tenantUserConfiguration = new HashSet<>();

Hibernate’s “Object References an Unsaved Transient Instance” Error

I recently startet Programming a Back-End in Spring Boot. When implementing the Entitys and their Relationships I soon faced Hibernate’s “Object References an Unsaved Transient Instance” Error. I searched for answers in this forum and tried to fix my Cascade Annotation with this Tutorial: https://www.baeldung.com/hibernate-unsaved-transient-instance-error (which is addressing the specific Error). But when I start my Project and fill in Hard Code Data with the init() method I always Face the Error again:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : *.structure.Cart.customer -> *.structure.Customer; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : *.structure.Cart.customer -> *.structure.Customer
#Entity
#Table(name="arti")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Article {
#Id
#GeneratedValue
#Column(name = "id")
private Long articleId;
private String name;
private String manufactor;
private float price;
#ManyToMany
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.PERSIST})
#JoinColumn(name = "cart_id")
private Set<Cart> articleCart= new HashSet<>();
#ManyToMany
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.PERSIST})
#JoinColumn(name = "order_id")
private Set<Cart> articleOrder= new HashSet<>();
}
#Entity
#Table(name="cust")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Customer {
#Id
#GeneratedValue
#Column(name = "id")
private Long customerId;
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "cart_id", referencedColumnName = "id")
private Cart cart;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Order> orders = new HashSet<>();
}
#Entity
#Table(name="cart")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Cart {
#Id
#GeneratedValue
#Column(name = "id")
private Long cartId;
#OneToOne(mappedBy = "cart")
private Customer customer;
#ManyToMany
#JoinColumn(name = "article_id")
private Set<Article> articles= new HashSet<>();
}
#Entity
#Table(name="orde")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Order {
#Id
#GeneratedValue
#Column(name = "id")
private Long orderId;
#ManyToOne
#Cascade(CascadeType.SAVE_UPDATE)
#JoinColumn(name = "customer_id")
private Customer customer;
#ManyToMany
#JoinColumn(name = "article_id")
private Set<Article> articles= new HashSet<>();
}
#SpringBootApplication
public class GqlApplication {
#Autowired
private ArticleRepository articleRepository;
#Autowired
private CartRepository cartRepository;
#Autowired
private CustomerRepository customerRepository;
#Autowired
private OrderRepository orderRepository;
public static void main(String[] args) {
SpringApplication.run(GqlApplication.class, args);
}
#PostConstruct
public void init() {
Article dress = Article.builder()
.name("red dress")
.manufactor("Prada")
.price(20)
.build();
Article hat = Article.builder()
.name("brown hat")
.manufactor("Gucci")
.price(280)
.build();
Set<Article> articles = new HashSet<>();
articles.add(dress);
articles.add(hat);
Customer tom = Customer.builder()
.name("tom")
.address("Am Baum 3")
.build();
Cart cart = Cart.builder()
.customer(tom)
.articles(articles)
.build();
Order order = Order.builder()
.customer(tom)
.articles(articles)
.build();
Set<Order> orders = new HashSet<>();
orders.add(order);
tom.setCart(cart);
tom.setOrders(orders);
articleRepository.save(dress);
articleRepository.save(hat);
cartRepository.save(cart);
customerRepository.save(tom);
orderRepository.save(order);
}
}

ManyToMany relationship in "hibernate" does not insert values into the join table when initializing the application

I got a lot of answers and advice, tried changing my code according to these tips.
But, unfortunately, these tips helped only partially.
Now, when creating a new project and creating a new user, I can add the desired user to the set of projects, and the required project will be added to the set of users.
But the relationship between the desired project and the desired user will not appear in the project_user table.
Please help find the answer.
Entity Project
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "project")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#Column
private String description;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "project_users",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "users_id"))
private Set<User> projectUserSet = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "project_task",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "task_id"))
private Set<Task> projectTaskSet = new HashSet<>();
public void addUserToProject(User user){
this.projectUserSet.add(user);
user.getUserProjectsSet().add(this);
}
public void addTasksToProject(Task task){
this.projectTaskSet.add(task);
task.getTasksProjectSet().add(this);
}
//constructors, hashCode, equals, toString
}
Entity User
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String firstName;
#Column
private String lastName;
#ManyToMany(mappedBy = "projectUserSet", cascade = CascadeType.ALL)
private Set<Project> userProjectsSet = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "users_task",
joinColumns = {#JoinColumn(name = "users_id")},
inverseJoinColumns = {#JoinColumn(name = "task_id")})
private Set<Task> userTasksSet = new HashSet<>();
public void addTaskToUser(Task task) {
this.userTasksSet.add(task);
task.getTasksUserSet().add(this);
}
//constructors, hashCode, equals, toString
}
project and user initialization
Project project1 = new Project("Project1", "Project1");
User user1 = new User("User1", "User1");
project1.addUserToProject(user1);
With code shown below, table project_user is populated, verified using H2 console. In order to avoid stack overflow, I had to modify method Project#addTaskToUser as shown below.
Please note that only code relevant to the question, is included.
Normally, issue should be described by some tests. In this case, I added a CommandLineRunner that runs at startup.
CascadeType.ALL is not recommended for many-to-many relations, hence I changed this in code shown below.
Tested using H2 in-memory db.
Project class
#Data
#Entity
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "project_users",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "users_id"))
private Set<MyUser> projectUserSet = new HashSet<>();
public void addUserToProject(User user) {
this.projectUserSet.add(user);
}
}
Project repo
public interface ProjectRepo extends JpaRepository<Project, Long> { }
User class
// cannot use #Data here because it will cause cyclic ref and stack overflow when accessing userProjectsSet
#Getter
#Setter
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#ManyToMany(mappedBy="projectUserSet")
private Set<Project> userProjectsSet = new HashSet<>();
}
User repo
public interface UserRepo extends JpaRepository<User, Long> {}
In app class
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
CommandLineRunner run(ProjectRepo projectRepo, EntityManagerFactory entityManagerFactory) {
return args -> {
var testUser = new User();
testUser.setFirstName("first-name");
testUser.setLastName("last-name");
var project = new Project();
project.setName("project-name");
project.setDescription("project-description");
project.addUserToProject(testUser);
projectRepo.save(project);
// get saved user and print some properties
var userInDb = entityManagerFactory.createEntityManager().find(User.class, testUser.getId());
System.out.println(userInDb.getFirstName()); // prints "first-name"
System.out.println(userInDb.getUserProjectsSet().size()); // prints "1"
};
}
}

Foreign key is null : Hibernate Spring

I try to save object Run to database. I defined relation between Run and City. One city could have many runs. I got problem with city_id. Is null.
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
java.sql.SQLIntegrityConstraintViolationException: Column 'city_id' cannot be null
My entieties and controller:
City
#Entity
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "cities")
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "city_id")
private long id;
#OneToMany(mappedBy = "city", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Run> runs = new ArrayList<>();
private String name;
}
Run
#Entity
#Builder
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "runs")
public class Run {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name_run")
private String nameRun;
#Column(name = "distance")
private double distance;
#Column(name = "date")
private Date date;
#Column(name = "my_time")
private String myTime;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "city_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private City city;
}
Controller
#CrossOrigin
#RestController
#RequestMapping("/api/")
public class RunController {
private RunRepository runRepository;
private RunService runService;
public RunController(RunRepository runRepository, RunService runService) {
this.runRepository = runRepository;
this.runService = runService;
}
#GetMapping("runs")
public ResponseEntity<List<Run>> getRuns() {
return runService.getRuns();
}
#PostMapping("runs")
public ResponseEntity addRun(#RequestBody Run run) {
return new ResponseEntity<>(runRepository.save(run), HttpStatus.OK);
}
}
I would like to save the run in DB.
My test request looks like :
{
"nameRun": "test",
"distance":"5.0",
"date":"2020-12-12",
"myTime":"50:40",
"city":"test1"
}
Result from evaluate expresion in Intelijj:
Why the City = null? Is here error in mapping?
Can you try with this json but you need to pass city id in json.
{
"nameRun": "test",
"distance": "5.0",
"date": "2020-12-12",
"myTime": "50:40",
"city": {
"id": 1,
"name": "test1"
}
}
Thanks
First of all, use Long for id please. It is better to add #Entity annotation too.
#Entity
public class City {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "city", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Run> runs = new ArrayList<>();
}
#Entity
public class Run {
#Id
#GeneratedValue
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
private City city;
}
You need to set city_id when you save Run.
The simplest way to do that is just create a fake transient City and set id to it.
City city = new City();
city.setId(1L);
Run run = new Run();
run.setCity(city);
repository.save(run);
Obviously you should have a city with id 1L in the database.
Other options are
Use something like session.load() Hibernate analogue with Spring repository to create City without loading it from datatbase.
Load City entity entirely by id.
if you wanna save any run class,
Run run = new Run();
City city = new City();
city.getRuns().add(run);
runRepository.save(run);
if you wanna save any run class, first you need to insert to (Arraylist) runs variable of city class like city.getRuns().add(run) after filling run then you can runRepository.save(run).
Also my samples are here. You can look at myclasses.
First class is called Patient .
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#ToString
#Table(name = "aapatient")
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "AA_PATIENT_SEQ")
#SequenceGenerator(sequenceName = "AA_PATIENT_SEQ", allocationSize = 1, name = "AA_PATIENT_SEQ")
#Column(name = "patientid")
private Long patientid;
private String name;
private String lastname;
#OneToMany(mappedBy = "patient", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Problem> problems;
}
Second Class called Problem is this one.
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Entity
#Table(name="aaproblem")
public class Problem{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "AA_PATIENT_SEQ")
#SequenceGenerator(sequenceName = "AA_PATIENT_SEQ", allocationSize = 1, name = "AA_PATIENT_SEQ")
#Column(name = "problemid")
private Long problemid;
private String problemName;
private String problemDetail;
#Temporal(TemporalType.TIMESTAMP)
Date creationDate;
#NotNull
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "patient_id")
private Patient patient;
}

Categories