web-dev-qa-db-fra.com

Spring Data JPA - relation bidirectionnelle avec récursion infinie

Voici d'abord mes entités.

Joueur :

@Entity
@JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, 
property="id")
public class Player {

    // other fields

    @ManyToOne
    @JoinColumn(name = "pla_fk_n_teamId")
    private Team team;

    // methods

}

Équipe :

@Entity
@JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, 
property="id")
public class Team {

    // other fields

    @OneToMany(mappedBy = "team")
    private List<Player> members;

    // methods

}

Comme de nombreux sujets ont déjà été mentionnés, vous pouvez éviter de plusieurs façons StackOverflowExeption dans votre WebService avec Jackson.

C'est cool et tout, mais JPA construit toujours une entité avec une récursion infinie vers une autre entité avant la sérialisation. C'est tout simplement laid et la demande prend beaucoup plus de temps. Vérifiez cette capture d'écran: débogueur IntelliJ

Y a-t-il un moyen de le réparer ? Sachant que je veux des résultats différents selon le point final. Exemples :

  • endpoint /teams/{id} => Team = {id ..., members = [Player = {id ..., team = null }]}
  • endpoint /members/{id} => Player = {id ..., team = {id ..., membres = null }}

Je vous remercie!

EDIT: peut-être que la question n'est pas très claire en donnant les réponses que j'obtiens alors je vais essayer d'être plus précis.

Je sais qu'il est possible d'empêcher la récursion infinie soit avec Jackson (@JSONIgnore, @ JsonManagedReference/@ JSONBackReference etc.) ou en faisant un mappage dans DTO. Le problème que je vois toujours est le suivant: les deux ci-dessus sont un traitement post-requête. L'objet renvoyé par Spring JPA sera toujours (par exemple) une équipe, contenant une liste de joueurs, contenant une équipe, contenant une liste de joueurs, etc. etc.

Je voudrais savoir s'il existe un moyen de dire à JPA ou au référentiel (ou quoi que ce soit) de ne pas lier des entités au sein d'entités encore et encore?

8
Mickaël Bénès

Voici comment je gère ce problème dans mes projets.

J'ai utilisé le concept d'objets de transfert de données, implémenté en deux versions: un objet complet et un objet léger.

Je définis un objet contenant les entités référencées comme List as Dto (objet de transfert de données qui ne contient que des valeurs sérialisables) et je définis un objet sans les entités référencées comme Info.

Un objet Info ne contient que des informations sur l'entité même et non sur les relations.

Maintenant, lorsque je livre un objet Dto sur une API REST, je mets simplement des objets Info pour les références.

Supposons que je distribue un PlayerDto sur GET /players/1:

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

Alors que l'objet TeamInfo ressemble à

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

par rapport à un TeamDto

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

Cela évite une sérialisation sans fin et fait également une fin logique pour vos ressources de repos, sinon vous devriez pouvoir GET /player/1/team/player/1/team

De plus, le concept sépare clairement la couche de données de la couche client (dans ce cas, l'API REST), car vous ne passez pas l'objet réellement entité à l'interface. Pour cela, vous convertissez l'entité réelle à l'intérieur de votre couche de service en Dto ou Info. J'utilise http://modelmapper.org/ pour cela, car c'est super facile (un court appel de méthode).

Je récupère également toutes les entités référencées paresseusement . Ma méthode de service qui obtient l'entité et la convertit en Dto pour les exécutions à l'intérieur d'une portée de transaction, ce qui est de toute façon une bonne pratique.

Récupération paresseuse

Pour dire à JPA de récupérer une entité paresseusement, modifiez simplement votre annotation de relation en définissant le type de récupération. La valeur par défaut est fetch = FetchType.EAGER ce qui dans votre situation est problématique. C'est pourquoi vous devriez le changer en fetch = FetchType.LAZY

public class TeamEntity {

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

De même, le Player

public class PlayerEntity {

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

Lorsque vous appelez votre méthode de référentiel à partir de votre couche de service, il est important que cela se produise dans un @Transactional scope, sinon, vous ne pourrez pas obtenir l'entité référencée paresseusement. Qui ressemblerait à ceci:

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

Vous pouvez utiliser @ JsonIgnoreProperties annotation pour éviter une boucle infinie, comme ceci:

@JsonIgnoreProperties("members")
private Team team;

ou comme ça:

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

ou les deux.

4
Cepr0

Dans mon cas, j'ai résolu cela en réalisant que je n'avais pas besoin d'une relation OneToMany - ManyToOne (bidirectionnelle).

Quelque chose comme ça a résolu mon problème

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

// Player Class:
// - I removed the 3 lines

Voici un lien avec plus d'exemples: https://github.com/thombergs/code-examples/tree/master/spring-data/spring-data-rest-associations/src/main/Java/com/example/démo

Project Lombok génère également ce même problème. J'ai essayé @ ToString et @ EqualsAndHashCode pour corriger cela:

par exemple.

@Data
@Entity

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

public class Team implements Serializable {

// ...


En outre, voici un guide utile sur les annotations empêchant les boucles
https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

1
user9869932