Return array of object(s) in join - java

I have this Product entity :
#Entity
public class Product implements Serializable{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id")
#JsonBackReference
private ProductCategory productCategory;
}
And Category Entity :
#Entity
public class ProductCategory implements Serializable{
#OneToMany(fetch = FetchType.LAZY, mappedBy="productCategory" ,cascade = CascadeType.ALL)
#JsonManagedReference
#OrderBy("id desc")
private Set<Product> products = new HashSet<Product>();
}
Rest API :
#Query("select p,productCategory.id,productCategory.name from Product p inner join p.productCategory productCategory")
public Page<Product> getProductsWithCategory(Pageable pageable);
This returns the following JSON :
"content": [
[
{
"id": 2,
"sku": "REF_25471",
"name": "Serrure Washington"
},
1,
"Hello 1"
],
I want to add the category object in the JSON like following :
"content": [
{
"id": 1,
"name": "Hello 1",
"products": [
{
"id": 4,
"sku": "REF_25472",
"name": "Serrure Washington",
"productCategory": [
{
"id": 1,
"name": "Hello 1"
}
]
}
]
}
]

Related

Infinite Recursion with Jackson JSON and Hibernate JPA

I want to store some data into database through OnetoMany and ManytoOne Bidirectional relationship mapping. While request for persist data in postman get infinite row in response.
Here down is my code:
Entity
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer author_id;
private String name;
private String language;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "author")
private Set<Book> book;
// getter setter
}
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "author_id")
private Author author;
// getter setter
}
Service
#Override
public Author insertAuthor(Author author) {
Set<Book> bookList = new HashSet<>();
Book book = new Book();
book.setTitle(book.getTitle());
bookList.add(book);
book.setAuthor(author);
author.setBook(bookList);
return authorRepo.save(author);
}
Controller
#RestController
public class PojoController {
#Autowired
private PojoService pojoService;
#RequestMapping(value="/book", method = RequestMethod.POST)
public Author addBookCourse(#RequestBody Author author) {
return this.pojoService.insertAuthor(author);
}
}
Request
{
"language": "english",
"name": "Banjamin franklin",
"book": [{
"title": "Theory Of Everything"
},
{
"title": "A Brief Story Of Time"
}]
}
Output
{
"author_id": 1,
"name": "Banjamin franklin",
"language": "english",
"book": [
{
"id": 1,
"title": null,
"author": {
"author_id": 1,
"name": "Banjamin franklin",
"language": "english",
"book": [
{
"id": 1,
"title": null,
"author": {
"author_id": 1,
"name": "Banjamin franklin",
"language": "english",
"book": [
{
"id": 1,
"title": null,
"author": {
"author_id": 1,
"name": "Banjamin franklin",
"language": "english",
"book": [
{
"id": 1,
"title": null,
more 7460 line
.......
.......
.......
{
"timestamp": "2021-11-30T10:25:03.957+00:00",
"status": 200,
"error": "OK",
"trace": "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: com.rest.RestApiPojo.Entity.Author[\"book\"]->org.hibernate.collection.internal.PersistentSet[0]->com.rest.RestApiPojo.Entity.Book[\"author\"]->com.rest.RestApiPojo.Entity.Author[\"book\"]->org.hibernate.collection.internal.PersistentSet[0]->com.rest.RestApiPojo.Entity.Book[\"author\"]
}
You need to use #JsonManagedReference and #JsonBackReference to allow Jackson to better handle the relationship between Author and Book:
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer author_id;
private String name;
private String language;
#JsonManagedReference
#OneToMany(cascade = CascadeType.ALL, mappedBy = "author")
private Set<Book> book;
// getter setter
}
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
#JsonBackReference
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "author_id")
private Author author;
// getter setter
}
You have other options (e.g. using #JsonIdentityInfo) to handle this infinite recursion issue, but this is the most common solution. You can check all other possible approaches at the following online resource https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.
Additionally, in your Service you are creating a brand new Book and setting its title with book.setTitle(book.getTitle());, which basically does nothing. In fact you don't even need to do most of the stuff you are doing there because Book instances are already in Author, you just need to set Author on each Book instance as follows:
#Override
public Author insertAuthor(Author author) {
for (Book book : author.getBook()) {
book.setAuthor(author);
}
return authorRepo.save(author);
}
Finally, consider changing the book property in Author to books since it contains multiple books (you will need to adjust your code afterwards).

How to correctly map 4 tables in spring jpa

I have 4 tables
Area: it is a catalog where there are all the departments of an organization (parent)
SubAreas: It is a catalog where there are all the subAreas that belong to before catalog (Area), that is (child)
personal: Catalog where all the employees of the organization exist
personal Area: It is a table where there are all the relationships between the tables previously described
The business logic is simple, an employee can belong to one of several areas and only one subarea for each area to which they belong.
Here my relationships
These are my entities
Personal
#Entity
#Table(name = "personal", catalog = "almacen", schema = "")
public class Personal {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Long id;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 30)
#Column(name = "curp")
private String curp;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 200)
#Column(name = "nombre")
private String nombre;
#JsonIgnore
#OneToMany(mappedBy = "personal")
private Set<PersonalArea> registrations = new HashSet<>();
}
PersonalArea
#Getter
#Setter
#Entity
#Table(name = "personal_area", catalog = "almacen", schema = "")
#XmlRootElement
public class PersonalArea {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#NotNull
private Long id;
#ManyToOne
#JoinColumn(name = "personal_id")
private Personal personal;
#ManyToOne
#JoinColumn(name = "area_id")
private Area area;
#ManyToOne
#JoinColumn(name = "subarea_id")
private subArea subArea;
private Date fechaCreacion;
}
Area
#Getter
#Setter
#Entity
#Table(name = "area", catalog = "almacen", schema = "")
#XmlRootElement
public class Area {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#NotNull
private Long id;
private String nombreArea;
#JsonIgnore
#OneToMany(mappedBy = "area")
private Set<PersonalArea> registrations = new HashSet<>();
}
SubArea
#Getter
#Setter
#Entity
#Table(name = "sub_area", catalog = "almacen", schema = "")
public class subArea {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#NotNull
private Long id;
private Long idArea;
private String nombre;
#JsonIgnore
#OneToMany(mappedBy = "personal")
private Set<PersonalArea> registrations = new HashSet<>();
}
Actually I get something like this
As you can see, the json has two times the same personal who belongs to two areas and one subarea for each area... It's ok, but I don't want this json
{
"items": [{
"id": 1,
"personal": {
"id": 1,
"curp": "AEMM680119HHGRRR03",
"nombre": "MARIO RUBIZEL",
"primerApellido": "ARTEAGA",
"segundoApellido": "MORALES",
"telefono": "7711263949",
"fechaCreacion": null,
"fechaModificacion": null
},
"area": {
"id": 2,
"nombreArea": "PERSONAL ADMINISTRATIVO"
},
"subArea": {
"id": 1,
"idArea": 2,
"nombre": "CONTROL ESCOLAR"
},
"fechaCreacion": "2021-06-24T16:26:49.000+00:00"
},
{
"id": 2,
"personal": {
"id": 1,
"curp": "AEMM680119HHGRRR03",
"nombre": "MARIO RUBIZEL",
"primerApellido": "ARTEAGA",
"segundoApellido": "MORALES",
"telefono": "7711263949",
"fechaCreacion": null,
"fechaModificacion": null
},
"area": {
"id": 5,
"nombreArea": "PERSONAL EXTERNO"
},
"subArea": {
"id": 2,
"idArea": 5,
"nombre": "FOTOCOPIADO"
},
"fechaCreacion": "2021-06-24T16:26:49.000+00:00"
}
]
}
I need my json something like this: Show just once personal and areas y subareas as child
{
"items": [{
"id": 1,
"personal": {
"id": 1,
"curp": "AEMM680119HHGRRR03",
"nombre": "MARIO RUBIZEL",
"primerApellido": "ARTEAGA",
"segundoApellido": "MORALES",
"telefono": "7711263949",
"fechaCreacion": null,
"fechaModificacion": null
},
"area": [{
"id": 1,
"nombreArea": "DIRECTOR",
"subArea": {
"id": 1,
"idArea": 2,
"nombre": "CONTROL ESCOLAR"
}
},
{
"id": 5,
"nombreArea": "PERSONAL EXTERNO",
"subArea": {
"id": 2,
"idArea": 5,
"nombre": "FOTOCOPIADO"
}
}
],
"fechaCreacion": "2021-06-24T16:26:49.000+00:00"
}
]
}
thanks in advance

In springboot, I can't get the JSON for manytomany with attribute on my REST Controller

I have a SpringBoot REST API where one or multiple User are part of one or multiple Team (Equipe), but they have a Role for each Team.
When I request my UserRepository.findAll(), I would like to get all my users and their teams and roles.
But when I request my TeamRepository.findAll(), I would like to get all my teams and their users and their roles.
But I don't know how to get a bidirectionnal request. I tried with #jsonmanagedreference and #jsonbackreference but it only allows me to get the teams from the user, and not the opposite.
Currently I have
[
{
"id": 1,
"prenom": "TestPrenom1",
"nom": "TestNom1",
"roleUserEquipe": []
}
{
"id": 2,
"prenom": "TestPrenom2",
"nom": "TestNom2",
"roleUserEquipe": []
}
]
But what I want is
[
{
"id": 1,
"prenom": "TestPrenom1",
"nom": "TestNom1",
"roleUserEquipe": [
{
"role": "Role1",
"team": {
"id": 1,
"nom": "Team1"
}
}
]
}
{
"id": 2,
"prenom": "TestPrenom2",
"nom": "TestNom2",
"roleUserEquipe": [
{
"role": "Role2",
"team": {
"nom": "Team1"
}
}
]
}
]
And when I request my teams I have
[
{
"id": 1,
"nom": "Team1",
"roleUserEquipe": [
{}
]
},
{
"id": 2,
"nom": "Team2",
"roleUserEquipe": [
{}
]
}
]
But I would like
[
{
"id": 1,
"nom": "Team1",
"roleUserEquipe": [
{
"role": "Role1",
"user": {
"id": 1,
"prenom": "TestPrenom1",
"nom": "TestNom1",
}
},
{
"role": "Role2",
"user": {
"id": 2,
"prenom": "TestPrenom2",
"nom": "TestNom2",
}
}
]
},
{
"id": 2,
"nom": "Team2",
"roleUserEquipe": []
}
]
My User.java
#Entity
#JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id")
public class User implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String prenom;
private String nom;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<RoleUserEquipe> roleUserEquipe = new HashSet<>();
//GETTERS AND SETTERS AND CONSTRUCTORS
}
My Equipe.java (Team)
#Entity
#JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Equipe implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String nom;
#OneToMany(mappedBy = "equipe", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<RoleUserEquipe> roleUserEquipe = new HashSet<>();
//GETTERS AND SETTERS AND CONSTRUCTORS
}
My RoleUserEquipe.java
#Entity
public class RoleUserEquipe implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "equipe_id")
private Equipe equipe;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
private String role;
//GETTERS AND SETTERS AND CONSTRUCTORS
}
EDIT :
When debugging, the subobjects are OK: they contain the values that I need.
Only when returning the JSON Response, they lose their values
I am not sure if this would work, but try removing the new keywords in:
#OneToMany(mappedBy = "equipe", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<RoleUserEquipe> roleUserEquipe = new HashSet<>();
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<RoleUserEquipe> roleUserEquipe = new HashSet<>();
to
#OneToMany(mappedBy = "equipe", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<RoleUserEquipe> roleUserEquipe;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<RoleUserEquipe> roleUserEquipe;
You didn't show us, how you convert your domain objects into resource/DTO objects.
Are you using the Spring ConversionService framework?
If so, show us the converter you wrote. Maybe your converter doesn't take the roles into account?

How can i get an object with checks on nested object in Spring boot?

I am unable to to get what i intend using a JpaRepository. I'll try to explain what I want using the following code:
Repository
#Repository
public interface CompanyRepository extends JpaRepository<Company, Long> {
Optional<Company> findByIdAndBranches_parent_idIsNull(Long id);
}
Service
#Service
public class BillService {
#Autowired
private CompanyRepository companyRepository;
#Autowired
private BranchRepository branchRepository;
public Company getCompanyById(Long id)
{
Optional<Company> company = companyRepository.findByIdAndBranches_parent_idIsNull(id);
return company.get();
}
Company Entity class
public class Company
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String address;
#OneToMany(mappedBy = "company")
private List<Branch> branches;
#OneToMany(mappedBy = "company")
private List<User> users;
}
Branch Entity class
#Entity
public class Branch
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String address;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id")
private Company company;
#JsonIgnore
#ManyToOne(cascade = CascadeType.ALL )
#JoinColumn(name = "parent_id")
#NotFound(action = NotFoundAction.IGNORE)
private Branch parent;
#OneToMany(mappedBy = "parent")
#NotFound(action = NotFoundAction.IGNORE)
private List<Branch> subBranches;
#OneToMany(mappedBy = "branch")
private List<User> users;
}
Now what i want to get is company with branches having parent id NULL but am getting all branches irrespective of parent id
This is what i want as result
{
"id": 1,
"name": "Lakshya",
"address": "Bahadurgarh",
"branches": [
{
"id": 1,
"name": "Lakshya Branch1",
"address": "Bahadurgarh1",
"subBranches": [
{
"id": 3,
"name": "Lakshya Branch1_3",
"address": "Bahadurgarh1_3",
"subBranches": [],
"users": []
}
],
"users": [
{
"id": 3,
"name": "User3_Company1_Branch1",
"address": "Bgz"
}
]
}
],
"users": [
{
"id": 1,
"name": "User_Company1",
"address": "Bgz"
},
{
"id": 2,
"name": "User_Company1_Branch1",
"address": "Bgz"
},
{
"id": 3,
"name": "User3_Company1_Branch1",
"address": "Bgz"
}
]
}
But this is what am getting
{
"id": 1,
"name": "Lakshya",
"address": "Bahadurgarh",
"branches": [
{
"id": 1,
"name": "Lakshya Branch1",
"address": "Bahadurgarh1",
"subBranches": [
{
"id": 3,
"name": "Lakshya Branch1_3",
"address": "Bahadurgarh1_3",
"subBranches": [],
"users": []
}
],
"users": [
{
"id": 3,
"name": "User3_Company1_Branch1",
"address": "Bgz"
}
]
},
{
"id": 3,
"name": "Lakshya Branch1_3",
"address": "Bahadurgarh1_3",
"subBranches": [],
"users": []
}
],
"users": [
{
"id": 1,
"name": "User_Company1",
"address": "Bgz"
},
{
"id": 2,
"name": "User_Company1_Branch1",
"address": "Bgz"
},
{
"id": 3,
"name": "User3_Company1_Branch1",
"address": "Bgz"
}
]
}
If you need to filter out associated entities, you can use #Where. It's not a part of JPA specification, but JPA implementation by Hibernate provides this annotation. So you can specify any additional conditions:
public class Company
{
...
#OneToMany(mappedBy = "company")
#Where(clause = "parent_id is null")
private List<Branch> branches;
#OneToMany(mappedBy = "company")
private List<User> users;
}
Then you have to change the repository's method name, since you don't need extra condition anymore:
#Repository
public interface CompanyRepository extends JpaRepository<Company, Long> {
Optional<Company> findById(Long id);
}
Using this approach you always get companies with only branches that don't have parent. If you need to get all branches of a company (whether they have parent or not) you can create and use repository for Branch entity class.

Fasterxml - How to exclude an object from json file?

I have mapped entities which I send in JSON format to the service. Here is my entities
#Entity
#Table(name = "company")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Company implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column
private String name;
#OneToMany(mappedBy = "company")
#Cascade(value = CascadeType.ALL)
private Collection<Employee> employees;
My Employee class
#Entity
#Table(name = "employee")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Integer id;
#Column
String name;
#ManyToOne()
#Cascade(value = org.hibernate.annotations.CascadeType.ALL)
#JoinColumn(name = "company_id", referencedColumnName = "id")
private Company company;
But I'm getting not appropriate json format.
{
"id": 1,
"name": "Tim",
"company": {
"id": 1,
"name": "Microsoft",
"employees": [1, {
"id": 5,
"name": "Jack",
"company": 1
}, {
"id": 6,
"name": "Jack",
"company": 1
}, {
"id": 7,
"name": "Jack",
"company": 1
}, {
"id": 8,
"name": "Tommy",
"company": 1
}]
}
}
But like I said I don't need "employees" object in "company". How to exclude it in my JSON file?
You can use the Jackson's bi-directional references to exclude "employees" object from "company". Here is an example based on your code that demonstrate the approach:
public class JacksonReferences {
#JsonIdentityInfo(generator = ObjectIdGenerators.None.class, property = "id")
static public class Company {
public Integer id;
public String name;
#JsonManagedReference
public Collection<Employee> employees;
public Company(Integer id, String name, Collection<Employee> employees) {
this.id = id;
this.name = name;
this.employees = employees;
}
}
#JsonIdentityInfo(generator = ObjectIdGenerators.None.class, property = "id")
static public class Employee {
public Integer id;
public String name;
#JsonBackReference
public Company company;
public Employee(Integer id, String name, Company company) {
this.id = id;
this.name = name;
this.company = company;
}
}
public static void main(String[] args) throws IOException {
Company company1 = new Company(1, "Microsoft", new ArrayList<Employee>());
Company company2 = new Company(2, "Google", new ArrayList<Employee>());
Employee employee1 = new Employee(1, "John", company1);
company1.employees.add(employee1);
Employee employee2 = new Employee(2, "Tim", company1);
company1.employees.add(employee2);
Employee employee3 = new Employee(3, "Bob", company2);
company2.employees.add(employee3);
ObjectMapper mapper = new ObjectMapper();
System.out.println("JSON for company #1:");
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(company1));
System.out.println("JSON for employee #1:");
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(employee1));
System.out.println("JSON for company #2:");
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(company2));
}
}
The output is:
JSON for company #1:
{
"id" : 1,
"name" : "Microsoft",
"employees" : [ {
"id" : 1,
"name" : "John"
}, {
"id" : 2,
"name" : "Tim"
} ]
}
JSON for employee #1:
{
"id" : 1,
"name" : "John"
}
JSON for company #2:
{
"id" : 2,
"name" : "Google",
"employees" : [ {
"id" : 3,
"name" : "Bob"
} ]
}

Categories

Resources