2011-08-18 11 views
7

Tengo una configuración con Oracle XE 10g, Hibernate 3.5, JPA 2.0. Hay una tabla simple con una clave principal, que se genera mediante un activador de base de datos en la inserción. El disparador obtiene el valor de una secuencia. La construcción del disparador/secuencia fue hecha por Oracle XE.¿Cómo anoto la columna Id si el valor es generado por un disparador?

La pregunta real es: ¿Cómo obtengo el valor actual del Id en mi entidad después de un EntityManager.persist?
me trataron:

@Id 
private long id; 

-> id es 0;

@Id 
@Generated(GenerationTime.ALWAYS) 
@Column(insertable = false, updatable = false) 
private long id; 

-> id es 0;

@Id 
@GeneratedValue(strategy=GenerationType.AUTO) 
private long id; 

-> Error porque Hibernate está intentando consultar la secuencia a directamente (lo que no existe).

Los ID se generan en la base de datos en los primeros dos enfoques, pero no tengo el valor en mi objeto.

Respuesta

2

A menos que esté vinculado al activador, parece un nivel de ofuscación sobre la secuencia y parece salirse del ciclo de vida normal de Hibernate. ¿Por qué no llamar directamente a la secuencia:

@SequenceGenerator(name="alias_for_my_sequence", sequenceName="seq_name_in_oracle") 
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="alias_for_my_sequence") 
@Id 
private Long id; 

por lo que recibirá el valor de nuevo, como Hibernate está directamente implicada en la generación, y no es algo que sucede después de los hechos.

+0

Gracias, pero: el factor desencadenante es la forma de Oracle de generar automáticamente valores PK en aumento. Si me deshago de eso, perderé la posibilidad de insertar filas fácilmente con consultas directas en la base de datos. Y segundo, si anoto como en su sugerencia, no puedo cambiar fácilmente a Derby para pruebas unitarias: (... Lo que necesito es una anotación que diga: "Simplemente tome lo que el DB ha generado". – Zeemee

+0

Hago un examen de Google en este punto, y en base a un examen superficial, parece que otros usuarios han tenido éxito refiriéndose a las secuencias de Oracle de Hibernate. He aquí un ejemplo: https://forum.hibernate.org/viewtopic.php?p=2442821. En el tema de Derby, unidad las pruebas en una plataforma de base de datos diferente de la plataforma de implementación no son una práctica recomendada y podrían generar inconsistencias entre las pruebas unitarias y el comportamiento en tiempo de ejecución. – atrain

+2

@Mulmoth: a menos que haya otro campo que identifique la fila de forma exclusiva, ¿cómo lo haría Hibernate? La identificación generada vuelve de la base de datos, ya que la única forma de identificar la fila que acaba de insertarse es su ID, que no conoce. En resumen, la ID es necesaria para obtener la ID. Su desencadenante es realmente una mala idea –

1

Puede intercambiar la clave generada db dentro de la sesión de Oracle. Para ello, el desencadenador de inserción debe llamar

dbms_session.set_identifier(new_id) 

Desde su código, puede recuperar el nuevo valor de la clave con la consulta

String sql = "SELECT sys_context('USERENV', 'CLIENT_IDENTIFIER') FROM dual"; 
Query query = em.createNativeQuery(sql); 
String new_id = (String) query.getSingleResult(); 

Es importante utilizar la misma instancia de EntityManager por persistir la nueva entidad y recuperar el valor clave generado.

7

Al contrario de los comentarios de @JB Nizet, puedo pensar en muchas razones por las que dejamos que un desencadenador asigne identificadores: ejecución de procesos almacenados, ejecución manual de SQL y ejecución de consultas nativas en Hibernate, por nombrar algunos.

Personalmente encontré la siguiente solución bastante satisfactoria. Permite que Hibernate encuentre la ID máxima y la incremente cada vez que se invoca una instrucción de inserción. Pero cuando la declaración golpea la base de datos, el ID es ignorado y anulado por la generada por el gatillo, así que no hay uniqueness in a cluster problem:

@Id 
    @GeneratedValue(generator="increment") 
    @GenericGenerator(name="increment", strategy = "increment") 
    private Long id; 

El mayor inconveniente es @GenericGenerator es una anotación de Hibernate, por lo que se pierde la portabilidad de APP . Tampoco está claro para los programadores que esta identificación esté realmente vinculada a una secuencia.

Otra alternativa es modificar el disparador para que solo incremente la secuencia cuando ID es nulo. Ver "Hibernate issue with Oracle Trigger for generating id from a sequence". En la mayoría de los casos, esta es la mejor solución porque muestra claramente que la identificación está vinculada a una secuencia. Mi única queja es que le da al usuario/hibernar la opción de insertar cualquier ID sin consultar la secuencia en primer lugar.

+0

Por favor, eche un vistazo a este ejemplo: http://developer-should-know.com/post/82479486933/how-to-use-oracle-before-insert-trigger-for-id Muestra cómo implementar un custome hibernate generador que recupera el inserto de publicación de identificación. –

Cuestiones relacionadas