2011-11-03 10 views
7

Tenemos un disparador de inserción anterior que obtiene el siguiente valor de la secuencia. Cuando el objeto persiste con el método save(), hibernate obtiene el valor de la secuencia y lo agrega al objeto. y cuando la transacción se compromete desde la capa de servicio de Spring, el valor de ID se incrementa de nuevo en la base de datos. ¿Cómo evito conseguir nextval() si el objeto ya tiene un id ..Problema de HIbernate con Oracle Trigger para generar id a partir de una secuencia

Esto es lo que ma tratando de hacer ..

UserDao

public User saveUser(User user){ 
     session.getCurrentSession.save(user);//line2 
     return user;//line3 
} 

UserService

public void saveUserAndWriteToAudit(User user, UserAudit userAudit){ 
    userDao.saveUser(user);//line1 
    userAudit.setUserId(user.getId);//line4 
    userAudit.saveUserAudit(userAudit);//line5 
} 

Y la clase de usuario

@Entity 
    public class User{ 

    @Id 
    @GeneratedValue(strategy=GenerationType.AUTO, generator="a1") 
    @SequenceGenerator(name="a1", sequenceName="usersequence") 
    private Long id; 
    ///////////////// 
} 

Cuando el cursor llega a la línea 1 y el objeto de usuario de la línea 2 tiene nulo en el atributo id. después de la línea2, tiene nextval de la secuencia, digamos 1. en la línea4, he agregado la identificación del usuario = 1 para usar el objeto ... cuando la transacción se confirma después de que la línea 5, 2 se inserta en la columna de identificación del usuario y 1 en la columna userId de UserAudit. Esto no sirve para nada :(¿Cómo evito este problema? ¡Gracias!

Respuesta

9

Simplemente actualice su disparador para disparar solo cuando no se le dé una identificación.

create or replace 
trigger sa.my_trigger 
before insert on sa.my_table 
for each row 
when (new.id is null) 
begin 
    select sa.my_sequence.nextval 
    into :new.id 
    from dual; 
end; 
+0

gracias matthew .. – RKodakandla

0

Idealmente eliminarías el ANTES DE INSERTAR EL DISPARO. Si no lo haces, Hibernate no tiene forma de saber la clave principal, y realmente necesita esa información. Si no le importa el objeto después del INSERT que podría estar bien (el segundo nivel de caché sigue siendo un problema), pero si necesita usarlo de inmediato es un problema real. En este último caso, podría probar este enfoque desagradable:.

  1. Dile a Hibernate que la gestión de la clave principal a sí mismo
  2. crear el nuevo objeto y poner un valor arbitrario en la clave principal
  3. .
  4. Enjuague la sesión de Hibernate y SELECCIONE la secuencia.CURRVAL (suponiendo que solo haya un INSERT).
  5. Cargue el objeto utilizando el valor de secuencia actual obtenido y no utilice la instancia anterior.
+0

@FelixM .. gracias por la respuesta ... si modificamos el disparador para verificar: new.id = null o no, ¿funcionaría? .. No puedo probarlo porque no tengo privilegios de db para modificar el disparador Necesito asegurarme de que sea suficiente antes de pedirle a dba que lo modifique – RKodakandla

+0

Si modifica el disparador como se describe en la otra respuesta, puede indicarle a Hibernate que use la secuencia. – FelixM

5

La solución anterior es genial, me ha ahorrado muchos dolores de cabeza en este problema.

Mi única queja es que abre la puerta a los usuarios/códigos para insertar cualquier valor de ID sin consultar la secuencia.

He encontrado que la siguiente solución también funciona. Permite que Hibernate encuentre el ID máximo y lo incremente cada vez que se ejecuta una declaración de inserción. Pero cuando llega a la base de datos, el ID es ignorada y sustituida 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 la APP. Tampoco está claro para los programadores que esta identificación esté realmente vinculada a una secuencia, mientras que, de hecho, es una de las soluciones acopladas más ajustadas que usan una secuencia.

+0

Gracias Christopher, estaba luchando para utilizar el desencadenador de base de datos para llenar la clave principal y esto resolvió el problema. – user75ponic

0
create or replace 
trigger sa.my_trigger 
before insert on sa.my_table 
for each row 
when (new.id is null) 
begin 
    select sa.my_sequence.nextval 
    into :new.id 
    from dual; 
end; 

esto es algo realmente incorrecto del mundo de la base de datos.

Considerando la solución anterior, digamos sa.my_sequence.nextval es 51 y su marco de hibernación funcionará sin problemas. Pero si alguien inserta jdbc directamente con su valor de clave principal como 66 sobreescribiendo la secuencia (digamos, el valor actual como 52), el disparador simplemente se insertará.

El verdadero problema es cuando el valor de la secuencia se incrementa a 66, lo que generará una excepción en el desencadenante al restablecer el valor de la secuencia. Esto realmente termina en una mala estructura de diseño de esquema desde el lado de la base de datos.

Cuestiones relacionadas