REST API Infinite loop - spring-boot

My API shows me infinite loop for adress field
When I insert #JsonIgnore, #JsonManagedReference or #JsonBackReference
I can clearly see one result as it should be, but than i don't have nested address fields.
What should I do to have also that address fields but one result?
These are my main entities:
1.Property
package com.realestate.petfriendly.entity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import lombok.Data;
#Entity
#Data
#Table(name = "property")
public class Property {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_property")
private int id_property;
#Column(name = "title")
private String title;
#Column(name = "type")
private String type;
#Column(name = "room")
private String room;
#Column(name = "price")
private double price;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "address_id_address")
// #JsonBackReference
private Address address;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id_user")
// #JsonBackReference
private User user;
}
User
package com.realestate.petfriendly.entity;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
#Entity
#Getter
#Setter
#Table(name = "user")
class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_user")
private int id_user;
#Column(name = "username")
private String username;
#Column(name = "name")
private String name;
#Column(name = "lastname")
private String lastname;
#Column(name = "phone")
private String phone;
#Column(name = "notes")
private String notes;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_address_id_user_address")
// #JsonManagedReference
private UserAddress userAddress;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
// #JsonManagedReference
private List<Property> property = new ArrayList<>();
}
Address
package com.realestate.petfriendly.entity;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
#Entity
#Getter
#Setter
#Table(name="address")
class Address{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_address")
private int id_address;
#Column(name = "city")
private String city;
#Column(name = "municipality")
private String municipality;
#Column(name = "place")
private String place;
#Column(name = "street")
private String street;
#Column(name = "house_number")
private double house_number;
#OneToOne(mappedBy = "address")
// #JsonManagedReference
private Property property;
}

You actually have the solution to your problem in your code, but the key annotations are commented-out and in the wrong places (according to your requirements). One of the ways to tackle this is by using #JsonManagedReference and #JsonBackReference as follows:
#Entity
#Data
#Table(name = "property")
public class Property {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_property")
private int id_property;
#Column(name = "title")
private String title;
#Column(name = "type")
private String type;
#Column(name = "room")
private String room;
#Column(name = "price")
private double price;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "address_id_address")
#JsonManagedReference
private Address address;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id_user")
#JsonBackReference
private User user;
}
#Entity
#Getter
#Setter
#Table(name = "user")
class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_user")
private int id_user;
#Column(name = "username")
private String username;
#Column(name = "name")
private String name;
#Column(name = "lastname")
private String lastname;
#Column(name = "phone")
private String phone;
#Column(name = "notes")
private String notes;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_address_id_user_address")
private UserAddress userAddress;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
#JsonManagedReference
private List<Property> property = new ArrayList<>();
}
#Entity
#Getter
#Setter
#Table(name="address")
class Address{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_address")
private int id_address;
#Column(name = "city")
private String city;
#Column(name = "municipality")
private String municipality;
#Column(name = "place")
private String place;
#Column(name = "street")
private String street;
#Column(name = "house_number")
private double house_number;
#OneToOne(mappedBy = "address")
#JsonBackReference
private Property property;
}
Keep in mind the following:
#JsonManagedReference is the forward part of the relationship: the one that gets serialized normally.
#JsonBackReference is the back part of the relationship: it will be omitted from serialization.
If you want to have a reference to the back part of the relationship, you can use #JsonIdentityInfo as follows:
#Entity
#Data
#Table(name = "property")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id_property")
public class Property {
(...)
}
#Entity
#Getter
#Setter
#Table(name = "user")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id_user")
class User {
(...)
}
#Entity
#Getter
#Setter
#Table(name="address")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id_address")
class Address{
(...)
}
You can read more about these and other techniques in the following online resource: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.

You have circular dependency between Property and Address class. In order to block infinite JSON serialization loop you can add #JsonIgnore annotation on one side of related properties

Related

Config ModelMapper giving me an error when converting From a Set<Author> to Set<Integer>

