2010-06-23 6 views
4

Ma base de données a deux tables, "question" et "champ". Les questions peuvent avoir plusieurs champs, et les champs peuvent avoir plusieurs champs. C'est un arbre avec un noeud racine spécial.Hibernate, SQL et les associations récursives

Je veux les utiliser avec mise en veille prolongée (actuellement potgresql) - il devrait donc être simple et facile à utiliser de java.

Quelle est la meilleure solution à cela?

  1. ajouter question_parent_id et field_parent_id à la table "sur le terrain", et utiliser uniquement question_parent_id si elle est un descendant direct de celui-ci. (vérifiez XOR quelle contrainte SQL ... peut dépendre du serveur SQL)
  2. ajoutez question_parent_id et field_parent_id, et utilisez toujours question_parent_id. Souvenez-vous de rester cohérent ... (question_id ne devrait pas changer, probablement pas un risque réel)
  3. Utilisez l'héritage de table spécifique postgresql: « question » et « champ » étend « contenu », donc une colonne clé étrangère suffit. Utilisez une contrainte supplémentaire sur "question" et "champ".
  4. utilisez une troisième table (appelée "conteneur"), constituée uniquement d'un ID. Les conteneurs peuvent avoir plusieurs champs et un champ peut avoir un conteneur. Les questions ont exaclty un conteneur. Mais cela nécessite un code supplémentaire en Java, et a un risque de boucles infinies, même avec une clé unique sur field_container_id ...
+0

J'ai ajouté quelques paragraphes supplémentaires à ma réponse. Vous devez fournir des informations plus spécifiques pour obtenir des réponses spécifiques. (Pensez à poser une nouvelle question si vous trouvez quelque chose de spécifique à demander.) –

Répondre

2

Je préfère penser au modèle de classe plutôt qu'au modèle relationnel. L'utilisateur à la fin ne se soucie pas (habituellement) combien de clés vous avez dans votre base de données. Il utilise vos cours, où il devrait être "simple et facile à utiliser". Alors écrivez d'abord votre modèle de classe et réfléchissez à la cartographie plus tard.

La solution dans la base de données dépend de votre modèle de classe.

Modifier: Votre modèle de l'autre côté dépend de ce que vous devez faire.

Navigation: Avez-vous habituellement besoin de tous les champs d'une question? Avez-vous habituellement besoin seulement des champs directement assignés à une question ou un champ ou tout le champ récursivement dans l'arbre? Avez-vous besoin de connaître le parent d'un domaine? etc., etc.

Requêtes: Avez-vous besoin de filtrer des questions ou des champs par des champs qui leur sont assignés? Recursivement? Avez-vous besoin de filtrer les champs par parent?

En d'autres termes: vous ne pouvez pas optimiser pour tout. Il y a des requêtes typiques et des chemins de navigation typiques. Soutenir un trop grand nombre de méthodes pourrait devenir coûteux et nécessiter des données redondantes tant dans le modèle que dans la base de données, ce qui le rend difficile à maintenir.

+0

+1 pour respecter le code client en tant qu'utilisateur final afin de générer le meilleur design. :) –

0

À moins que je me manque quelque chose, vous avez un à plusieurs rapports entre [Question] et [Field] (c'est un un-à-plusieurs, n'est-ce pas?) et une auto-référence association un-à-plusieurs entre [Field]. Je voudrais donc:

  • ajouter un question_id à la table [Field] pour l'ex-relation
  • ajouter un parent_id à la table [Field] pour la relation plus tard

Mise en veille prolongée peut mapper ce sans aucun problème.

+0

ouais, ceci est mon # 1 ou # 2 :) – Dutow

+0

@Dutow Oui, mais je n'ai pas pu voir la différence (cela doit être évident pour vous :) –

+0

dans # 1: question.fields [x] .question_id! = NULL && question.fields [x] .fields [y] .question_id == NULL est toujours vrai pour tout x et y. dans # 2 question.fields [x] .question_id! = NULL && question.fields [x] .fields [y] .question_id! = NULL toujours vrai pour tout x et y. (# 2 peut être incohérent: champs [x] .fields [y] .parent_id! = Champs [x] .parent_id) – Dutow

0

Vous devez examiner attentivement la profondeur de votre hiérarchie si vous voulez une solution performante. Ce type de structure récursive peut être extrêmement coûteux pour le chargement d'hibernate car il finira souvent par créer un grand nombre de jointures pour chaque champ car il peut avoir des champs enfants (et ils peuvent avoir des champs enfants, etc.). vous voulez autoriser une profondeur infinie, mais vous voulez toujours charger toute la question, y compris tous les champs et sous-champs. Ensuite, je suggérerais que Field possède un champ parent (nullable) et une question propriétaire (non nullable). Cela vous permet de charger efficacement la question entière avec le HQL suivant:

SELECT q FROM Question q 
JOIN FETCH q.allFields 
Questions connexes