2009-10-05 4 views
7

J'ai des tables héritées dans une base de données Oracle auxquelles j'aimerais accéder à partir d'une application Java avec Hibernate. Le problème est le suivant: les tables n'ont pas de bonnes clés primaires. Par exemple, une table ressemblerait à ceci:Table Oracle ancienne sans PK bon: Comment Hiberner?

create table employee (
    first_name varchar(64) not null, 
    last_name  varchar(64) not null, 
    hired_at  date, 
    department varchar(64) 
); 
create unique index emp_uidx on employee (firstname, lastname, hired_at); 

Le concepteur original a décidé d'utiliser la colonne hired_at comme un champ d'état, pensant que la base de données aurait jamais besoin de faire la distinction entre le John Smith en dehors de la société, mais les employés portant le même nom pourraient être identifiés par leur date d'embauche. Évidemment, cela crée une clé primaire partiellement nulle et modifiable. Comme la base de données ne l'autorisait pas en tant que clé primaire, il utilisait un index unique à la place.

Maintenant, si je tente d'écrire un mapping Hibernate pour cela, je pourrais venir avec quelque chose comme ceci:

<hibernate-mapping> 
    <class name="com.snakeoil.personnel" table="employee"> 
    <composite-id class="com.snakeoil.personnel.EmpId" name="id"> 
     <key-property name="firstName" column="first_name" type="string"/> 
     <key-property name="lastName" column="last_name" type="string"/> 
    </composite-id> 
    <property name="hiredAt" column="hired_at" type="date"/> 
    <property name="department" type="string"> 
    </class> 
</hibernate-mapping> 

Cela fonctionne pour les données de lecture, mais quand je crée de nouvelles entrées qui ne diffèrent que par leur HiredAt date, je cours dans org.hibernate.NonUniqueObjectExceptions. Alternativement, je pourrais envisager la fonctionnalité ROWID d'Oracle:

<id column="ROWID" name="id" type="string"> 
    <generator class="native"/> 
</id> 

Cela fonctionne très bien pour lire des données, aussi. Mais évidemment, vous ne pouvez pas persister de nouveaux objets, car Hibernate lance maintenant une exception org.hibernate.exception.SQLGrammarException: n'a pas pu obtenir la valeur de la séquence suivante en essayant de stocker l'entité.

La solution la plus évidente consiste à utiliser des clés de substitution. Cela nécessiterait toutefois de modifier le schéma de la base de données, ce qui nous ramène à cette situation «patrimoniale».

Que suis-je en train de négliger? Existe-t-il un moyen de faire coopérer Hibernate avec le ROWID d'Oracle, peut-être avec un générateur différent? Ou y a-t-il un moyen de faire fonctionner la première idée, peut-être avec des DAOs intelligents qui expulsent et rechargent beaucoup d'entités, et cachent la complexité de l'application? Ou devrais-je chercher une alternative à Hibernate, Ibatis peut-être?

Répondre

7

Vous ne voulez pas utiliser ROWID comme clé primaire, car il n'est pas garanti qu'il soit stable pendant la durée de vie d'une ligne. L'ajout d'une clé synthétique est le meilleur choix. Utilisez un déclencheur pour remplir la colonne avec une valeur de séquence.

Vous êtes un peu vague sur l'aspect héritage, il est donc difficile d'être certain que les implications. L'ajout d'une colonne romprait toute instruction d'insertion qui ne liste pas explicitement les colonnes cibles. Il pourrait aussi casser les SELECT * requêtes (à moins qu'ils choisissent dans une variable déclarée en utilisant le mot-clé %ROWTYPE Mais vous ne devez changer toute autre application il a utilisé la nouvelle clé primaire au lieu des colonnes existantes -. Sauf si vous voulez vraiment.

+0

Oui, semble que c'est la meilleure idée.Toutes les autres options semblent exiger une magie DAO cryptographique – wallenborn

+1

Oracle ROWID est" stable "Le seul problème est quand (citation): _Si vous supprimez et réinsérez une ligne avec l'importation et l'exportation Utilitaires, par exemple, alors son rowid peut changer.Si vous supprimez une ligne, alors Oracle peut réaffecter son rowid à une nouvelle ligne insérée plus tard._ – xmedeko

+0

@xmedeko - l'omis volontairement la première phrase de ce paragraphe "Vous ne devriez pas utiliser ROWID comme la clé primaire d'une table. "Http://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns008.htm – APC

5

Je voudrais ajouter une clé de substitution avec une séquence de sauvegarde. Cela ne changerait rien à votre sémantique héritée et Hibernate serait satisfait.

curiosité - si l'indice a les noms et prénoms et date d'embauche, pourquoi votre clé composite exclure date d'embauche? Je m'attendais à voir tous les champs de l'index dans la clé composite aussi.

+0

Si je fais ça, Hibernate renvoie des valeurs nULL. donc, s'il y a un (Smith, John, 2009-10-01) et un (Smith, John, null) et un (Smith, Jack, null), un queryon 'de l'employé où lastName = 'Smith'' rapporterait trois entrées, un (Smith, John, 2009-10-01) et deux pointeurs nuls. Voilà pourquoi je l'ai fait le champ annulable une propriété ordinaire, en espérant que je serais en mesure "réparer dans les DAO." – wallenborn

Questions connexes