I am trying to convert a book class (livro) to a bookDto. I was tryied doing that creating a bean config of model mapper but it is not working.
I have on the book class a Set of Authors (autores) and a Set of Genre (generos) and all classes have Integers ids. I want the BookDTO to have only a set of the ids. I guess the problem is on the model mapper config i did
package br.com.newgo.biblioteca.config;
import br.com.newgo.biblioteca.data.dto.output.LivroCriadoDto;
import br.com.newgo.biblioteca.data.entity.Autor;
import br.com.newgo.biblioteca.data.entity.Genero;
import br.com.newgo.biblioteca.data.entity.Livro;
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Set;
import java.util.stream.Collectors;
#Configuration
public class MapperConfig {
#Bean
public ModelMapper modelMapper(){
ModelMapper modelMapper = new ModelMapper();
Converter<Set<Autor>, Set<Integer>> autoresParaAutoresId = context -> {
if (context.getSource() == null) {
return null;
}
return context.getSource().stream()
.map(Autor::getId).collect(Collectors.toSet());
};
Converter<Set<Genero>, Set<Integer>> generosParaGenerosId = context -> {
if (context.getSource() == null){
return null;
}
return context.getSource().stream()
.map(Genero::getId).collect(Collectors.toSet());
};
modelMapper.typeMap(Livro.class , LivroCriadoDto.class).addMappings(
src -> {
src.using(autoresParaAutoresId).map(Livro::getAutores, LivroCriadoDto::setAutores);
src.using(generosParaGenerosId).map(Livro::getGeneros, LivroCriadoDto::setGeneros);
});
return modelMapper;
}
}
Here is my book class:
package br.com.newgo.biblioteca.data.entity;
import jakarta.persistence.*;
import lombok.*;
import java.util.HashSet;
import java.util.Set;
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity()
#Table(name = "livros")
public class Livro {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(nullable = false)
private String nome;
#Column(nullable = false, length = 13)
private String isbn;
//de 10 ou 13?
#Column(nullable = false)
private Float valor;
#Column(nullable = false)
private Integer paginas;
#Column(nullable = false)
private Float altura;
#Column(nullable = false)
private Float largura;
#Column(nullable = false)
private Float profundidade;
#ManyToMany
#JoinTable(name = "livro_genero")
private Set<Genero> generos = new HashSet<>();
#ManyToMany
#JoinTable(name = "livro_autor",
joinColumns = #JoinColumn(name = "livro_id"),
inverseJoinColumns = #JoinColumn(name = "autor_id"))
private Set<Autor> autores = new HashSet<>();
}
and here is the dto i am trying to convert to :
package br.com.newgo.biblioteca.data.dto.output;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import java.util.HashSet;
import java.util.Set;
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class LivroCriadoDto {
#NotNull
private Integer id;
#NotBlank
private String nome;
#NotBlank
#Length(min = 10, max = 13)
private String isbn;
#NotNull
#Positive
private Float valor;
#NotNull
#Positive
private Integer paginas;
#NotNull
#Positive
private Float altura;
#NotNull
#Positive
private Float largura;
#NotNull
#Positive
private Float profundidade;
#NotNull
#NotEmpty
private Set<Integer> generos = new HashSet<>();
#NotNull
#NotEmpty
private Set<Integer> autores = new HashSet<>();
}
I am trying doing this
modelMapper.map(livro, LivroCriadoDto.class);
and it is returning me
Converter br.com.newgo.biblioteca.config.MapperConfig$$Lambda$1243/0x000000080140e3d8#16a95f37 failed to convert org.hibernate.collection.spi.PersistentSet to java.util.Set.
1) Converter br.com.newgo.biblioteca.config.MapperConfig$$Lambda$1243/0x000000080140e3d8#16a95f37 failed to convert org.hibernate.collection.spi.PersistentSet to java.util.Set.
Caused by: java.lang.NullPointerException
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1845)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at br.com.newgo.biblioteca.config.MapperConfig.lambda$modelMapper$0(MapperConfig.java:27)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:306)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:243)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:187)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:151)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:105)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:71)
at org.modelmapper.ModelMapper.mapInternal(ModelMapper.java:589)
at org.modelmapper.ModelMapper.map(ModelMapper.java:422)
at br.com.newgo.biblioteca.domain.services.LivroService.cadastrar(LivroService.java:51)
at br.com.newgo.biblioteca.presentation.controllers.LivroController.cadastrar(LivroController.java:21)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1010)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:913)
The problem was that jpa was creating a Autor and Genero null because it was not supposed to be null and passing it no Livro

Spring boot entity has a list of another entity

