Création de clé étrangère dans une structure one-to-one table par béton-class

TL; DR Comment puis-je imposer la création de schéma Hibernate pour créer une contrainte de clé étrangère dans une installation table-par-béton-classe de AbstractProperty.ownerId à Owner.ownerId pour la structure affichée ci-dessous, sans append de propriété Owner à AbstractProperty ?

Je travaille sur un projet où j’ai la structure de classe suivante:

Exemple de structure de classe

Le Owner a un mappage AnotherProperty avec un AbstractProperty , qui est étendu par la classe ConcreteProperty (et d’autres comme AnotherProperty , mais ce n’est pas vraiment pertinent pour la suite de cette question).

AbstractProperty n’a en réalité qu’une seule propriété, abstractPropertyId . Pour ce faire, nous souhaitons utiliser la structure table-par-béton-classe , qui AnotherProperty aux tables Owner , ConcreteProperty et aux tables des autres classes d’extension AbstractProperty ( AnotherProperty ).

À cette fin, j’ai créé le mappage suivant pour le Owner :

            

Et pour la AbstractProperty :

       ownerId           

Cela marche.

Cependant, et voici ma question: utiliser ce mappage et create Hibernate créer le schéma pour moi ( create ), il ne crée pas de contrainte de clé étrangère à partir de ConcreteProperty.ownerId champ de firebase database au champ Owner.ownerId . Cela se produit lorsque je crée le champ un à un contraint de AbstractProperty en Owner aide de ce mappage pour AbstractProperty (où le champ owner est de type Owner dans la classe java AbstractProperty ):

       ownerId            

Comment puis-je imposer la création d’une clé étrangère de AbstractProperty.ownerId à Owner.ownerId sans ce champ Owner dans mon AbstractProperty ?

Réponse simple: Ne laissez jamais Hibernate créer le schéma pour des applications réelles.

Hibernate est un mappeur relationnel d’objects et il convient de le traiter comme tel.

Le fait que Hibernate crée également des schémas est tout au plus gentil pour la première fois. Mais dans les environnements postérieurs à la première version, vous ne souhaitez pas que Hibernate contrôle le schéma. Après tout, vous devez gérer SQL pour disposer de scripts de migration (manuellement ou avec un support d’outil). Après la première publication, vous aurez des données dans la firebase database. Pour garantir la migration des données sur le système de production avec moins de problèmes, vous devez considérer la migration des schémas et des données de la même manière dans l’environnement de production que dans votre environnement de développement.

Les exceptions à cette règle peuvent être toute application dont les données changent rarement et qui peuvent être reconstituées rapidement en cas de perte de données.

Nous utilisons la norme JPA (pas de piratage spécifique à Hibernate) et avons le même problème, et nous n’avons pas trouvé de solution intéressante.

Hypothèses:

  1. AbstractProperty est une classe d’un package qui est réutilisable / partagée dans différentes applications et que vous ne voulez pas de référence à une classe de Owner spécifique à une application.
  2. ConcreteProperty & AnotherProperty est spécifique à l’application.

Dans ce cas, la solution serait de mettre des références dans ConcreteProperty au Owner (avec des clés étrangères), éventuellement avec AnotherProperty étendant le même ApplicationProperty et rendant privé le abstractPropertyId de sorte qu’il soit automatiquement défini lors du paramétrage du propriétaire.

Cela ne fonctionnerait-il pas automatiquement si vous définissez l’atsortingbut Owner sur la propriété Abstract comme étant ” transitoire “?

Les variables peuvent être marquées comme transitoires pour indiquer qu’elles ne font pas partie de l’état persistant d’un object.

Et si vous implémentez votre propre sérialisation manuelle, vous pouvez vérifier le modificateur sur un champ et l’ignorer -> problème de sérialisation cyclique évité.

La seule autre façon que je vois est de pousser l’atsortingbut Owner vers chacune des classes Property concrètes et de changer votre correspondance en

    ownerId            

ce qui crée le sql suivant:

 create table AnotherProperty ( ownerId integer not null, anotherProperty varchar(255), primary key (ownerId) ) create table ConcreteProperty ( ownerId integer not null, concreteProperty varchar(255), primary key (ownerId) ) create table Owner ( ownerId integer generated by default as identity, ownerProperty varchar(255), primary key (ownerId) ) alter table AnotherProperty add constraint FK_ceq89n6x2i1ax18bb4gqpq4m5 foreign key (ownerId) references Owner alter table ConcreteProperty add constraint FK_i41buhvtxxtpsim2cc0ur1gxr foreign key (ownerId) references Owner 

Premièrement: Hibernate / JPA est capable de gérer un grand nombre de scénarios. S’il y avait vraiment beaucoup de gens qui essayaient la même approche que vous, je penserais que cela aurait déjà été fait – il ne s’agit pas d’un poulet de spring. ** c’est un indice 😉 **

Deuxièmement: avoir une table appelée ‘Propriétaire’ avec un ‘propriétaireProperty’ est un autre indice. Les noms impliquent une relation intégrée.

Troisièmement: simplement en déclarant que vous ne voulez pas de propriété propriétaire dans la table AbstractProperty, cela ouvre la voie à une erreur logique communément appelée catch-22 ( http://en.wikipedia.org/wiki/False_dilemma ). .

Mon sharepoint vue -> Cela semble être davantage un problème de modélisation / conception qu’un problème technique / cadre.

Ma suggestion serait de prendre du recul par rapport au problème et de le réévaluer. Par exemple, si vous écriviez simplement des requêtes directes avec spring-jdbc, comment envisagez-vous d’interagir avec les données pour les opérations SELECT, UPDATE et DELETE? … si vous travaillez à travers ceux-ci, votre solution / vos besoins se présenteraient probablement plus clairement. Pour être encore plus précis, à quoi pensez-vous que le comportement sera sur une suppression en cascade? Si j’émets une instruction DELETE sur un seul enregistrement Propriétaire, souhaitez-vous que la firebase database supprime automatiquement les enregistrements des tables enfants? récursivement? Une fois que cela est isolé, vous pouvez trouver un moyen de dire à Hibernate quoi faire: ne laissez pas les membres de l’équipe mettre la charrue avant les boeufs en limitant prématurément la solution.

Par exemple, (cas 1), si vous avez réellement affaire à une “propriété” d’un propriétaire, il serait raisonnablement prévisible que vous deviez stocker plusieurs propriétés relatives à un propriétaire (aka: OneToMany).

Alternativement, (cas 2), si vous avez affaire à un “type” de propriétaire (comme dans un champ discriminant), alors votre table “AbstractProperty” devrait étendre le propriétaire … Dans ce cas, vous pourrez peut-être réduire votre solution. jusqu’à 3 tables (propriétaire avec un discriminateur, Concrete1 w / ownerId, Concrete2 w / ownerId).

Solution proposée 1: Dans l’un ou l’autre de ces cas, il serait toujours logique que la table ‘AbstractProperty’ fasse référence à son parent / propriétaire. Et si c’est le cas, je pense que Cascading DELETES peut fonctionner comme vous le préféreriez.

Solution proposée 2: Si toutefois, dans un scénario de suppression en cascade d’un enregistrement de propriétaire, vous préféreriez que la ou les lignes de AbstractProperty restnt en tant que données de référence, vous pouvez alors faire valoir que vous devez placer un tableau supplémentaire entre Propriétaire et AbstractProperty pour protéger vos données de référence … en tant que table de mappage dotée d’une clé composite unique.

Concentrez-vous sur les besoins de l’entreprise et les histoires d’utilisateurs, ce qui devrait, espérons-le, guider votre choix des solutions disponibles.