2011-03-11 5 views
7

¿Es posible cambiar una instancia de entidad versionada y obtener la versión que se va a incrementar sin usar flush? Porque, por lo que he leído, tengo miedo de que el color no sea una buena práctica porque tiene un impacto negativo en el rendimiento o incluso en la corrupción de datos. No estoy seguro: DFusionar y obtener la versión actualizada sin descarga?


Aquí hay un código simple y también la salida como el comentario:

/* 
    Hibernate: select receivingg0_.id as id9_14_, receivingg0_.creationDate as creation2_9_14_, ... too long 
    the version before modification : 16 
    the version after modification : 16 
    after merge the modification, the version is : 16 
    Hibernate: update ReceivingGood set creationDate=?, modificationDate=?, usercreate_id=?, usermodify_id=?, ... too long 
    after flushing the modification, the version is finally : 17 
*/ 
public void modifyHeaderAndGetUpdatedVersion() { 
    String id = "3b373f6a-9cd1-4c9c-9d46-240de37f6b0f"; 
    ReceivingGood receivingGood = em.find(ReceivingGood.class, id); 
    System.out.println("the version before modification : " + receivingGood.getVersion()); 

    receivingGood.setTransactionNumber("NUM001xyz"); 
    System.out.println("the version after modification : " + receivingGood.getVersion()); 

    receivingGood = em.merge(receivingGood); 
    System.out.println("after merge the modification, the version is : " + receivingGood.getVersion()); 

    em.flush(); 
    System.out.println("after flushing the modification, the version is finally : " + receivingGood.getVersion()); 
} 

En mi prueba, la versión obtuve incrementa después de la descarga. La instancia devuelta desde la operación de fusión no tiene la versión incrementada.

Pero en mi caso, me gustaría devolver la entidad a mi webui en forma de DTO, y la entidad debe tener la versión después del enjuague/confirmación antes de convertirla en DTO y devolverla a la interfaz de usuario para ser prestado Y luego la interfaz de usuario podría tener la última versión, y pasará esta versión para la próxima presentación.


¿Hay alguna manera en la que pueda obtener la última versión sin hacer flush?

¡Gracias!


ACTUALIZACIÓN


En mi experiencia, incrementando de este manual puede ser problemático, ya que se puede ver en este ejemplo a continuación. En este ejemplo, tenemos 2 descargas.

El primero es sincronizar los cambios a la conexión db, de modo que una llamada a procedimiento almacenado desde la misma conexión pueda ver los cambios realizados desde el entityManager.

Se llama a la segunda descarga para obtener la versión final. Y podemos ver que esto es incrementado dos veces. Por lo tanto, obtener la versión solo del incremento manual sin vaciado no funcionaría en esta condición, ya que tenemos que contar realmente cuántas descargas se están realizando.

/* 
Hibernate: select receivingg0_.id as id9_14_, receivingg0_.creationDate as creation2_9_14_, .. too long 
the version before modification : 18 
the version after modification : 18 
after merge the modification, the version is : 18 
now flushing the modification, so that the stored procedure call from the same connection can see the changes 
Hibernate: update ReceivingGood set creationDate=?, modificationDate=?, usercreate_id=?, .. too long 
after flushing the modification, the version is : 19 
Hibernate: update ReceivingGood set creationDate=?, modificationDate=?, usercreate_id=?, .. too long 
after the second flush, the version got increased again into : 20 
*/ 
public void modifyHeaderAndGetUpdatedVersionWith2Flushes() { 
    String id = "3b373f6a-9cd1-4c9c-9d46-240de37f6b0f"; 
    ReceivingGood receivingGood = em.find(ReceivingGood.class, id); 
    System.out.println("the version before modification : " + receivingGood.getVersion()); 

    //auditEntity(receivingGood, getUser("3978fee3-9690-4377-84bd-9fb05928a6fc")); 
    receivingGood.setTransactionNumber("NUM001xyz"); 
    System.out.println("the version after modification : " + receivingGood.getVersion()); 

    receivingGood = em.merge(receivingGood); 
    System.out.println("after merge the modification, the version is : " + receivingGood.getVersion()); 
    System.out.println("now flushing the modification, so that the stored procedure call from the same connection can see the changes"); 
    em.flush(); 
    System.out.println("after flushing the modification, the version is : " + receivingGood.getVersion()); 

    receivingGood.setTransactionNumber("NUM001abc"); 

    em.flush(); 
    System.out.println("after the second flush, the version got increased again into : " + receivingGood.getVersion()); 
} 

¿Esto significa que realmente no tienen que depender de la descarga al final para obtener la última versión modificada de la entidad?


