En este rápido tutorial, analizaremos cómo habilitar los bloqueos de transacción en Spring Data JPA para los métodos de consulta personalizados y los métodos de repositorio predefinidos de CRUD.

También veremos diferentes tipos de bloqueo y estableceremos los tiempos de espera de bloqueo de transacción.

Tipos de bloqueo

JPA tiene dos tipos de bloqueo principales definidos, que son el bloqueo pesimista y el bloqueo optimista.

  • Bloqueo pesimista Cuando usamos el bloqueo pesimista en una transacción y accedemos a una entidad, se bloqueará de inmediato. La transacción libera el bloqueo ya sea confirmando o revirtiendo la transacción.
  • Bloqueo optimista: En el bloqueo optimista, la transacción no bloquea la entidad inmediatamente. En su lugar, la transacción generalmente guarda el estado de la entidad con un número de versión asignado.

    Cuando intentamos actualizar el estado de la entidad en una transacción diferente, la transacción compara el número de versión guardado con el número de versión existente durante una actualización.

    En este punto, si el número de versión es diferente, significa que la entidad no se puede modificar. Si hay una transacción activa, esa transacción se retrotraerá y la implementación JPA subyacente generará una excepción OptimisticLockException.

    Además del enfoque del número de versión, podemos usar otros enfoques como las marcas de tiempo, el cálculo del valor hash o la suma de comprobación serializada, dependiendo de qué enfoque sea el más adecuado para nuestro contexto de desarrollo actual.

Habilitar bloqueos de transacción en métodos de consulta

Para adquirir un bloqueo en una entidad, podemos anotar el método de consulta de destino con una anotación de bloqueo pasando el tipo de modo de bloqueo requerido.

Los tipos de modo de bloqueo son valores de enumeración que deben especificarse al bloquear una entidad. El modo de bloqueo especificado luego se propaga a la base de datos para aplicar el bloqueo correspondiente en el objeto de entidad.

Para especificar un bloqueo en un método de consulta personalizado de un repositorio de Spring Data JPA, podemos anotar el método con @Lock y especificar el tipo de modo de bloqueo requerido:

@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
@Query("SELECT c FROM Cliente c WHERE c.orgId = ?1")
public List<Cliente> obtenerClientesPorOrgId(Long orgId);

Para aplicar el bloqueo en los métodos de repositorio predefinidos, como findAll o findById (id), debemos declarar el método dentro del repositorio y anotar el método con la anotación de bloqueo:

@Lock(LockModeType.PESSIMISTIC_READ)
public Optional<Cliente> findById(Long idCliente);

Cuando el bloqueo está habilitado explícitamente y no hay una transacción activa, la implementación de JPA subyacente generará una excepción TransactionRequiredException.

En caso de que no se pueda otorgar el bloqueo y el conflicto de bloqueo no resulte en una reversión de la transacción, JPA lanza una excepción LockTimeoutException. Pero no marca la transacción activa para la reversión.

Configuración de timeouts de bloqueo de transacción

Cuando se utiliza el bloqueo pesimista, la base de datos intentará bloquear la entidad inmediatamente. La implementación JPA subyacente lanza una excepción LockTimeoutException cuando el bloqueo no se puede obtener de inmediato. Para evitar tales excepciones, podemos especificar el valor de tiempo de espera de bloqueo.

En Spring Data JPA, el tiempo de espera de bloqueo se puede especificar mediante la anotación QueryHints colocando un QueryHint en los métodos de consulta:

@Lock(LockModeType.PESSIMISTIC_READ)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "3000")})
public Optional<Customer> findById(Long idCliente);

Cuando la transacción debe cumplir estrictamente con las reglas de ACID, debemos utilizar el bloqueo pesimista. El bloqueo optimista debe aplicarse cuando necesitamos permitir múltiples lecturas simultáneas y cuando la coherencia final es aceptable dentro del contexto de la aplicación.

Tagged in:

, , ,