2017-10-03 1 views
0

je les 2 entités suivantes:Comment annoter plusieurs ManyToOne à la même entité, mais un de leurs champs doit être la même

@Entity 
@Table(name="user") 
public class User { 
    @Id 
    @Column("id") 
    private long id; 

    @Column(name="code", nullable = false) 
    private String code; 

    @Column(name="session_id", nullable = false) 
    private long sessionId; 
} 

@Entity 
@Table(name="task") 
public class Task { 
    @Id 
    @Column("id", nullable = false) 
    private long id; 

    @ManyToOne(optional = false) 
    @JoinColumn(name="primary_user_code", referencedColumnName = "code", nullable = "false") 
    private User primaryUser; 

    @ManyToOne(optional = true) 
    @JoinColumn(name="secondary_user_code", referencedColumnName = "code", nullable = "true") 
    private User secondaryUser; 
} 

Le problème est que les deux objets User à Task doit avoir la mêmes sessionId. Existe-t-il un moyen pour que cela soit appliqué en utilisant les annotations d'hibernation? Ou dois-je juste mordre la balle et l'appliquer dans le code?

J'ai essayé de regarder dans les @Where et @WhereJoinTable annotations, mais selon ce hibernate rapport de bogue, link, il n'est pas pris en charge pour @ManyToOne


Mise à jour

j'aurais probablement mentionné que User déjà existe et il ne peut pas être changé. Ce que je contrôle est la classe Task que j'ajoute.

Le code n'est pas unique et, associé au paramètre session_id, identifie un utilisateur unique. Le session_id fait référence à une table distincte mais elle n'est pas annotée avec une relation @OneToOne. C'est juste une colonne simple et la relation avec la table Session est traitée dans le code. Essentiellement, chaque User a un code, et lié à plusieurs Sessions dont un seul peut être actif.

Pour la table de travail que j'ajoute, je voulais annoter si possible

+0

Vous ne savez pas exactement ce que vous essayez d'accomplir ici, mais je pense que ce que vous avez écrit est tout à fait correct. Vous n'avez qu'à vous soucier des colonnes de clé étrangère qui sont ajoutées à votre table TASK. Pour 2 entités Utilisateur associées, vous avez 2 diff. les noms de colonnes de clés étrangères, à savoir primary_user_code et secondary_user_code, qui n'auraient pas de conflits de noms. L'attribut sessionId de l'entité User ne sera pas mappé à la table TASK, il n'y a donc pas de conflit de nom. – Ish

+1

Non directement liée à la question mais votre clé étrangère pointe vers la colonne 'code' de l'utilisateur. Cette colonne est-elle unique? Selon votre question, je ne pense pas que votre exigence puisse être résolue par une APP commune. Vous aurez besoin de faire la vérification avant de persister/fusionner les entités impliquées – Al1

+0

Cela ne fonctionnera que sur la plupart des RDBM si vous ajoutez un unique = true à la définition de la colonne. Même alors, je pense que c'est une mauvaise pratique: FK devrait toujours pointer vers PK. – fhossfel

Répondre

0

Ceci est impossible dans SQL classique. Les contraintes de vérification sont appliquées par ligne et comme les deux session_ids apparaissent sur deux lignes différentes, les contraintes de vérification ne seront d'aucune aide. Dans Oracle, vous pouvez avoir un rapide sur commit rafraîchissant MV avec une contrainte pour de telles situations mais c'est hors de portée ici.

Le mieux est de vérifier cette condition du côté Java en utilisant les rappels @PrePersist @PreUpdate.

@PrePersist @PreUpdate 
public void checkSessionId() { 
    if (primaryUser.getSessionId() != secondaryUser.getSessionId()) 
     throw new IllegalStateException("Mismatch of sessionId between primary and secondaryIser"); // Better to define your own exception here 

} 

Bien sûr, la partie délicate consiste à gérer correctement l'exception. EntityManager marquera la transaction comme rollback. Donc, cela devrait vraiment être votre dernière ligne de défense dans le cas où la vérification de la logique métier a échoué.

+0

Merci, je vais essayer ça. –

+0

Malheureusement, je ne peux pas utiliser '@ PrePersist' et' @ PreUpdate' puisque nous utilisons Hibernate Session. Cela ne fonctionne que lorsque vous utilisez 'EntityManager' https://stackoverflow.com/a/4133629/8712057 –

+0

Avec Hibernate pure, vous pouvez enregistrer un Interceptor qui fait essentiellement la même chose. Voir la documentation [Hibernate] (https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#events) pour plus de détails. – fhossfel

0

Vous pouvez écrire votre annotation de validation personnalisée et une classe qui implémente ConstraintValidator. En remplaçant sa méthode isValid, vous pouvez ajouter votre logique personnalisée à votre annotation personnalisée. Découvrez this exemple.

+0

Merci, je vais essayer ça. –