IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Persistance des données Java Discussion :

Spring : manyToMany bidirectionnel, les données mappés d'un coté est à null


Sujet :

Persistance des données Java

  1. #1
    Membre habitué
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2022
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2022
    Messages : 10
    Par défaut Spring : manyToMany bidirectionnel, les données mappés d'un coté est à null
    Spring 2.5.6

    Bonjour,

    J'ai mis en place manyToMany bidirectionnel, seulement un seul coté récupère les données mappés.

    /profils
    j'ai bien des fonctionnalités dans profil
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
            {
                "id": 1,                          // profil
                "code": "CODEP1",
                "label": "defautP1",
                "description": "description defautP1",
                "fonctionnalites": [
                    {
                        "id": 3,                  // fonctionnalité
                        "code": "codeF3",
                        "label": "labelF3",
                        "description": "descriptionF3",
                        "profils": null
                    },
                    {
                        "id": 1,                  // fonctionnalité
                        "code": "codeF1",
                        "label": "labelF1",
                        "description": "descriptionF1",
                        "profils": null
                    }
                ]
            },

    /fonctionnalites

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    [
            {
                "id": 1,               // fonctionnalités
                "code": "codeF1",
                "label": "labelF1",
                "description": "descriptionF1",
                "profils": null      // est toujours à NULL
                                         // il doit y avoir profil dont id = 1
            },
    Profil
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    @Entity
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @Table(name = "t_profil")
    public class Profil {
     
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_profil")
        @SequenceGenerator(name = "seq_profil", sequenceName = "seq_profil", allocationSize = 1)
        Long id;
     
        @Column(name = "code")
        String code;
     
        @Column(name = "label")
        String label;
     
        @Column(name = "description")
        String description;    
     
        @ManyToMany(cascade =  {
                CascadeType.ALL,
            }, fetch = FetchType.LAZY)
        @JoinTable(
                name = "t_profils_fonctionnalites",
                joinColumns = { @JoinColumn(name = "profil_id") },
                inverseJoinColumns = { @JoinColumn(name = "fonctionnalite_id") }
        )
        public Set<Fonctionnalite> fonctionnalites; 
    }
    Fonctionnalité
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    @Entity
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @Table(name = "t_fonctionnalite")
    public class Fonctionnalite {
     
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_fonctionnalite")
        @SequenceGenerator(name = "seq_fonctionnalite", sequenceName = "seq_fonctionnalite", allocationSize = 1)
        Long id;
     
        @Column(name = "code")
        String code;
     
        @Column(name = "label")
        String label;
     
        @Column(name = "description")
        String description;    
     
        //@JsonIgnore    
        @ManyToMany(mappedBy = "fonctionnalites", fetch = FetchType.LAZY, cascade =  { CascadeType.ALL, })
        Set<Profil> profils; 
    }
    j'ai essayé pour profil et fonctionnalité le mode : FetchType.EAGER, même résultat !


    controller
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    fonctionnaliteDao.findAll(spec, pageable);
    j'ai aussi essayé (en mode LAZY) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    fonctionnaliteDao.findAll(spec, pageable);
    fonctionnalitePage.getContent().forEach(f -> f.getProfils());

  2. #2
    Membre très actif

    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    483
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 483
    Billets dans le blog
    5
    Par défaut
    Plusieurs remarques...

    Déjà, j'éviterais l'Entité (le POJO Hibernate) dans le controller. Je passerai par une DTO qui réduirait les données que l'on veut communiquer à l'extérieure.

    Deuxièmement, je me poserais plus sérieusement la question du EAGER/LAZY car elle est effectivement fondamentale.

    EAGER, ça veut dire que je demande à Hibernate de charger ce qui est lié à l'entité. Donc éventuellement de faire des requête en plus.

    LAZY, ça veut dire que je demande à Hibernate d'être faignant. De charger à la demande. Si c'est LAZY, hibernate ne le fera pas.

    Cette question est fondamentale car elle a des répercussions sur les performances.

    Par défaut, la défensive c'est de mettre LAZY.


    On peut rendre un LAZY EAGER avec du JPQL (comme SELECT user FROM User user INNER JOIN FECTH user.roles) avec ici roles LAZY.

    Je vais donner un exemple de mon projet (même si ici, il n'y a pas ManyToMany, mais le principe reste le même).

    https://bitbucket.org/philippegibaul...r40k/src/main/

    Dans mon projet, j'ai des factions et des camps.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
     
    package com.calculateur.warhammer.entity.entity;
     
    import java.util.Set;
     
    import javax.persistence.EmbeddedId;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.ForeignKey;
    import javax.persistence.JoinColumn;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.ManyToOne;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    import javax.validation.constraints.NotNull;
    import com.calculateur.warhammer.entity.pk.FactionPK;
     
    /**
     * Entité pour les faction en BDD
     * 
     * @author phili
     *
     */
    @Entity
    @Table(name = "FACTION")
    public class FactionEntity extends AbstractEntityAvecLibelle<FactionPK> {
     
    	@EmbeddedId
    	@NotNull(message = "faction.pk.null")
    	private FactionPK id;
     
    	@ManyToOne(fetch = FetchType.EAGER)
    	@JoinColumn(name = "ID_CAMP",foreignKey = @ForeignKey(name = "FK_CAMP"),insertable = false, updatable = false,nullable = false)
    	@NotNull(message = "faction.camp.null")
    	private CampEntity camp;
     
    	@OneToMany(fetch = FetchType.LAZY,mappedBy = "faction")
    	private Set<SousFactionEntity> sousFactions;
     
    	@ManyToMany(fetch = FetchType.LAZY)
    	@JoinTable(name = "ASSOCIATION_FACTION_REGLE_FACTION",
    		joinColumns = {
    				@JoinColumn(name = "ID_CAMP",foreignKey = @ForeignKey(name = "FK_CAMP_ASSOCIATION_ASSOCIATION_FACTION_REGLE_FACTION"),insertable = false, updatable = false),
    				@JoinColumn(name = "ID_FACTION",foreignKey = @ForeignKey(name = "FK_FACTION_ASSOCIATION_ASSOCIATION_FACTION_REGLE_FACTION"),insertable = false, updatable = false)
    		},
    		inverseJoinColumns = @JoinColumn(name = "ID_REGLE_FACTION",foreignKey = @ForeignKey(name = "FK_REGLE_FACTION_ASSOCIATION_ASSOCIATION_FACTION_REGLE_FACTION"))
    			)
    	private Set<RegleFactionEntity> regles;
     
    	@JoinTable(name = "ASSOCIATION_FACTION_AURA",
    			joinColumns = {
    					@JoinColumn(name = "ID_CAMP",foreignKey = @ForeignKey(name = "FK_CAMP_ASSOCIATION_ASSOCIATION_FACTION_AURA"),insertable = false, updatable = false),
    					@JoinColumn(name = "ID_FACTION",foreignKey = @ForeignKey(name = "FK_FACTION_ASSOCIATION_ASSOCIATION_FACTION_AURA"),insertable = false, updatable = false)
    			},
    			inverseJoinColumns = @JoinColumn(name = "ID_AURA",foreignKey = @ForeignKey(name = "FK_REGLE_FACTION_ASSOCIATION_ASSOCIATION_FACTION_AURA"))
    				)
    	@ManyToMany(fetch = FetchType.LAZY)
    	private Set<AuraEntity> auras;
     
    	@Override
    	public FactionPK getId() {
    		return id;
    	}
     
    	@Override
    	public void setId(FactionPK id) {
    		this.id = id;
    	}
     
    	public CampEntity getCamp() {
    		return camp;
    	}
     
    	public void setCamp(CampEntity camp) {
    		this.camp = camp;
    	}
     
    	public Set<SousFactionEntity> getSousFactions() {
    		return sousFactions;
    	}
     
    	public void setSousFactions(Set<SousFactionEntity> sousFactions) {
    		this.sousFactions = sousFactions;
    	}
     
    	public Set<RegleFactionEntity> getRegles() {
    		return regles;
    	}
     
    	public void setRegles(Set<RegleFactionEntity> regles) {
    		this.regles = regles;
    	}
     
    	public Set<AuraEntity> getAuras() {
    		return auras;
    	}
     
    	public void setAuras(Set<AuraEntity> auras) {
    		this.auras = auras;
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    package com.calculateur.warhammer.entity.entity;
     
    import java.util.Set;
     
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    import javax.validation.constraints.NotNull;
     
     
    @Entity
    @Table(name = "CAMP")
    public class CampEntity extends AbstractEntityAvecLibelle<Integer>{
     
    	@Id
    	@Column(name = "ID_CAMP",nullable = false)
    	@NotNull(message = "id.null")
    	private Integer id;
     
    	@OneToMany(mappedBy = "camp",fetch = FetchType.LAZY)
    	private Set<FactionEntity> factions;
     
    	@Override
    	public Integer getId() {
    		return id;
    	}
     
    	@Override
    	public void setId(Integer id) {
    		this.id = id;
    	}
     
    	public Set<FactionEntity> getFactions() {
    		return factions;
    	}
     
    	public void setFactions(Set<FactionEntity> factions) {
    		this.factions = factions;
    	}
    }
    Mon service donnant à l'extérieur les Camps ne donne que ce qui est propre aux camps. Et idem pour la faction.

    Donc, tout est LAZY.

    Pour donner à l'extérieur, via des Services REST, je passe par des DTO:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    package com.calculateur.warhammer.dto;
     
    public class CampDTO extends AbstractLibelleDTO {
     
     
     
    	/**
             * 
             */
    	private static final long serialVersionUID = 1L;
     
    	private static final String RESOURCES_TRADUCTION = "com.calculateur.warhammer.dto.camp.camp";
     
    	@Override
    	public String getResourceBudleAsString() {
    		return RESOURCES_TRADUCTION;
    	}
     
    	@Override
    	public String toString() {
    		StringBuilder sb = new StringBuilder("Camp : [id = ");
    		sb.append(getId());
    		sb.append(", nom = ");
    		if(getLibelle() != null) {
    			sb.append(getLibelle());
    		}else {
    			sb.append(getNom());
    		}
    		sb.append("]");
    		return sb.toString();
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    package com.calculateur.warhammer.dto;
     
    public class FactionDTO extends AbstractLibelleDTO{
     
    	/**
             * 
             */
    	private static final long serialVersionUID = 1L;
     
    	private static final String RESOURCES = "com.calculateur.warhammer.dto.faction.faction";
     
    	@Override
    	public String getResourceBudleAsString() {
    		return RESOURCES;
    	}
     
    	@Override
    	public String toString() {
    		StringBuilder sb = new StringBuilder("Faction : [id = ");
    		sb.append(getId());
    		sb.append(", nom = ");
    		if(getLibelle() != null) {
    			sb.append(getLibelle());
    		}else {
    			sb.append(getNom());
    		}
    		sb.append("]");
    		return sb.toString();
    	}
    }
    Ma DTO est spéciale, coome mon appli est internationale, je traduis selon une clé de traduction (en BDD):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
    package com.calculateur.warhammer.dto;
     
    import java.util.Locale;
     
    import com.calculateur.warhammer.base.dto.ILibelleDTO;
     
    public abstract class AbstractLibelleDTO implements ILibelleDTO{
     
    	/**
             * 
             */
    	private static final long serialVersionUID = 1L;
     
    	private Integer id;
    	private String nom;
    	private String cleTraduction;
    	private String libelle;
     
    	@Override
    	public void traduireLibelles(Locale locale) {
    		libelle = getResourceBundle(locale).getString(getCleTraduction());
    	}
     
    	@Override
    	public Integer getId() {
    		return id;
    	}
     
    	@Override
    	public void setId(Integer id) {
    		this.id = id;
    	}
     
    	@Override
    	public String getNom() {
    		return nom;
    	}
     
    	@Override
    	public void setNom(String nom) {
    		this.nom = nom;
    	}
     
    	@Override
    	public String getCleTraduction() {
    		return cleTraduction;
    	}
     
    	@Override
    	public void setCleTraduction(String cleTraduction) {
    		this.cleTraduction = cleTraduction;
    	}
     
    	@Override
    	public String getLibelle() {
    		return libelle;
    	}
     
    	public void setLibelle(String libelle) {
    		this.libelle = libelle;
    	}
    }
    Je n'ai plus qu'à mapper (chez moi, dans un sens seulement):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    package com.calculateur.warhammer.service.mapping;
     
    import javax.inject.Named;
     
    import com.calculateur.warhammer.dto.CampDTO;
    import com.calculateur.warhammer.entity.entity.CampEntity;
     
     
    @Named(value = "campMapping")
    public class CampMapping extends AbstractMappingPourLibelle<Integer, CampEntity, CampDTO>{
     
    	@Override
    	protected CampDTO getNewInstanceDTO() {
    		return new CampDTO();
    	}
     
    	@Override
    	protected Integer getIdAsInteger(Integer id) {
    		return id;
    	}
     
    	@Override
    	protected void autreMappingEntityVersDTO(CampEntity entity, CampDTO dto) {
    		//Rien
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
     
    package com.calculateur.warhammer.service.mapping;
     
    import java.io.Serializable;
     
    import com.calculateur.warhammer.base.dto.ILibelleDTO;
    import com.calculateur.warhammer.base.entity.IEntityAvecLibelle;
    import com.calculateur.warhammer.base.mapping.IMappingEntityVersDTO;
     
    /**
     * Template Methode pour les classes ayant des libellé (et description aussi)
     * @author phili
     *
     * @param <I> Id entité
     * @param <E> Entité
     * @param <D> DTO
     */
    public abstract class AbstractMappingPourLibelle<I extends Serializable,E extends IEntityAvecLibelle<I>,D extends ILibelleDTO> implements IMappingEntityVersDTO<E, D>{
     
    	@Override
    	public D mapEntityVersDTO(E entity) {
    		D dto = null;
    		if(entity != null) {
    			dto = getNewInstanceDTO();
    			dto.setId(getIdAsInteger(entity.getId()));
    			dto.setNom(entity.getNom());
    			dto.setCleTraduction(entity.getCleTraduction());
    			autreMappingEntityVersDTO(entity, dto);
    		}
    		return dto;
    	}
     
     
    	/**
             * 
             * @return Nouvelle instance DTO
             */
    	protected abstract D getNewInstanceDTO();
     
    	/**
             * 
             * @param id Id DTO
             * @return L'identifiant pour la DTO
             */
    	protected abstract Integer getIdAsInteger(I id);
     
    	/**
             * Autre mapping de l'entité vers la DTO
             * @param entity Une entité
             * @param dto une DTO
             */
    	protected abstract void autreMappingEntityVersDTO(E entity, D dto);
    }
    A noter que comme je n'aime pas me répéter, j'utilise le Design Pattern Template Methode.

    A noté que la DTO permet de différentier l'entité. Ce n'est pas le même rôle.

    Dans ton cas, je garderai l'entité que je séparerai de la DTO, c'est à dire je garderai l'entité pour mapper à la BDD, j'enlèverai toute la logique JSON de l'entité, et selon ce que je veux sortir et donner à l'utilisateur, je ferai 1 ou plusieurs DTO.

    Cordialement.

  3. #3
    Membre très actif

    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    483
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 483
    Billets dans le blog
    5
    Par défaut
    J'ai de fait oublié que l'on passe par des services, qui donnent des DTO.

  4. #4
    Membre habitué
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2022
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2022
    Messages : 10
    Par défaut
    merci philippe pour tous ces renseignements
    je vais étudier tes conseils

    merci encore !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Spring : manyToMany qui récupère les données mappées de façon incomplète
    Par poubda dans le forum Persistance des données
    Réponses: 6
    Dernier message: 08/12/2022, 06h02
  2. Réponses: 6
    Dernier message: 25/05/2018, 12h26
  3. Réponses: 1
    Dernier message: 19/04/2017, 16h06
  4. extraire toutes les données d'une table (coté serveur)
    Par stoner2008 dans le forum Servlets/JSP
    Réponses: 0
    Dernier message: 13/06/2014, 12h40
  5. trier les données dans le cache ??
    Par psyco2604 dans le forum XSL/XSLT/XPATH
    Réponses: 31
    Dernier message: 10/06/2003, 10h03

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo