tutorial mkyong example español ejemplo data baeldung spring hibernate spring-boot spring-data spring-data-jpa

mkyong - Spring Boot Data JPA-Modificar consulta de actualización-Actualizar contexto de persistencia



spring persistence (2)

Estoy trabajando con Spring Boot 1.3.0.M4 y una base de datos MySQL.

Tengo un problema al utilizar consultas de modificación, el EntityManager contiene entidades obsoletas después de que la consulta se haya ejecutado.

Repositorio original de JPA:

public interface EmailRepository extends JpaRepository<Email, Long> { @Transactional @Modifying @Query("update Email e set e.active = false where e.active = true and e.expire <= NOW()") Integer deactivateByExpired(); }

Supongamos que tenemos un correo electrónico [id = 1, active = true, expire = 2015/01/01] en DB.

Después de ejecutar:

emailRepository.save(email); emailRepository.deactivateByExpired(); System.out.println(emailRepository.findOne(1L).isActive()); // prints true!! it should print false

Primer enfoque para resolver el problema: agregar clearAutomatically = true

public interface EmailRepository extends JpaRepository<Email, Long> { @Transactional @Modifying(clearAutomatically = true) @Query("update Email e set e.active = false where e.active = true and e.expire <= NOW()") Integer deactivateByExpired(); }

Este enfoque borra el contexto de persistencia para no tener valores desactualizados, pero elimina todos los cambios sin vaciar aún pendientes en el EntityManager. Como solo uso los métodos save() y no saveAndFlush() algunos cambios se pierden para otras entidades :(

Segundo enfoque para resolver el problema: implementación personalizada para el repositorio

public interface EmailRepository extends JpaRepository<Email, Long>, EmailRepositoryCustom { } public interface EmailRepositoryCustom { Integer deactivateByExpired(); } public class EmailRepositoryImpl implements EmailRepositoryCustom { @PersistenceContext private EntityManager entityManager; @Transactional @Override public Integer deactivateByExpired() { String hsql = "update Email e set e.active = false where e.active = true and e.expire <= NOW()"; Query query = entityManager.createQuery(hsql); entityManager.flush(); Integer result = query.executeUpdate(); entityManager.clear(); return result; } }

Este enfoque funciona de manera similar a @Modifying(clearAutomatically = true) pero primero obliga al EntityManager a vaciar todos los cambios en la base de datos antes de ejecutar la actualización y luego borra el contexto de persistencia. De esta manera no habrá entidades desactualizadas y todos los cambios se guardarán en la base de datos.

Me gustaría saber si hay una mejor manera de ejecutar sentencias de actualización en JPA sin tener el problema de las entidades desactualizadas y sin el lavado manual a DB. ¿Tal vez deshabilitar el caché de segundo nivel? ¿Cómo puedo hacerlo en Spring Boot?

Actualización 2018

Spring Data JPA aprobó mi PR, ahora hay una opción @Modifying() en @Modifying() .

@Modifying(flushAutomatically = true, clearAutomatically = true)


Como dijo klaus-groenbaek, puede inyectar EntityManager y usar su método de actualización:

@Inject EntityManager entityManager; ... emailRepository.save(email); emailRepository.deactivateByExpired(); Email email2 = emailRepository.findOne(1L); entityManager.refresh(email2); System.out.println(email2.isActive()); // prints false


Sé que esta no es una respuesta directa a su pregunta, ya que ya ha creado una solución y ha iniciado una solicitud de extracción en Github. ¡Gracias por eso!

Pero me gustaría explicar la forma en que JPA puede ir. Por lo tanto, le gustaría cambiar todas las entidades que coincidan con un criterio específico y actualizar un valor en cada una. El enfoque normal es simplemente cargar todas las entidades necesarias:

@Query("SELECT * FROM Email e where e.active = true and e.expire <= NOW()") List<Email> findExpired();

Luego itere sobre ellos y actualice los valores:

for (Email email : findExpired()) { email.setActive(false); }

Ahora hibernate conoce todos los cambios y los escribirá en la base de datos si la transacción se realiza o si llama a EntityManager.flush() manualmente. Sé que esto no funcionará bien si tiene una gran cantidad de entradas de datos, ya que carga todas las entidades en la memoria. Pero esta es la mejor manera de mantener sincronizados el caché de entidades en hibernación, las cachés de segundo nivel y la base de datos.

¿Esta respuesta dice "la anotación` @ Modificación´ es inútil "? ¡No! Si se asegura de que las entidades modificadas no estén en su caché local, por ejemplo, la aplicación de solo escritura, este enfoque es el camino a seguir.

Y solo para el registro: no necesita @Transactional en sus métodos de repositorio.

Solo para el registro v2: la columna active parece que tiene una dependencia directa para expire . Entonces, ¿por qué no eliminar active completo y esperar que expire en todas las consultas?