Spring boot JSON return infinite nested objects - spring-boot

I have the following in my code:
CompanyEntity
#Entity
#Table(name = "company")
public class Company{
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL)
#JsonUnwrapped
private Set<User> users;
}
UserEntity
#Entity
#Table(name="user")
public class User{
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name="company_id")
private Company company;
}
CompanyController
#GetMapping("/company")
public ResponseEntity<Object> getAllCompanies(){
List<Company> allCompanies = companyService.findAll();
return ResponseEntity.ok(allCompanies);
}
problem is when i call /company in the browser i am getting the users object including the company object. something like this
[
{
"id": 1,
"name": "company",
"users": [
{
"id": 14,
"firstName": "Yamen",
"lastName": "Nassif",
"company": {
"id": 1,
"name": "company",
"users": [
{
"id": 14,
"firstName": "Yamen",
"lastName": "Nassif",
"company": {
"id": 1,
"name": "company",
"users": [
...
same goes when i getAllUsers companies and users are also exanding.
my database looks just fine.
and its endless and of course Stackoverflow error is in the console. How can i fix this ?

You have this error because of the infinite recursion.
Company has a link on User and User has a link on Company.
You have at least two options:
use #JsonManagedReference and #JsonBackReference annotation on the relation fields.
create a pair of DTOs and fill them manually with data from you entities.
e.g.
#GetMapping("/company")
public ResponseEntity<Object> getAllCompanies() {
List<Company> allCompanies = companyService.findAll();
List<CompanyDto> allCompanyDtoList = convertToCompanyDtoList(allCompanies);
return ResponseEntity.ok(allCompanyDtoList );
}
Personally, I'd prefer the second option, since returning Entities is NOT a good practice.

You can use #JsonIgnore annotation to prevent this type of behavior. This usually happens with bidirectional mapping within your entities. It is caused by infinite recursion.
#Entity
#Table(name="user")
public class User{
#ManyToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name="company_id")
#JsonIgnore
private Company company;
}

Related

FetchType.LAZY is working as FetchType.EAGER

I have a problem with hibernate, namely with lazy data fetching. I have two entities and one of them clinic is having relation #OneToMany. I setup FetchType to LAZY, but still, when I am making request to get all clinic, I still have an array in the clinic entity. The questions is - why I am still getting full #OneToMany array? Shouldn't it be null?
Here I provided my entity classes
#Getter
#Setter
#Entity(name = "clinic")
public class Clinic {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy="clinic")
private List<Doctor> doctors;
}
And doctor entity
#Getter
#Setter
#Entity(name = "doctor")
public class Doctor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
#ManyToOne(fetch = FetchType.LAZY)
#JsonIgnore
private Clinic clinic;
}
And my controller, which is JpaRepository interface
#RestController
#RequestMapping("clinics")
public class ClinicController {
private final ClinicRepository clinicRepository;
public ClinicController(ClinicRepository clinicRepository) {this.clinicRepository = clinicRepository;}
#GetMapping()
public List<Clinic> getAllClinics() {
return clinicRepository.findAll();
}
}
I also logged SQL provided by Hibernate and it looks like this
Hibernate: select clinic0_.id as id1_0_, clinic0_.name as name2_0_ from clinic clinic0_
Hibernate: select doctors0_.clinic_id as clinic_i4_1_0_, doctors0_.id as id1_1_0_, doctors0_.id as id1_1_1_, doctors0_.clinic_id as clinic_i4_1_1_, doctors0_.firstname as firstnam2_1_1_, doctors0_.lastname as lastname3_1_1_ from doctor doctors0_ where doctors0_.clinic_id=?
Hibernate: select doctors0_.clinic_id as clinic_i4_1_0_, doctors0_.id as id1_1_0_, doctors0_.id as id1_1_1_, doctors0_.clinic_id as clinic_i4_1_1_, doctors0_.firstname as firstnam2_1_1_, doctors0_.lastname as lastname3_1_1_ from doctor doctors0_ where doctors0_.clinic_id=?
As I understand, it supposed to send only one query (first select).
And of course, I also will provide an answer that I am getting right now
[
{
"id": 1,
"name": "Clinic 1",
"doctors": [
{
"id": 1,
"firstname": "Bob",
"lastname": "Fisher"
},
{
"id": 2,
"firstname": "John",
"lastname": "Cena"
}
]
},
{
"id": 2,
"name": "Clinic 2",
"doctors": [
{
"id": 3,
"firstname": "Some",
"lastname": "guy"
}
]
}
]
The association is lazily loaded because Jackson (JSON serialization library) simply serializes this proxy collection. Lazy loading is possible because Spring enables the open-session-in-view by default. If you turn that config off, you will see a LazyInitializationException.
What you want though, is to tell Jackson not to serialize the collection, by annotating it with #JsonIgnore:
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY, mappedBy="clinic")
private List<Doctor> doctors;

Spring Boot neo4j infinite loop

I have a Spring Boot App with a neo4j database and have a problem with the neo4j repository fetching nested data and creating a loop.
I have a node Person. A Person can send access requests to another Person. When I fetch a Person, I want to get the data of all the access requests TO and FROM other Persons. The model looks like this:
#Data
#Node
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
#Id
#GeneratedValue
private Long id;
private String email;
#Relationship(type = "ACCESS_REQUESTED", direction = Relationship.Direction.OUTGOING)
#JsonProperty("requested_contacts")
private List<AccessRequested> requestedContacts;
#Relationship(type = "ACCESS_REQUESTED", direction = Relationship.Direction.INCOMING)
#JsonProperty("contact_requests")
private List<AccessRequested> contactRequests;
}
#RelationshipProperties
#Data
public class AccessRequested {
#RelationshipId
#GeneratedValue
#JsonIgnore
private Long id;
#TargetNode
private Person person;
private String reason;
}
Now I am using the Neo4jRepository like this:
#Repository
public interface PersonRepository extends Neo4jRepository<Person, Long> {}
And I am calling findById(Long id)
The problem is that when I fetch a Person that has sent the request to another person, it creates an infinite loop:
{
"id": 13,
"requested_contacts": [],
"contact_requests": [
{
"person": {
"id": 5,
"requested_contacts": [
{
"person": {
"id": 13,
"requested_contacts": [],
"contact_requests": [
{
"person": {
"id": 5,
"contact_cards": [],
"requested_contacts": [
{
"person": {
"id": 13,
"requested_contacts": [],
"contact_requests": [
{
"person": {
"id": 5,
"contact_cards": [],
"requested_contacts": [
{
"person": {
"id": 13,
…….
How can I fix that? I want to fetch the full person on top level, but the nested persons should only contain id and email.

spring-data-jpa - Limit how deep objects populate

I have the following entities:
#Entity
#Table(name = "business", schema = "public")
public class Business {
// some properties
}
#Entity
#Table(name = "appuser", schema = "public")
public class AppUser implements UserDetails {
// some properties
#JsonManagedReference
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
private List<UserBusinessRole> userBusinessRoles = new ArrayList<>();
}
#Entity
#Table(name = "appuser_business_role", schema = "public")
public class UserBusinessRole {
// some properties
#ManyToOne
#JoinColumn(name = "business_id")
private Business business;
}
These work without issue when calling individually, however, I also have an entity that has business AND app user:
#Entity
#Table(name = "import_session", schema = "public")
public class ImportSession {
// some properties
#JsonIgnore
#ManyToOne()
#JoinColumn(
name = "requester_user_id",
referencedColumnName = "id"
)
private AppUser requester;
#ManyToOne
#JoinColumn(name = "business_id")
private Business business;
}
But it returns duplicate values for business like below (listed under roles and in the root object):
{
"id": 14,
...
"requesterDto": {
"id": 123,
"emailAddress": "bar#bar.com",
"userBusinessRolesDto": [
{
"id": 6,
"type": "ADMIN",
"businessDto": {
"name": "Foo Inc"
...
}
}
]
},
"businessDto": {
"name": "Foo Inc"
}
}
Is there a way to make it ONLY return certain fields, or control how 'deep' it populates, without a lot of manual fiddeling / creating separate DTOs all over? So it would look something like this for example:
{
"id": 14,
...
"requesterDto": {
"id": 123,
"emailAddress": "bar#bar.com"
},
"businessDto": {
"name": "Foo Inc"
...
}
}

Bidirectional mapping does not pick up parent

I have a bidirectional relationship between two entities. Here are the two classes, getters/setters omitted:
#Entity
#Table(name = "strategy")
public class Strategy {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#OneToMany(mappedBy = "strategy", cascade = { CascadeType.ALL })
private List<StrategyDetail> details;
}
And
public class StrategyDetail {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String content;
#ManyToOne()
private Strategy strategy;
}
I then have a repository and controller over this, but nothing worth mentionning (my repository only extends the JpaRepository and the controller has a get/post mapping).
When I try to create a strategy, I send this JSON:
{
"name": "my strat",
"details": [{
"content": "my content"
}]
}
and the response is this:
{
"id": 1,
"name": "my strat",
"details": [
{
"id": 2,
"content": "my content",
"strategy": null
}
]
}
I don't get why the strategy is null here, the mapping is fairly obvious. When I then try to get the strategy, the answer is this:
[
{
"id": 1,
"name": "my strat",
"details": []
}
]
Which I guess is working as intended (foreign key failure so the detail has not been saved?).
What should I change to make it work (meaning having access to the strategy inside the strategyDetail)? Even better I think would be having the strategy id rather than the whole strategy.
I've tried removing the bidirectionality by removing the strategy property in StrategyDetail and then it does properly save the details, but then I obviously don't have access to the strategy field inside the detail...
I've been looking for a solution on this for the past hour, looking at tutorials and what not, but I have not found a working solution.

How populate Spring Data Repositories with ManyToMany relationship

I want to populate the repository with user roles and two initial users related to those roles.
This is the JSON I want to upload:
[
{
"_class": "models.Role",
"#id": 1,
"name": "ROLE_BLOG_ADMIN",
"description": "Rol de los administradores del blog"
},
{
"_class": "models.Role",
"#id": 2,
"name": "ROLE_BLOG_CONTRIBUTOR",
"description": "Rol de los editores de artículos"
},
{
"_class": "models.User",
"username": "sergio11",
"password": "$2a$10$0eCQpFRdw8i6jJzjj/IuNuKpJYnLaO5Yp9xSJ3itcfPmQNXVhmNyu",
"email": "gfhdsgfjhdsgfjhdsgf#gmail.com",
"fullName": "Sergio Sánchez Sánchez",
"roles": [1, 2]
},
{
"_class": "models.User",
"username": "dani33",
"password": "$2a$10$0eCQpFRdw8i6jJzjj/IuNuKpJYnLaO5Yp9xSJ3itcfPmQNXVhmNyu",
"email": "danihiglesias#usal.es",
"fullName": "Daniel de la Iglesia",
"roles": [2]
}
]
I am using JsonIdentityInfo in the Roles entity:
#Entity
#Table(name = "roles")
#JsonIdentityInfo(generator=IntSequenceGenerator.class, property="#id")
public class Role implements Serializable
I have included Jackson2RepositoryPopulatorFactoryBean in the context:
#Bean(name="repositoryPopulator")
public Jackson2RepositoryPopulatorFactoryBean provideJackson2RepositoryPopulatorFactoryBean(){
Resource sourceData = new ClassPathResource("data.json");
Jackson2RepositoryPopulatorFactoryBean factory = new Jackson2RepositoryPopulatorFactoryBean();
factory.setResources(new Resource[] { sourceData });
return factory;
}
But, no role is associated with any user.
This is the association:
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name="USER_ROLES",
joinColumns=#JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="ROLE_ID", referencedColumnName="ID"))
private Set<Role> roles;
Does anyone know how to fix them?
It should only complain about a missing constructor for the Role entity, for me everything went smoothly with the following :
#Entity
#Table(name = "roles")
public class Role implements Serializable {
public Role() {
}
public Role(Integer id) {
this.id = id;
}
#Id
#JsonProperty("#id")
private Integer id;
//additional properties, getters & setters
...
}
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name="USER_ROLES",
joinColumns=#JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="ROLE_ID", referencedColumnName="ID"))
private Set<Role> roles;
//additional properties, getters & setters
...
}
And I properly get :
[
{
"id":1,
"roles":[
{
"name":"ROLE_BLOG_ADMIN",
"#id":1
},
{
"name":"ROLE_BLOG_CONTRIBUTOR",
"#id":2
}
],
"email":"sss4esob#gmail.com"
},
{
"id":2,
"roles":[
{
"name":"ROLE_BLOG_CONTRIBUTOR",
"#id":2
}
],
"email":"danihiglesias#usal.es"
}
]
Can you provide more of your entities code if this isn't working for you ? Do you encounter any exception ?

Resources