I have a problem with the following code:
#Entity
#Table(name = "app_user")
#NoArgsConstructor
#AllArgsConstructor
#Data
#Builder
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
private String username;
#Column(unique = true)
private String email;
private String name;
private String password;
#Enumerated(EnumType.STRING)
private Role role;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
#JoinTable(name = "user_shoes",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "shoes_id"))
private List<Shoe> shoes = new ArrayList<>();
#Builder.Default
private final Instant created = new Date().toInstant();
}
#Entity
#Table(name = "shoes")
#NoArgsConstructor
#AllArgsConstructor
#Data
#Builder
public class Shoe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private User user;
private String brand;
private String type;
private String size;
private String color;
private String image_url;
private String sex;
#Builder.Default
private Instant created = new Date().toInstant();
}
Inside User entity I want to make a list that contains Shoe entities, but I always get an error.
Table 'webshop.shoes' doesn't exist
Anybody know how to fix this problem?
It is required to my home project of a shoe webshop.
Thanks.
I had pretty similar codes with yours.
Please see mine and hope you solve it as I did.
import com.fasterxml.jackson.annotation.JsonBackReference;
import lombok.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
#Entity
#Getter
#Table(name = "orders")
#ToString(exclude = "user")
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#EntityListeners(AuditingEntityListener.class)
#SequenceGenerator(
name = "order_seq_generator"
, sequenceName = "orders_order_id_seq"
, initialValue = 1
, allocationSize = 1
)
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE
, generator = "order_seq_generator")
#Column(name = "order_id")
private Long orderId;
#Column(name = "order_number")
private String orderNumber;
#Column(name = "salad_date")
private LocalDate saladDate;
#Column(name = "order_date")
#CreatedDate
private LocalDateTime orderDate;
#ManyToOne
#JsonBackReference
#JoinColumn(name = "user_id")
private User user;
#Column(name = "cancel_yn", columnDefinition = "boolean default false")
private boolean cancelYn;
#Builder
public Order(String orderNumber, User user, LocalDate saladDate, boolean cancelYn) {
this.orderNumber = orderNumber;
this.user = user;
this.saladDate = saladDate;
this.cancelYn = cancelYn;
}
public void updateOrder(String orderNumber, boolean cancelYn) {
this.orderNumber = orderNumber;
this.cancelYn = cancelYn;
}
}
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
#Entity
#Getter
#ToString
#Table(name = "users")
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#EntityListeners(AuditingEntityListener.class)
#SequenceGenerator(
name= "users_seq_generator"
, sequenceName = "users_user_id_seq"
, initialValue = 1
, allocationSize = 1
)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE
, generator = "users_seq_generator")
#Column(name = "user_id")
private Long userId;
#Column(name = "password")
private String password;
#Column(name = "user_email")
private String userEmail;
#JsonIgnore
#JsonManagedReference // Avoid infinite recursion
#OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST)
private List<Order> orders = new ArrayList<>();
#Column(name = "user_sabun")
private String userSabun;
#Column(name = "user_name")
private String userName;
#Column(name = "user_phone")
private String userPhone;
#Column(name = "send_email_yn")
private boolean sendEmailYn;
#Column(name = "join_date")
#CreatedDate
private LocalDateTime joinDate;
#Builder
public User(Long userId, String userEmail, String password, String userSabun, String userName, String userPhone, boolean sendEmailYn, LocalDateTime joinDate) {
this.userId = userId;
this.userEmail = userEmail;
this.password = password;
this.userSabun = userSabun;
this.userName = userName;
this.userPhone = userPhone;
this.sendEmailYn = sendEmailYn;
this.joinDate = joinDate;
}
public void userUpdate(String userEmail, String userSabun, String userName, String userPhone, boolean sendEmailYn) {
this.userEmail = userEmail;
this.userSabun = userSabun;
this.userName = userName;
this.userPhone = userPhone;
this.sendEmailYn = sendEmailYn;
}
}

(Do not display relationship values)

I have two entity with name of the article and article Category.
and they have one-to-many relationships.
I use #JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,property = "id")
but I cant see data of article category(category_id) in spring data rest.
ArticleCategory.class
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory", fetch = FetchType.LAZY)
private Set<Article> articles = new HashSet<>();
}
Article.class
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable {
public Article() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
#Column(name = "title")
private String title;
#Column(name = "image_url")
private String image_url;
#Column(name = "short_description")
private String short_description;
#Column(name = "text")
private String text;
#Column(name = "keywords", nullable = true)
private String keywords;
#Column(name = "visit", nullable = false)
private int visit;
#Column(name = "code", nullable = false)
private UUID code;
#Column(name = "date_created")
#CreationTimestamp
private Date dateCreated;
#Column(name = "date_updated", nullable = false)
#UpdateTimestamp
private Date dateUpdated;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
public Article(String title, String image_url, String short_description, String text, String keywords, int visit, UUID code) {
this.title = title;
this.image_url = image_url;
this.short_description = short_description;
this.text = text;
this.keywords = keywords;
this.visit = visit;
this.code = code;
}
}
Article Repository
#CrossOrigin("http://localhost:4200")
#RepositoryRestResource(collectionResourceRel = "article", path = "article")
public interface ArticleRepository extends JpaRepository<Article,Long> {
Article findByCode(UUID uuid);
}
And this is output of spring data rest
enter image description here
That is exactly because you used #JsonManagedReference and #JsonBackReference. Keep in mind the following when using them:
#JsonManagedReference is the forward part of the relationship and is the one that gets serialized normally.
#JsonBackReference is the back part of the relationship and it will be omitted from serialization.
The serialized Article object does not contain a reference to the ArticleCategory object.
If you want to have any ArticleCategory data when serializing Article you can either use #JsonIdentityInfo so that one of the properties is serialized (in this case I've chosen id for both):
#Entity
#Table(name = "article")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Article implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private ArticleCategory articleCategory;
}
#Entity
#Table(name = "article_category")
#Getter
#Setter
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If you are only interested in categoryId another possibility would be to use #JsonIgnore on private Set<Article> articles property so that it is not serialized:
#Entity
#Table(name = "article_category")
#Getter
#Setter
public class ArticleCategory implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "category_name")
private String categoryName;
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL,mappedBy = "articleCategory" ,fetch = FetchType.LAZY)
private Set<Article> articles=new HashSet<>();
}
If none of those suits your needs you might need to implement your own custom serializer. You can read more about all those options at https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.
I solved the problem using the controller
And that's why #JsonManageRefrence and #JsonBackRefrence do not work
I replaced the lazy load with the eager load in both entity
#ManyToOne(fetch = FetchType.Eager)
#JoinColumn(name = "user_id")
#JsonManageRefrence
private User user;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "articleCategory",
fetch = FetchType.Eager)
#JsonBackRefrence
private Set<Article> articles = new HashSet<>();
and then add a controller
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
#RequestMapping("/getAllArticle")
public class MyController {
private ArticleRepository articleRepository;
// you must do constructor injection
#GetMapping("/getAllArticle")
public List<Article> allArticle()
{
return articleRepository.findAll();
}
}

