最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

spring boot - Springboot + JPA + Java recursion problem - Stack Overflow

programmeradmin4浏览0评论

I'm building a Springboot-based application, but I'm running into a recursion issue that I can't resolve.

It mainly affects to Usuarios and Viviendas.

package com.antoniounidad.security.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.ToString;
import .hibernate.annotations.Fetch;
import .hibernate.annotations.FetchMode;
import .springframework.security.core.GrantedAuthority;
import .springframework.security.core.authority.SimpleGrantedAuthority;
import .springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

@Entity
@Table(name = "usuarios")
@Data
public class Usuario implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String username;

    @Column(nullable = false)
    private String nombre;

    @Column(nullable = false)
    private String apellido;

    @Column(nullable = false)
    @NotBlank
    @Size(min = 6,max =20)
    private String password;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private boolean enabled;

    // Un usuario puede estar en varias comunidades y una comunidad tiene varios usuarios
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
            name = "usuario_comunidad",
            joinColumns = @JoinColumn(name = "usuario_id"),
            inverseJoinColumns = @JoinColumn(name = "comunidad_id")
    )
    private Set<Comunidad> comunidades;

    // Relación ManyToMany con Vivienda
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
            name = "usuario_vivienda",
            joinColumns = @JoinColumn(name = "usuario_id"),
            inverseJoinColumns = @JoinColumn(name = "vivienda_id")
    )
    @JsonIgnore
    private Set<Vivienda> viviendas;


    // Relación ManyToMany con Roles
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
            name = "usuario_roles",
            joinColumns = @JoinColumn(name = "usuario_id"),
            inverseJoinColumns = @JoinColumn(name = "rol_id")
    )
    @Fetch(FetchMode.JOIN)
    private Set<Rol> roles;

    // Implementación de UserDetails para Spring Security
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles != null ? roles.stream()
                .map(rol -> new SimpleGrantedAuthority(rol.getNombre()))
                .collect(Collectors.toSet()) : Set.of();
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return this.enabled;
    }
}

viviendas.java

package com.antoniounidad.security.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import lombok.*;

import java.util.List;
import java.util.Set;
import java.util.Optional;

@Entity
@Data
@Table(name = "vivienda")
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Vivienda {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String bloque;
    private String piso;
    private String puerta;
    private Boolean alquilado;
    private Boolean alCorrientePago;

    // Relación Muchos a Uno con Comunidad (Cada vivienda pertenece a una comunidad)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "comunidad_id")
    @JsonIgnoreProperties({"viviendas"}) // Evita la carga recursiva infinita
    @ToString.Exclude // Evita la recursión infinita
    private Comunidad comunidad;

    /**
     * -- GETTER --
     *  Método para obtener los propietarios, forzando la carga de datos.
     */
    // Relación Muchos a Muchos con Usuario (Cada vivienda puede tener varios propietarios)
    @ManyToMany(mappedBy = "viviendas", fetch = FetchType.LAZY)
    private Set<Usuario> usuarios;


    // Relación Uno a Muchos con Pagos
    @OneToMany(mappedBy = "vivienda", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Pago> pagos;

    /**
     * Método para verificar si la vivienda está al corriente de pago.
     */
    public boolean estaAlCorriente() {
        return Optional.ofNullable(pagos)
                .map(listaPagos -> listaPagos.stream().allMatch(Pago::isRealizado))
                .orElse(true); // Si no hay pagos, asumimos que está al corriente
    }

}

When I call the controller who is supposed to return the "Usuarios" information, it doesn't return the Viviendas IDs.

[
    {
        "id": 1,
        "username": "antonio",
        "password": null,
        "nombre": "Antonio",
        "apellido": "Pedro",
        "email": "[email protected]",
        "enabled": true,
        "rolesIds": [
            2,
            1
        ],
        "viviendasIds": [],
        "comunidadesIds": [
            1,
            2
        ]
    }
]

This is a controller.

    @GetMapping
    public List<UsuarioDTO> getAllUsuarios() {
        return usuarioService.getAllUsuarios();
    }

I need to solve the recursion problem. I don't understand where the error is. The "comunidades" information is returned correctly.

I'm building a Springboot-based application, but I'm running into a recursion issue that I can't resolve.

It mainly affects to Usuarios and Viviendas.

package com.antoniounidad.security.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.ToString;
import .hibernate.annotations.Fetch;
import .hibernate.annotations.FetchMode;
import .springframework.security.core.GrantedAuthority;
import .springframework.security.core.authority.SimpleGrantedAuthority;
import .springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

@Entity
@Table(name = "usuarios")
@Data
public class Usuario implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String username;

    @Column(nullable = false)
    private String nombre;

    @Column(nullable = false)
    private String apellido;

    @Column(nullable = false)
    @NotBlank
    @Size(min = 6,max =20)
    private String password;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private boolean enabled;

    // Un usuario puede estar en varias comunidades y una comunidad tiene varios usuarios
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
            name = "usuario_comunidad",
            joinColumns = @JoinColumn(name = "usuario_id"),
            inverseJoinColumns = @JoinColumn(name = "comunidad_id")
    )
    private Set<Comunidad> comunidades;

    // Relación ManyToMany con Vivienda
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
            name = "usuario_vivienda",
            joinColumns = @JoinColumn(name = "usuario_id"),
            inverseJoinColumns = @JoinColumn(name = "vivienda_id")
    )
    @JsonIgnore
    private Set<Vivienda> viviendas;


    // Relación ManyToMany con Roles
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
            name = "usuario_roles",
            joinColumns = @JoinColumn(name = "usuario_id"),
            inverseJoinColumns = @JoinColumn(name = "rol_id")
    )
    @Fetch(FetchMode.JOIN)
    private Set<Rol> roles;

    // Implementación de UserDetails para Spring Security
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles != null ? roles.stream()
                .map(rol -> new SimpleGrantedAuthority(rol.getNombre()))
                .collect(Collectors.toSet()) : Set.of();
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return this.enabled;
    }
}

