2009-10-05 25 views
7

Tengo tablas heredadas en una base de datos Oracle a las que me gustaría acceder desde una aplicación Java con Hibernate. El problema es que las tablas no tienen buenas claves primarias. Por ejemplo, una tabla se vería así:Tabla de herencia de Oracle sin buena PK: ¿Cómo hibernar?

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); 

El diseñador original decidió utilizar la columna de la hired_at como un campo de estado, pensando que la base de datos nunca tendría que distinguir entre el John Smith fuera de la empresa, pero que los empleados con el mismo nombre podrían identificarse por su fecha de contratación. Obviamente, eso crea una clave principal que es parcialmente anulable y modificable. Dado que la base de datos no permitiría esto como clave principal, en su lugar utilizó un índice único.

Ahora, si trato de escribir un mapeo de Hibernate para esto, que podría llegar a algo como esto:

<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> 

Esto funciona para la lectura de datos, pero cuando i crear nuevas entradas que se diferencian sólo en su hiredAt date, me encuentro con org.hibernate.NonUniqueObjectExceptions. Alternativamente, podría considerar la característica ROWID de Oracle:

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

Esto funciona bien para leer datos, también. Pero, obviamente, no se pueden persistir objetos nuevos, ya que Hibernate ahora arroja una org.hibernate.exception.SQLGrammarException: no se pudo obtener el siguiente valor de secuencia al intentar almacenar la entidad.

La forma obvia de salir de esto es a través de claves sustitutivas. Sin embargo, eso requeriría que se cambiara el esquema de la base de datos, lo que nos lleva de regreso a esa cosa "heredada".

¿Qué estoy pasando por alto? ¿Hay alguna manera de hacer que Hibernate coopere con ROWID de Oracle, tal vez con un generador diferente? ¿O hay una manera de hacer que la primera idea funcione, tal vez con DAO inteligentes que desalojan y vuelven a cargar entidades, y ocultan la complejidad de la aplicación? ¿O debería buscar una alternativa a Hibernate, quizás Ibatis?

Respuesta

7

No desea utilizar ROWID como clave principal, ya que no se garantiza que sea estable durante la vida útil de una fila.

Agregar una clave sintética es la mejor opción. Use un disparador para poblar la columna con un valor de secuencia.

Usted es un poco vago en el aspecto legado, por lo que es difícil estar seguro de cuáles son sus implicaciones. Agregar una columna rompería cualquier instrucción de inserción que no enumere explícitamente las columnas de destino. También podría interrumpir las consultas SELECT * (a menos que seleccionen una variable declarada con la palabra clave %ROWTYPE. Pero no tendría que cambiar ninguna otra aplicación para usar la nueva clave principal en lugar de las existentes, a menos que realmente lo desee).

+0

Sí, parece que es la mejor idea. Todas las demás opciones parecen necesitar una magia DAO cruftastic. – wallenborn

+1

Oracle ROWID es" estable ". El único problema es cuando (cita): _Si elimina y vuelve a insertar una fila con la importación y exportación utilidades, por ejemplo, su rowid puede cambiar. Si elimina una fila, Oracle puede reasignar su rowid a una nueva fila insertada más tarde. – xmedeko

+0

@xmedeko - a propósito se omite la primera oración de ese párrafo "No debe usar ROWID como clave principal de una tabla. "Http://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns008.htm – APC

5

Agregaría una clave sustituta con una secuencia de respaldo. No cambiaría ninguna de sus semánticas heredadas y Hibernate estaría satisfecho.

Simplemente curioso: si el índice tiene nombre y apellido y fecha de contratación, ¿por qué su clave compuesta excluye la fecha de contratación? Hubiera esperado ver todos los campos en el índice en la clave compuesta también.

+0

Si hago eso, Hibernate devuelve valores nulos. Por lo tanto, si hay un (Smith, John, 2009-10-01) y un (Smith, John, nulo) y un (Smith, Jack, nulo), un queryon 'from Employee where lastName = 'Smith' 'devolvería tres entradas, una (Smith, John, 2009-10-01) y dos punteros nulos. Es por eso que convertí el campo anulable en una propiedad común, con la esperanza de poder para "arreglarlo en los DAO"." – wallenborn

Cuestiones relacionadas