ACTUALIZACIÓN 2


Aquí es un simple ejemplo de un método de servicio que actualizará la entidad ReceivingGood y debe devolver un DTO que tiene la versión más reciente.

public ReceivingGoodDTO update(ReceivingGood entity) { 
    // merge it 
    entity = entityManager.merge(entity); 

    // the version is not incremented yet, so do the flush to increment the version 
    entityManager.flush(); // if i dont do this, the dto below will get the unincremented one 

    // use a mapper, maybe like dozer, to copy the properties from the entity to the dto object, including the newest version of that entity 
    ReceivingGoodDTO dto = mapper.map(entity, dto); 

    return dto; 
} 

y aquí es un ejemplo que hace uso de ese método:

@Transactional 
public ReceivingGoodDTO doSomethingInTheServiceAndReturnDTO() { 
    // do xxx .. 
    // do yyy .. 
    dto = update(entity); 
    return dto; // and the transaction commits here, but dto's version isnt increased because it's not a managed entity, just a plain POJO 
} 

Respuesta

5

Volveré a recomendar leer más sobre Hibernate y cómo funciona, ya sea por su documentación o "Java Persistence with Hibernate".

Está viendo esto en la operación de descarga porque es allí donde se producen las actualizaciones de la base de datos. Si solo omites el color y comprometes la transacción, verás el mismo comportamiento. Es decir, siempre que termine su unidad de trabajo, Hibernate eliminará los cambios en la base de datos. Es en este paso que Hibernate compara y actualiza el número de versión. Si la versión de la base de datos es 17 y está actualizando la versión 16, Hibernate arrojará una excepción sobre la actualización de un objeto obsoleto.

Dicho esto, puede "predecir" cuál sería la próxima versión incrementando 1 en el valor que tiene actualmente en su instancia. Pero esto nunca será real, porque esto solo se incrementa de manera efectiva justo antes de actualizar el registro de la base de datos. Por lo tanto, cualquier cambio concurrente no será visible para su hilo, a menos que consulte la base de datos.

Editar:

Usted está viendo dos incrementos, ya que está haciendo dos descargas. La "versión" es una técnica para el bloqueo "optimista", es decir, espera que solo un hilo actualice un registro durante cualquier unidad de trabajo (en un sentido más amplio, piense como una "acción del usuario", donde enumera el registros, elija uno y lo actualiza). Su propósito es principalmente evitar que Hibernate actualice un objeto obsoleto, donde dos usuarios eligen el mismo registro para editarlo, hacen algunos cambios concurrentes y lo actualizan. Una de las ediciones tendrá que ser rechazada, y la primera que llegue a la base de datos gana. Como está actualizando el registro dos veces (llamando dos veces al color), verá dos incrementos. Pero realmente no se trata de rubores, se trata de "actualizaciones emitidas en la base de datos". Si tuviera dos transacciones, vería el mismo comportamiento.

Dicho esto, vale la pena señalar que esta técnica debe ser gestionada por Hibernate. No debes incrementarlo por ti mismo. A menudo es una buena práctica no proporcionar facilitadores para este tipo de campos.

En otras palabras: debe considerar la "versión" como un valor de solo lectura y que no tiene control sobre ella. Por lo tanto, es seguro mostrar su valor en pantalla para el usuario, pero no es seguro manipularlo (o "determinar" la próxima versión). A lo sumo, puede hacer una "mejor predicción", prediciendo que será current_version + 1.

+0

@partenon: entiendo cómo funciona el enjuague y la confirmación, y cómo la versión se incrementó por ellos. Pero aún leeré el libro que recomendó :-) De todos modos, he actualizado mi publicación original para algunas inquietudes adicionales con respecto al incremento manual. Gracias, como siempre. – bertie

+0

Acabo de actualizar la respuesta para abordar sus nuevas inquietudes :-) – jpkrohling

+1

@partenon: Entonces, finalmente puedo concluir que, para crear un DTO dentro de la transacción activa que refleja la entidad con la última versión, realmente tengo que hacer una enjuagar(). Gracias ! – bertie

0

La versión más reciente es el método actual de un + 1. Se podría incrementar la versión de sí mismo en el entityToDto().

Pero en su ejemplo, hacer el color no causará un problema de rendimiento, IMO, ya que Hibernate lo hará de todos modos en el momento de la confirmación. Habrá dos sofocos, pero el segundo ya no tendrá nada que enjuagar.

+0

Hello2, he actualizado mi publicación original para algunas inquietudes adicionales con respecto al incremento manual. – bertie

Cuestiones relacionadas