9
votes

Existe-t-il un moyen d'utiliser sa participation pour sauter plusieurs tables?

Disons que j'ai une table d'utilisateur primaire appelée userprofile , et il y a un nom d'affichage.

J'ai divers modules que vous pouvez être membre de, avec un userProfile et un moduleID sur celui-ci pour signifier votre modulemembership . Vous pouvez ensuite avoir un profil pour chaque module différent pour stocker des données liées à ce module, par exemple si vous êtes inscrit pour le pokermodule Vous obtiendrez un pokerprofile .

Je voudrais mettre le nom d'affichage de userprofile sur le pokerprofile , mais je voudrais le faire de manière normalisée. Je pourrais le faire via Hibernate ou via SQL, de toute façon fonctionne. La relation exacte serait pokerprofile.membership.userprofile.displayName - Comment puis-je obtenir cela dans un @column sur le pokerprofile classe?


5 commentaires

Je dois manquer quelque chose parce que cela me semble que vous pouvez simplement copier le nom display de UserProfile vers Modulemembership et fusionner la mise à jour.


Qu'en est-il de @manytoone? C'est peut-être une association d'entité.


Cela pourrait être utile si vous pouviez partager le code de vos définitions d'entité. Bien que de ce que vous décrivez, je suis incertain pourquoi vous ne pouvez pas simplement ajouter une méthode @Transient sur la classe pokerprofile (comme, dites getansername () < / Code>) Cela fait quelque chose comme renvoyer cela.getmembership (). GetUserProfile (). GetDisplayName (); . En supposant que vous avez défini toutes vos relations correctement. Il n'y a aucune raison d'utiliser SQL ou HQL pour quelque chose comme ceci, à moins que le SQL que la couche ORM génère est simplement trop lente pour votre goût.


@Aroth Je ne suis pas certain qu'il serait utile de le faire - la question ne concerne pas cet exemple de poker, il s'agit de savoir comment obtenir génériquement une colonne qui vit dans une autre table dans une classe. Vous avez raison qu'une méthode transitoire puisse fonctionner, et elle est élégante, mais elle souffre également du problème n + 1 sinon tout est paresseux chargé. Pour continuer l'exemple de poker, envisagez de rechercher l'état actuel de la table de poker - je ne veux pas récupérer l'adhésion et l'UserProfile (qui est assez gros!) Juste pour obtenir les noms d'affichage. L'Ussurofile peut être levé vers le haut via le nom d'affichage si nécessaire.


@corsika - Oui, il peut toujours y avoir des problèmes d'optimisation. La meilleure solution dépendra de la manière dont les données sont utilisées. Par exemple, les propriétés dérivées sont peuplées au moment de l'extraction, ce qui signifie que la sous-requête associée est toujours . Cela peut être inefficace si vous êtes seulement parfois intéressé par la valeur. Mais si vous le voulez presque toujours, c'est une histoire différente. Bien que pour les joueurs d'un jeu, je pourrais utiliser une requête dédiée, comme Sélectionnez displayName à partir de joueurs où ID IN (Sélectionnez PlayerID à partir de gameplayers où gameid =?) . Mais comme vous l'avez dit, c'est un exemple de fait valable de toute façon.


3 Réponses :


9
votes

Vous pouvez utiliser un Propriété dérivée pour récupérer le displayName code> dans votre pokerprofile Code> Classe, comme suit:

@Transient
public String getDisplayName() {
    if (membership == null) {
        return null;
    }
    UserProfile userProfile = membership.getUserProfile();
    return userProfile == null ? null : userProfile.getDisplayName();
}


6 commentaires

Si getdisplayName () est censé être placé à l'intérieur d'une définition d'entité, vous ne voulez pas lancer un @Transient devant celui-ci afin que hibernate ne se plaignait pas Quand il voit, il n'y a pas de champ "displayName" dans la table de support (ou pire, essayez de créer ce champ si vous avez des mises à jour de schéma automatiques activées)?


@Aroth Vous avez peut-être raison, mais je ne suis pas sûr que si c'est nécessaire dans Hibernate 5. Je ne vous avertiez pas d'avertissement lorsque je gère mon programme, il n'y a pas de colonne supplémentaire créée, et je ne vois rien d'explicite dans l'hibernate 5 documents.


@Aroth je l'ai ajouté parce que je sentais que c'était plus clair, peu importe peu. Je ne sais toujours pas si c'est nécessaire ou non. Cela pourrait valoir une autre question Stackoverflow. Merci pour la suggestion!


J'aime l'élégance de votre méthode transitoire, mais cela nécessite que les objets soient chargés, et ceux-ci ne seront souvent pas nécessaires (par exemple, dans l'exemple de poker artificiel, la visualisation de toutes les personnes de votre table n'auront pas besoin de chercher tout Ces enregistrements pour obtenir le nom d'affichage ...) Votre première suggestion, tout en étant aussi élégante, semble faire le travail effectué. Je verrai sur le test de tester ce demain pour voir si cela fonctionne comme prévu, et évidemment un exemple de HQL est préféré mais non requis. J'apprécie grandement votre réponse et je vous ferai savoir comment il a secoué!


@Heeneenee - pourrait avoir raison sur Hibernate 5. Je n'ai jamais essayé avec quoi que ce soit de 4,3,6 (qui se plaint définitivement si le @Transient n'est pas là).