viviendas.java

package com.antoniounidad.security.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import lombok.*;

import java.util.List;
import java.util.Set;
import java.util.Optional;

@Entity
@Data
@Table(name = "vivienda")
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Vivienda {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String bloque;
    private String piso;
    private String puerta;
    private Boolean alquilado;
    private Boolean alCorrientePago;

    // Relación Muchos a Uno con Comunidad (Cada vivienda pertenece a una comunidad)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "comunidad_id")
    @JsonIgnoreProperties({"viviendas"}) // Evita la carga recursiva infinita
    @ToString.Exclude // Evita la recursión infinita
    private Comunidad comunidad;

    /**
     * -- GETTER --
     *  Método para obtener los propietarios, forzando la carga de datos.
     */
    // Relación Muchos a Muchos con Usuario (Cada vivienda puede tener varios propietarios)
    @ManyToMany(mappedBy = "viviendas", fetch = FetchType.LAZY)
    private Set<Usuario> usuarios;


    // Relación Uno a Muchos con Pagos
    @OneToMany(mappedBy = "vivienda", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Pago> pagos;

    /**
     * Método para verificar si la vivienda está al corriente de pago.
     */
    public boolean estaAlCorriente() {
        return Optional.ofNullable(pagos)
                .map(listaPagos -> listaPagos.stream().allMatch(Pago::isRealizado))
                .orElse(true); // Si no hay pagos, asumimos que está al corriente
    }

}

When I call the controller who is supposed to return the "Usuarios" information, it doesn't return the Viviendas IDs.

[
    {
        "id": 1,
        "username": "antonio",
        "password": null,
        "nombre": "Antonio",
        "apellido": "Pedro",
        "email": "[email protected]",
        "enabled": true,
        "rolesIds": [
            2,
            1
        ],
        "viviendasIds": [],
        "comunidadesIds": [
            1,
            2
        ]
    }
]

This is a controller.

    @GetMapping
    public List<UsuarioDTO> getAllUsuarios() {
        return usuarioService.getAllUsuarios();
    }

I need to solve the recursion problem. I don't understand where the error is. The "comunidades" information is returned correctly.

Share Improve this question asked Mar 19 at 8:17 Antonio Jesús Rodríguez TorresAntonio Jesús Rodríguez Torres 11 bronze badge
Add a comment  | 

2 Answers 2

Reset to default 0

if you using @JsonIgnore resultList can't make items.

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
            name = "usuario_vivienda",
            joinColumns = @JoinColumn(name = "usuario_id"),
            inverseJoinColumns = @JoinColumn(name = "vivienda_id")
    )
    @JsonIgnore
    private Set<Vivienda> viviendas;

but this @JsonIgnore remove will cause recursive error. because you includes Viviendas has Usuarios list(set) and Usuarios has Viviendas list(set).

so if you want solve this; make another table and that table has two Class.

Use @JsonIgnoreProperties Instead of @JsonIgnore

Instead of ignoring the entire viviendas field, you can explicitly ignore the usuarios field inside Vivienda to prevent infinite recursion.

Modify Usuario.java

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
    name = "usuario_vivienda",
    joinColumns = @JoinColumn(name = "usuario_id"),
    inverseJoinColumns = @JoinColumn(name = "vivienda_id")
)
@JsonIgnoreProperties("usuarios") // Prevent recursion
private Set<Vivienda> viviendas;
发布评论

评论列表(0)

  1. 暂无评论