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 :

Gestion clefs etrangere Hibernate JPA


Sujet :

Persistance des données Java

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    143
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 143
    Points : 127
    Points
    127
    Par défaut Gestion clefs etrangere Hibernate JPA
    Bonjour,

    J'ai besoin de votre aide car je n'arrive pas à comprendre comment utiliser des jointures dans une query lorsqu'il y a plus relations one to many? Voici ce que j'essaye de faire :

    Les films ont une relation one to many avec les acteurs
    @OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<Actors> actors;

    et les acteurs ont une relation one to many avec les Manageurs
    @OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<Manager> managers;

    Je souhaite implémenter un service qui va filtrer les films et ne remonter que ceux qui ont au moins un acteur ayant un manager avec le nom donné. En sql d'habitude cela se fait en faisant des jointures et en utilisant les clef étrangères mais là je ne vois pas comment faire car il n'y a pas de clef étrangère la relation est juste déclaré en mettant "Set<Manager> managers"

    Est-ce que vous sauriez m’éclairer?

    merci d'avance je suis totalement perdu pour l'instant

    J'ai commencé à écrire cela mais comme je le disais je ne vois pas comment faire les jointures sans clef étrangères
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    @Query("select id From Movie as m join Actor as a on m. = a.")
        List<Movie> getMoviesByManagerName(String managerName);

  2. #2
    Membre éclairé

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

    Informations forums :
    Inscription : Janvier 2009
    Messages : 463
    Points : 897
    Points
    897
    Billets dans le blog
    5
    Par défaut
    La logique est dans le Mapping.

    Exemple dans mon projet:
    https://bitbucket.org/philippegibaul...r40k/src/main/

    J'ai un camp:
    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;
    	}
    }
    J'ai une faction qui a un camp unique:
    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;
    	}
    }
    La clé étrangère est portée par l'annotation:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    @JoinColumn(name = "ID_CAMP",foreignKey = @ForeignKey(name = "FK_CAMP"),insertable = false, updatable = false,nullable = false)
    Du coup, je peux avoir une logique JPA Objet lors du JPQL.

    Je recherche évidement les factions par camps, soit:
    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
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
     
    package com.calculateur.warhammer.dao.dao;
     
    import java.util.ArrayList;
    import java.util.List;
     
    import javax.inject.Inject;
    import javax.inject.Named;
    import javax.persistence.Query;
     
    import com.calculateur.warhammer.base.dao.IDAO;
    import com.calculateur.warhammer.base.exception.DAOException;
    import com.calculateur.warhammer.base.exception.FunctionnalExeption;
    import com.calculateur.warhammer.dao.contrat.IFactionDAO;
    import com.calculateur.warhammer.dao.verification.VerificationUtils;
    import com.calculateur.warhammer.entity.entity.CampEntity;
    import com.calculateur.warhammer.entity.entity.FactionEntity;
    import com.calculateur.warhammer.entity.pk.FactionPK;
     
    @Named("factionDAO")
    public class FactionDAO extends AbstractDAO<FactionPK, FactionEntity> implements IFactionDAO {
     
    	private static final String ENTITY_CLASS = "FactionEntity";
     
    	private static final String ENTITY_LIBELLE = "factionEntity";
     
    	private static final String RES = "com.calculateur.warhammer.entity.faction.faction";
     
    	private static final String JPQL_DELETE = "DELETE FROM FactionEntity factionEntity WHERE factionEntity.id.idCamp = :idCamp AND factionEntity.id.idFaction = :idFaction";
     
    	private static final String JPQL_SELECT_BY_CAMP = "SELECT DISTINCT(factionEntity) FROM FactionEntity factionEntity WHERE factionEntity.id.idCamp = :idCamp";
     
    	@Inject
    	@Named("campDAO")
    	private IDAO<Integer, CampEntity> campDAO;
     
    	@SuppressWarnings("unchecked")
    	@Override
    	public List<FactionEntity> getFactions(Integer idCamp) throws DAOException {
    		try {
    			Query query = em.createQuery(JPQL_SELECT_BY_CAMP);
    			query.setParameter("idCamp", idCamp);
    			return query.getResultList();
    		} catch (Exception e) {
    			throw new DAOException(e);
    		}
    	}
     
    	@Override
    	protected String getEntityClass() {
    		return ENTITY_CLASS;
    	}
     
    	@Override
    	protected String getEntityLibelle() {
    		return ENTITY_LIBELLE;
    	}
     
    	@Override
    	protected String getErrorBundle() {
    		return RES;
    	}
     
    	@Override
    	protected Class<FactionEntity> getClassEntity() {
    		return FactionEntity.class;
    	}
     
    	// Surcharge à cause de l'id
    	@Override
    	protected Query getDeleteQuery(FactionPK id) {
    		Query query = em.createQuery(JPQL_DELETE);
    		query.setParameter("idCamp", id.getIdCamp());
    		query.setParameter("idFaction", id.getIdFaction());
    		return query;
    	}
     
    	@Override
    	public void verifieEntity(FactionEntity entity) throws DAOException, FunctionnalExeption {
    		super.verifieEntity(entity);
    		verificationSupplementaire(entity);
    	}
     
    	/**
             * Vérifications supplémentaires sur la clé primaire et le camp.
             * 
             * @param entity Entité à vérifier
             * @throws FunctionnalExeption Si erreur fonctionnelle détectée
             * @throws DAOException        Si erreur avec la BDD
             */
    	private void verificationSupplementaire(FactionEntity entity) throws FunctionnalExeption, DAOException {
    		List<String> errors = new ArrayList<>();
    		VerificationUtils.verifie(entity.getId(), RES);
    		campDAO.verifieEntity(entity.getCamp());
    		verificationCoherenceCleComposee(entity, entity.getId(), errors);
     
    		if (!errors.isEmpty()) {
    			throw new FunctionnalExeption(errors, RES);
    		}
    	}
     
    	private void verificationCoherenceCleComposee(FactionEntity entity, FactionPK id, List<String> errors)
    			throws DAOException {
    		if (id.getIdCamp().intValue() != entity.getCamp().getId().intValue()) {
    			errors.add("faction.camp.id.error");
    		}
    		CampEntity aCamp = campDAO.getById(entity.getCamp().getId());
    		if (aCamp == null) {
    			errors.add("faction.camp.camp.null");
    		}
    	}		
    }
    Le JPQL:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    private static final String JPQL_SELECT_BY_CAMP = "SELECT DISTINCT(factionEntity) FROM FactionEntity factionEntity WHERE factionEntity.id.idCamp = :idCamp";

  3. #3
    Membre émérite
    Homme Profil pro
    Ingénieur en génie logiciel
    Inscrit en
    Juin 2012
    Messages
    861
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Ingénieur en génie logiciel
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Juin 2012
    Messages : 861
    Points : 2 450
    Points
    2 450
    Par défaut
    il faudrait que tu postes le code de tes classes Actors et Managers car comme l'a dit PhilippeGibault, la relation est dans ces classes

  4. #4
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    143
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 143
    Points : 127
    Points
    127
    Par défaut
    merci beaucoup pour ton aide, j'ai essayé de m'inspirer de ce que tu as fait mais à l'execution j'ai toujours cette erreur "Path expected for join!" voici la version hibernate qu'on ma donné : Hibernate Core {5.0.11.Final}

    J'ai essayé de faire quelque chose de simple pour essayer de trouver la source du problème mais je ne trouve pas du tout

    Movie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    @OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "movie")
    private Set<Actor> actor;
    Actor :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    @ManyToOne
    @JoinColumn (name="movie_id")
    private Movie movie;
    MovieRepository
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    @Query("select m From Movie as m join Actor as a")
    List<Movie> searchByName(String name);

  5. #5
    Membre éclairé

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

    Informations forums :
    Inscription : Janvier 2009
    Messages : 463
    Points : 897
    Points
    897
    Billets dans le blog
    5
    Par défaut
    Je comprends rien au code 3.

    Déjà on a:
    et non:
    De fait, comme je n'ai qu'un seul "movie" par "actor", la requête devient:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    SELECT DISTINCT(m) FROM Movie m WHERE INNER JOIN m.actor AS a WHERE a.name = ?1
    Le ?1 est pour utiliser les Repository JPA Spring (ce que je ne fais pas dans mon projet).

    La jointure se fera par Hibernate en se basant sur le mapping (d'où l'importance de faire un bon mapping).

    Quelque remarques
    1) Dans la vraie vie, un acteur joue dans plusieurs film. On a donc une relation @ManyToMany et dans la base de données une table de jointure.
    Le mapping change, mais le JPQL reste le même.
    2)Attention au pluriel.
    Préférer
    à
    On a un set donc une collection, ils sont plusieurs.
    3)Ici, si on a des Homonymes (acteur ayant le même nom mais pas le même prénom), on a la liste des films avec les deux acteurs (comme George Dupond et Robert Dupond). Dans la vraie vie, il faut les identifier fonctionnellement au mieux ou par id éventuellement.

  6. #6
    Membre éclairé

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

    Informations forums :
    Inscription : Janvier 2009
    Messages : 463
    Points : 897
    Points
    897
    Billets dans le blog
    5
    Par défaut
    Autre remarque: Faire attention aux EAGER et LAZY.
    Ce concept existe pour @OneToMany mais aussi pour @ManyToOne.

    EAGER, ça veut dire que JPA va ramener tout ce qui est lié, et donc faire des requête en plus.

    En LAZY, il ne va pas chercher.

    Dans ton cas, pour que ça marche, c'est effectivement EAGER.

    Si tu mets LAZY, la requête devient:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    SELECT DISTINCT(m) FROM Movie m WHERE INNER FECTH JOIN m.actor AS a WHERE a.name = ?1

  7. #7
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    143
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 143
    Points : 127
    Points
    127
    Par défaut
    merci beaucoup!

    Je comprend beaucoup mieux comment ca fonctionne du coup, merci pour tes éclaircissements ça m'aide beaucoup.

    Effectivement j'ai écrit Event à la place de Movie dans le post, j'ai corrigé.

    Ça avance mais je suis encore bloqué : user lacks privilege or object not found: ACTORS1_.MOVIE_ID

    Je pense que le ACTORS1 vient de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    @OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "movie")
    private Set<Actor> actors;
    et le MOVIE_ID de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    @ManyToOne
    @JoinColumn (name="movie_id")
    private Movie movie;
    Mais je vois pas du tout ce que c'est cette histoire de privilège, est-ce que vous avez des idées?

  8. #8
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    143
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 143
    Points : 127
    Points
    127
    Par défaut
    c'est bon j'ai fini par trouvé, il y avait une conf à modifier dans l'application.yml

    hibernate.ddl-auto: "update"

    Merci!

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

Discussions similaires

  1. innobd clef etrangere
    Par mikebranque dans le forum SQL Procédural
    Réponses: 5
    Dernier message: 27/08/2006, 11h18
  2. Problème de clefs étrangères
    Par rieppe dans le forum SQL Procédural
    Réponses: 7
    Dernier message: 26/07/2006, 09h36
  3. gestion clefs primaires
    Par madadiop dans le forum Bases de données
    Réponses: 4
    Dernier message: 19/09/2005, 23h56
  4. clefs etrangeres en mysql
    Par loveflower dans le forum Décisions SGBD
    Réponses: 1
    Dernier message: 10/11/2004, 19h48
  5. Modification de clefs etrangeres impossible
    Par mboitet dans le forum SQL
    Réponses: 1
    Dernier message: 19/08/2004, 17h11

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