How to join 3 tables into one table with JPA?

I am creating an API where I have 3 tables called User, Book and Status. And I want to create a combined table User_Book_Status. I am not sure how to implement this in JPA. I think all tables have M:N relationship with each other. How should I join these 3 tables?
Here is my design for the database.
User.java
import javax.persistence.*;
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
#Column(nullable = false)
private String username;
#Column(nullable = false)
private String password;
public User() {}
//Getters and setters omitted.
}
Book.java
import javax.persistence.*;
#Entity
#Table(name = "books")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
#Column(nullable = false)
private String title;
#Column(nullable = false)
private String author;
#Column(nullable = false)
private int pages;
public Book() {}
//Getters and setters omitted.
}
Status.java
import javax.persistence.*;
#Entity
public class Status {
public enum ReadingStatus {READING,
FINISHED,
ONHOLD}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Enumerated(EnumType.STRING)
private ReadingStatus status;
}
here is an example of how to implement your solution already working:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name = "user_book_status")
public class UserBookStatus {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#ManyToOne
#JoinColumn(name = "book_id")
private Book book;
#ManyToOne
#JoinColumn(name = "status_id")
private Status status;
}
#Table to specify the name of the Table, so you can change the name of the entity freely.
#ManyToOne means that you can have many records of UserBookStatus for a single user, or book or status.
#JoinColumn use to specify the name of the column in your entity that maps to the #Id from the entity (User,Book,Status) you are referencing.
It seems to me that you don't really need an entity for Status. I would model it like this:
#Entity
public class Loan {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne
private Book book;
#ManyToOne
private User user;
#Enumerated(EnumType.STRING)
private ReadingStatus status;
//...
}
Also, you could make the relationships bidirectional, so that both User and Bookhas a list of Loans.

Error creating table with Spring Boot and Postgres

I am trying to create an application using Spring and Postgres. Currently I have 3 Entities: User, Role and UserRole.
User
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import javax.persistence.*;
#Data
#Entity
#Builder
#AllArgsConstructor
public class User {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "user_id", nullable = false)
private Long id;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "name", nullable = false)
private String firstName;
#Column(name = "lastName", nullable = true)
private String lastName;
#Column(name = "gender", nullable = false)
private char gender;
#Column(name = "email", nullable = true)
private String email;
public User() {}
public User(String username, String password, String firstName, String lastName, char gender, String email) {
this.username = username;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.gender = gender;
this.email = email;
}
}
Role
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import javax.persistence.*;
#Data
#Entity
#Builder
#AllArgsConstructor
public class Role {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "role_id", nullable = false)
private Long id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "description", nullable = false)
private String description;
public Role () {}
public Role (String name, String description) {
this.name = name;
this.description = description;
}
}
UserRole
import lombok.AllArgsConstructor;
import lombok.Data;
import javax.persistence.*;
#Data
#Entity
#AllArgsConstructor
public class UserRole {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "user_role_id")
private Long id;
#OneToOne
#JoinColumn(name = "user_id")
private User user;
#OneToOne
#JoinColumn(name = "role_id")
private Role role;
public UserRole () {}
public UserRole (User user, Role role) {
this.user = user;
this.role = role;
}
}
When running the application, Role and UserRole tables are created in the database correctly, but its not the case with User table. I will appreciate your help :).
user is a reserved keyword in postgresql because of which it is not allowing to create the table with this name. Try with another name.

Resources