Dans Hibernate 3 & 4, les annotations sont extraites des champs ou des getters. Vous ne pouvez pas les mélanger. Cela signifie que si vous annotant les champs, vous pouvez ajouter une méthode sans grand effet. Toutefois, si vous avez annoté vos getters, puis ajoutez un getter transitoire (même sans champ), alors Hibernate s'attendra à une colonne de DB correspondante. Réponse courte: Si vous avez annoté les getters, vous avez besoin d'une annotation @Transient. Si vous avez annoté vos champs, vous ne le faites pas.



1
votes

Hibernate formule S est une solution élégante pour cela, mais ils peuvent être difficiles à obtenir si elles sont complexes (Hibernate a une capacité limitée à analyser SQL). De plus, même si vous les obtenez de fonctionner correctement, les performances peuvent souffrir, car les formules peuvent impliquer des sous-soluies qui sont toujours intégrées à la finale SQL.

L'approche que j'utilise est souvent de créer une vue de base de données qui fait que les jointures nécessaires pour moi Et puis je mappe mon entité à la vue. La base de données est capable d'optimiser la vue beaucoup mieux que les sous-candidatures imbriquées et Java Code est un peu plus propre, car il n'y a pas de SQL natif de l'intérieur.

Donc, vous pouvez créer une vue db Membres_profile < / Code> et une entité réutilisable mappée à celle-ci, disent MembresProfile . xxx

alors, vous pouvez associer cette entité avec des profils spécifiques, par exemple: xxx

Le bénéfice de cette approche est que vous chargez que vous chargez le displayName (et tout autre MembresProfile propriétés) paresseusement lorsque vous Besoin d'eux (évitant ainsi le coût d'exécution de la vue ou des sous-requêtes).

Une alternative à la création de la vue de base de données est hibernate sous-sélection s qui sont une sorte de vues hibernate (en lecture seule), mais je m'attends à ce que de vraies vues de DB effectuent beaucoup mieux. < / p>


2 commentaires

Cela aurait bénéficié d'être très bas niveau - Hibernate ne connaîtrait pas la différence et je pourrais tirer parti de l'optimisation complète du côté SQL pour la saveur que je cours. En fait, je n'ai même pas besoin de le mettre sur le profil de l'adhésion - il pourrait rester sur le profil d'utilisateur comme s'il est maintenant, a un profil de poker sans nom d'affichage et avoir une vue qui enveloppe les deux.


@corsika True, vous pouvez envelopper dans la vue tout ce qui convient le mieux à vos affaires d'utilisation et à vos exigences de performance. Les inconvénients de définir la vue sur pokerprofile est qu'il va toujours exécuter lorsque vous chargez pokerprofile des instances et que vous devriez définir une nouvelle vue pour l'autre * Profil Utiliser des cas (par exemple BlackjackProfile , etc.).



2
votes

Si nous pensons au domaine donné en termes de manière orientée objet / relationnelle, nous pouvons conclure la relation suivante:

pokerprofile hérite userprofile

Vous pouvez obtenir la même relation avec JPA et SGBM à l'aide de " une table par sous-classe héritage " et relations clés étrangères respectivement.

Donc, vous pouvez définir quelque chose comme ci-dessous:

userprofile.java xxx

pokerprofile.java xxx

L'avantage de ce modèle est qu'il exprime clairement le fonctionnalité souhaitée et bien sûr clairement synchronisée avec le profil parent. Il n'y a pas de solution de contournement ni de modification requise. Et sous la hotte, il utilise une relation de clé étrangère entre PokerProfile et UserProfile et donc aucune redondance de données.


2 commentaires

Le profil de poker n'hérite d'aucune manière du profil utilisateur. S'il y a hérité, je ne pourrais pas avoir un ussurofile qui souhaitait jouer au poker et au blackjack sans avoir un identifiant différent. J'aime la pensée extérieure de la boîte, mais malheureusement, je ne pense pas que cela s'applique ici.


J'ai donc commencé à penser à cela et je me suis rendu compte que je veux que PokerProfile hériter de quelque chose, mais pas de UserProfile. Cette classe existe comme une représentation mondiale d'une personne réelle, tandis que leurs citadres ne représentent que choisir de rejoindre un module spécifique. Il n'y a pas de comportement partagé entre un pokerprofile et un ussurofile, mais il y aurait entre un pokerprofile et un blackjackprofile. J'ai donc fait un abstractprofile (en tant que @MapsuPlass) pour le poker et le blackjack à hériter et mettre l'annotation ci-dessus. Encore une fois, cependant, j'aime la pensée ici, ça ne me convient pas.