Saltar al contenido

Spring Data JPA – relación bidireccional con recursividad infinita

Solución:

Así es como manejo este problema en mis proyectos.

Utilicé el concepto de objetos de transferencia de datos, implementado en dos versiones: un objeto completo y un objeto ligero.

Defino un objeto que contiene las entidades referenciadas como Lista como Dto (objeto de transferencia de datos que solo contiene valores serializables) y defino un objeto sin las entidades referenciadas como Info.

A Info El objeto solo contiene información sobre la propia entidad y no sobre las relaciones.

Ahora cuando entrego un Dto objeto sobre una API REST, simplemente puse Info objetos para las referencias.

Supongamos que entrego un PlayerDto sobre GET /players/1:

public class PlayerDto{
   private String playerName;
   private String playercountry;
   private TeamInfo;
}

Mientras que el TeamInfo el objeto se parece a

public class TeamInfo {
    private String teamName;
    private String teamColor;
}

comparado con un TeamDto

public class TeamDto{
    private String teamName;
    private String teamColor;
    private List<PlayerInfo> players;
}

Esto evita una serialización interminable y también crea un final lógico para sus recursos de descanso, ya que de lo contrario debería poder GET /player/1/team/player/1/team

Además, el concepto separa claramente la capa de datos de la capa del cliente (en este caso, la API REST), ya que no pasa el objeto de entidad real a la interfaz. Para esto, convierte la entidad real dentro de su capa de servicio en un Dto o Info. Utilizo http://modelmapper.org/ para esto, ya que es muy fácil (una breve llamada al método).

También busco todas las entidades referenciadas perezosamente. Mi método de servicio que obtiene la entidad y la convierte a la Dto allí para ejecuciones dentro del alcance de una transacción, lo cual es una buena práctica de todos modos.

Ir a buscar perezoso

Para decirle a JPA que busque una entidad de forma perezosa, simplemente modifique la anotación de su relación definiendo el tipo de búsqueda. El valor predeterminado para esto es fetch = FetchType.EAGER que en tu situación es problemática. Por eso deberías cambiarlo a fetch = FetchType.LAZY

public class TeamEntity {

    @OneToMany(mappedBy = "team",fetch = FetchType.LAZY)
    private List<PlayerEntity> members;
}

Asimismo el Player

public class PlayerEntity {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "pla_fk_n_teamId")
    private TeamEntity team;
}

Al llamar a su método de repositorio desde su capa de servicio, es importante que esto suceda dentro de un @Transactional alcance, de lo contrario, no podrá obtener la entidad a la que se hace referencia perezosamente. Que se vería así:

 @Transactional(readOnly = true)
public TeamDto getTeamByName(String teamName){
    TeamEntity entity= teamRepository.getTeamByName(teamName);
    return modelMapper.map(entity,TeamDto.class);
}

Puede usar la anotación @JsonIgnoreProperties para evitar un bucle infinito, como este:

@JsonIgnoreProperties("members")
private Team team;

o así:

@JsonIgnoreProperties("team")
private List<Player> members;

o ambos.

En mi caso, me di cuenta de que no necesitaba una relación (bidireccional) OneToMany-ManyToOne.

Esto solucionó mi problema

// Team Class:
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Player> members = new HashSet<Player>();

// Player Class - These three lines removed:
// @ManyToOne
// @JoinColumn(name = "pla_fk_n_teamId")
// private Team team;

Proyecto Lombok también podría producir este problema. Intenta agregar @Encadenar y @EqualsAndHashCode si es el caso.

@Data
@Entity

@EqualsAndHashCode(exclude = { "members"}) // This,
@ToString(exclude = { "members"}) // and this

public class Team implements Serializable {

// ...


Buena guía en anotaciones de recursividad infinitas

https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *