2010-04-03 28 views
83

¿Cómo funciona la anotación @Version en JPA?Java - JPA - @Version Anotación

He encontrado varias respuestas cuyo extracto es el siguiente:

APP utiliza un campo de versión en sus entidades para detectar modificaciones concurrentes para el mismo registro del almacén de datos. Cuando el tiempo de ejecución de JPA detecta un intento de modificar al mismo tiempo el mismo registro, arroja una excepción a la transacción que intenta cometer el último.

Pero todavía no estoy seguro de cómo funciona.


también a partir de las siguientes líneas:

usted debe considerar campos de versión inmutable. Cambiar el valor del campo tiene resultados indefinidos.

¿Significa que deberíamos declarar nuestro campo de versión como final?

+0

único que hace es cheque/actualizar la versión en cada consulta de actualización: 'ACTUALIZACIÓN myEntity SET micolumna = 'Valor nuevo', version = Versión + 1 donde version = [old.version] '. Si alguien actualizó el registro, 'old.version' ya no coincidirá con el del DB y la cláusula where impedirá que la actualización ocurra. Las 'filas actualizadas' serán '0', que JPA puede detectar para concluir que ocurrió una modificación simultánea. –

Respuesta

145

Pero todavía no estoy seguro de cómo funciona?

Digamos que una entidad MyEntity tiene una anotada version propiedad:

@Entity 
public class MyEntity implements Serializable {  

    @Id 
    @GeneratedValue 
    private Long id; 

    private String name; 

    @Version 
    private Long version; 

    //... 
} 

En la actualización, el campo anotado con @Version se incrementará y se añade a la cláusula WHERE, algo como esto:

UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?)) 

Si la cláusula WHERE no coincide con un registro (porque la misma entidad ya ha sido actualizada por otro hilo), luego el proveedor de persistencia lanzará un OptimisticLockException.

¿Quiere decir que debemos declarar nuestro campo de versión como definitiva

No, pero se podía considerar la posibilidad de la incubadora protegida como que no se supone llamarlo.

+5

Obtuve una excepción de puntero nulo con este fragmento de código, porque Long se inicializa como nulo, probablemente debería inicializarse explícitamente con 0L. – Markus

+1

No confíe en auto-unboxing a 'long' o simplemente llamando' longValue() 'en él. Necesita verificaciones 'null' explícitas. Inicializándolo explícitamente, no haría, ya que se supone que este campo debe ser administrado por el proveedor de ORM. Creo que se establece en '0L' en la primera inserción en la base de datos, por lo que si se establece en' nulo' esto le indica que este registro no se ha conservado aún. –

+0

¿Esto evitará que se cree (intente) crear una entidad más de una vez? Quiero decir supongamos que un mensaje de tema JMS desencadena la creación de una entidad y hay varias instancias de una aplicación que escucha el mensaje. Solo quiero evitar el error de violación de restricción único. – Manu

8

Cada vez que se actualiza una entidad en la base de datos, el campo de versión se incrementará en uno. Cada operación que actualice la entidad en la base de datos tendrá como anexo WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE a su consulta.

En comprobación de filas afectadas de la operación del marco de la APP puede asegurarse de que no hubo modificación concurrente entre la carga y la persistencia de su entidad porque la consulta no encontraría su entidad en la base de datos cuando es el número de versión se ha incrementado entre la carga y persistir .

+0

No a través de JPAUpdateQuery no. Por lo tanto, "toda operación que actualice la entidad en la base de datos tendrá anexada WHERE versión = VERSION_THAT_WAS_LOADED_FROM_DATABASE a su consulta" no es verdadera. –

1

Versión utilizada para garantizar que solo se realice una actualización a la vez. El proveedor de JPA verificará la versión, si la versión esperada ya ha aumentado, entonces otra persona ya actualizará la entidad para que se genere una excepción.

Así que actualizar el valor de la entidad sería más seguro, más optimista.

Si el valor cambia con frecuencia, puede considerar no utilizar el campo de versión. Para un ejemplo "una entidad que tiene campo de contador, que se incrementará cada vez que una página web accesible"

15

Aunque respuesta @Pascal es perfectamente válido, desde mi experiencia puedo encontrar el código de abajo útiles para llevar a cabo el bloqueo optimista:

@Entity 
public class MyEntity implements Serializable {  
    // ... 

    @Version 
    @Column(name = "optlock", columnDefinition = "integer DEFAULT 0", nullable = false) 
    private long version = 0L; 

    // ... 
} 

¿Por qué? Porque:

  1. Bloqueo optimista no va a funcionar si el campo anotado con @Version accidentalmente se configura null.
  2. Como este campo especial no es necesariamente una versión comercial del objeto, para evitar un error, prefiero llamar a este campo como optlock en lugar de version.

Primer punto no importa si la aplicación utiliza solamente APP para insertar datos en la base de datos, como proveedor JPA hará cumplir 0 para @version campo durante la creación. Pero casi siempre las sentencias SQL simples también están en uso (al menos durante las pruebas de unidad/integración).

+0

Lo llamo ** 'u_lmod' ** (último usuario modificado) donde ** el usuario puede ser un proceso/entidad/aplicación humana o definida (automatizada) ** (por lo que el almacenamiento adicional de' u_lmod_id' también podría tener sentido). (En mi opinión, es un meta-atributo comercial muy básico). Por lo general, almacena su valor como FECHA (HORA) (lo que significa información sobre el año ...millis) en UTC (si la "ubicación" de la zona horaria del editor es importante almacenarla con la zona horaria). adicionalmente muy útil para escenarios de sincronización DWH. –

+4

Creo que debe usarse bigint en lugar de un entero en tipo db para que coincida con un tipo java largo. – Dmitry

+0

Buen punto Dmitry, gracias, aunque cuando se trata de versionar en mi humilde opinión realmente no importa. –

0

Solo agrego un poco más de información.

JPA gestiona la versión bajo el capó para usted, sin embargo, no lo hace al actualizar su registro a través de JPAUpdateClause(), en tales casos debe agregar manualmente el incremento de la versión a la consulta.

Pedro

+1

¿Qué es 'JPAUpdateClause'? –