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 :

persistance avec un modèle de données tricoté


Sujet :

Persistance des données Java

  1. #1
    Membre du Club
    persistance avec un modèle de données tricoté
    Bonjour,
    Je vais simplifier mon modèle à l'extrême, pour expliquer mon problème

    J'ai plusieurs entités, A B et C avec une relation ManyToOne entre B et A, et aussi B et C. En gros, on pourrait dire qu'il y a une relation ManyToMany entre A et C mais la table de liaison contient des champs supplémentaires bref, B contient les identifiants A_ID et C_ID. Et même, il peut y avoir doublon sur le couple A_ID et C_ID dans cette table B, on est donc au delà du ManyToMany. Donc B avec une relation ManyToOne avec A et aussi ManyToOne avec C.
    Mais ces 3 entités sont aussi liées à D, aussi avec une relation ManyToOne dans les 3 cas. C'est redondant j'en suis conscient mais à ce jour c'est fait ainsi. Il y a un champ D_ID dans les 3 entités A B et C, et des clés étrangères en bdd.

    Côté java les relations sont bidirectionnelles, donc dans l'entité D j'ai un Set<A> un Set<B> un Set<C> et quand je persiste D je m'attends à ce que toutes les entités A B et C liées soient persistée aussi en BDD. ça marche "presque" sauf que l'on a souvent des ID null dans les tables liées. C'est permis en bdd pour l'instant donc ça passe; et lorsque j'ajoute une contrainte NOT NULL sur ces champs, comme prévu j'ai une exception oracle lors de l'insertion, ou une exception hibernate si j'ajoute dans l'annotation 'nullable = false'

    Je ne sais pas comment corriger cela, je voudrais dire à hibernate de persister d'abords l'entité D, puis les A et les C, et en dernier les B qui ont besoin des A_ID et C_ID mais je ne sais pas comment faire cela, si c'est possible ?

    Merci pour vos conseils

  2. #2
    Membre du Club
    re,

    Comme je n'ai pas résolu le problème, je pose la suite de mes investigations : j'ai activé le mode SQL pour tracer l'ordre des requêtes, et voila ce qui se passe :
    1/ hibernate commence par récupérer tous les ID de séquence pour tous les objets à insérer avec un "select SEQ_TBL.nextval from dual"
    2/ insert de l'entité D
    3/ ( insert de 2 autres entités liées à D que je n'ai pas évoqué ici )
    4/ insert de l'entité A liée à D
    5/ insert de l'entité B, mais pas toutes ses valeurs : seulement sa PK, ainsi que D_ID et A_ID. Donc erreur ORA-01400: cannot insert NULL into ("schema"."B"."C_ID")

    Mais, pour tester j'ai donc désactivé cette contrainte oracle, donc hibernate continue son job :

    6/ insert de l'entité C
    7/ update (!) de toutes les entités précédemment insérées, pour leur coller un champs de clé étrangère, en particulier le fameux B.C_ID manquant est ici mis à jour (7 update en tout, plusieurs sont redondant c'est pas étonnant, le modèle est redondant dans ses liaisons). Certains update vont mettre à jour un champs qui a déjà été mis à jour dans l'insert initial.

    C'est la pagaille. Je n'ai pas d'idée pour gérer l'ordre des insert au moment du persist de mes entités.

  3. #3
    Modérateur

    Ce serait bien de montrer tes entités, là, c'est un peu difficile de répondre.
    De ce que j'ai compris, il y a déjà une chose qui me dérange, c'est tes colonnes représentant les clés étrangères.
    Normalement, on met l'entité cible, pas la clé étrangère pour une entité avec une casacade=all sur les sous-entités.

    Bref, j'attends de voir le code
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  4. #4
    Membre du Club
    Bonjour,

    Voici les 4 entités, résumé au minimum, j'ai juste mis les liens et j'ai encore enlevé quelques annotations (@Data l'annotation lombok pour gérer les getter / setter, @XmlTransient et autres annotations en lien avec la marshallisation aussi...). Il s'agit de relations classique - enfin je crois ? - bidirectionnelle avec à la fois l'entité cible "mappedBy" et la clé étrangère avec @ForeignKey

    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
     
    @Entity
    @Table(name = "ENT_D")
    @org.hibernate.annotations.Entity(dynamicUpdate = true, dynamicInsert = true)
    public class EntD implements Serializable {
     
        @Id
        @GeneratedValue(generator = "hibseq")
        @GenericGenerator(name = "hibseq", strategy = "seqhilo", parameters = { @Parameter(name = "max_lo", value = "1"),
                @Parameter(name = "sequence", value = "SEQ_ENT_D") })
        @Type(type = "com.projet.perso.usertype.SequenceString")
        @Column(name = "D_ID")
        private Long dId;
     
        @OneToMany(cascade = CascadeType.ALL)
        @JoinColumn(name = "D_ID", nullable = true)
        @ForeignKey(name = "FK_ENT_C_ENT_D")
        private Set<EntC> entC;
     
        @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
        @JoinColumn(name = "D_ID", nullable = true)
        @ForeignKey(name = "FK_ENT_A_ENT_D")
        private Set<EntA> entA;
     
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "entD")
        @ForeignKey(name = "FK_ENT_B_ENT_D")
        private Set<EntB> entB;
     
    }
     
    @Entity
    @Table(name = "ENT_C")
    @org.hibernate.annotations.Entity(dynamicUpdate = true, dynamicInsert = true)
    public class EntC implements Serializable {
     
        @Id
        @GeneratedValue(generator = "hibseq")
        @GenericGenerator(name = "hibseq", strategy = "seqhilo", parameters = { @Parameter(name = "max_lo", value = "1"),
                @Parameter(name = "sequence", value = "SEQ_ENT_C") })
        @Type(type = "com.projet.perso.usertype.SequenceString")
        @Column(name = "C_ID")
        private Long cId;
     
        @ManyToOne
        @JoinColumn(name = "D_ID", nullable = false)
        @ForeignKey(name = "FK_ENT_C_ENT_D")
        private EntD entD;
     
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "entC")
        @ForeignKey(name = "FK_ENT_B_ENT_C")
        private Set<EntB> entB;
     
    }
     
    @Entity
    @Table(name = "ENT_A")
    @org.hibernate.annotations.Entity(dynamicUpdate = true, dynamicInsert = true)
    public class EntA implements Serializable {
     
        @Id
        @GeneratedValue(generator = "hibseq")
        @GenericGenerator(name = "hibseq", strategy = "seqhilo", parameters = { @Parameter(name = "max_lo", value = "1"),
                @Parameter(name = "sequence", value = "SEQ_ENT_A") })
        @Type(type = "com.projet.perso.usertype.SequenceString")
        @Column(name = "A_ID")
        private Long aId;
     
        @ManyToOne
        @JoinColumn(name = "D_ID", nullable = false)
        @ForeignKey(name = "FK_ENT_A_ENT_D")
        private EntD entD;
     
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "entA")
        @ForeignKey(name = "FK_ENT_B_ENT_A")
        private Set<EntB> entB;
     
    }
     
    @Entity
    @Table(name = "ENT_B")
    @org.hibernate.annotations.Entity(dynamicUpdate = true, dynamicInsert = true)
    public class EntB implements Serializable {
     
        @Id
        @GeneratedValue(generator = "hibseq")
        @GenericGenerator(name = "hibseq", strategy = "seqhilo", parameters = { @Parameter(name = "max_lo", value = "1"),
                @Parameter(name = "sequence", value = "SEQ_ENT_B") })
        @Column(name = "A_ID")
        private Long bId;
     
        @ManyToOne
        @JoinColumn(name = "D_ID")
        private EntD entD;
     
        @ManyToOne
        @JoinColumn(name = "C_ID")
        private EntC entC;
     
        @ManyToOne
        @JoinColumn(name = "A_ID")
        private EntA entA;
     
    }