mkyong example ejemplo data java spring jpa spring-data spring-data-jpa

java - ejemplo - spring jpa repository example



¿Cómo habilitar LockModeType.PESSIMISTIC_WRITE al buscar entidades con Spring Data JPA? (3)

¿Cómo puedo lograr el equivalente de este código?

tx.begin(); Widget w = em.find(Widget.class, 1L, LockModeType.PESSIMISTIC_WRITE); w.decrementBy(4); em.flush(); tx.commit();

... pero usando las anotaciones Spring-Spring-Data-JPA?

La base de mi código actual es:

@Service @Transactional(readOnly = true) public class WidgetServiceImpl implements WidgetService { /** The spring-data widget repository which extends CrudRepository<Widget, Long>. */ @Autowired private WidgetRepository repo; @Transactional(readOnly = false) public void updateWidgetStock(Long id, int count) { Widget w = this.repo.findOne(id); w.decrementBy(4); this.repo.save(w); } }

Pero no sé cómo especificar que todo en el método updateWidgetStock se debe hacer con un conjunto de bloqueo pesimista.

Hay una anotación Spring Data JPA org.springframework.data.jpa.repository.Lock que le permite establecer un LockModeType , pero no sé si es válido ponerlo en el método updateWidgetStock . Suena más como una anotación en el WidgetRepository , porque el Javadoc dice:

org.springframework.data.jpa.repository
@Target (value = METHOD)
@Retention (value = RUNTIME)
@Documentado
cerradura de interfaz pública
Anotación utilizada para especificar el LockModeType que se utilizará al ejecutar la consulta. Se evaluará al usar Query en un método de consulta o si deriva la consulta del nombre del método.

... así que eso no parece ser útil.

¿Cómo puedo hacer que mi método updateWidgetStock() se ejecute con LockModeType.PESSIMISTIC_WRITE establecido?


Si no desea anular el findOne() estándar de findOne() , puede adquirir un bloqueo en su método personalizado al usar select ... for update consulta de select ... for update como esta:

/** * Repository for Wallet. */ public interface WalletRepository extends CrudRepository<Wallet, Long>, JpaSpecificationExecutor<Wallet> { @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("select w from Wallet w where w.id = :id") Wallet findOneForUpdate(@Param("id") Long id); }

Sin embargo, si está utilizando PostgreSQL, las cosas pueden complicarse un poco si desea establecer el tiempo de espera de bloqueo para evitar interbloqueos. PostgreSQL ignora la propiedad estándar javax.persistence.lock.timeout establecida en las propiedades JPA o en la anotación @QueryHint .

La única forma en que podía hacerlo funcionar era crear un repositorio personalizado y configurar el tiempo de espera manualmente antes de bloquear una entidad. No es agradable, pero al menos funciona:

public class WalletRepositoryImpl implements WalletRepositoryCustom { @PersistenceContext private EntityManager em; @Override public Wallet findOneForUpdate(Long id) { // explicitly set lock timeout (necessary in PostgreSQL) em.createNativeQuery("set local lock_timeout to ''2s'';").executeUpdate(); Wallet wallet = em.find(Wallet.class, id); if (wallet != null) { em.lock(wallet, LockModeType.PESSIMISTIC_WRITE); } return wallet; }

}


@Lock es compatible con los métodos CRUD a partir de la versión 1.6 de Spring Data JPA (de hecho, ya hay un milestone disponible). Vea este ticket para más detalles.

Con esa versión, simplemente declaras lo siguiente:

interface WidgetRepository extends Repository<Widget, Long> { @Lock(LockModeType.PESSIMISTIC_WRITE) Widget findOne(Long id); }

Esto hará que la implementación de CRUD, parte del proxy del repositorio de respaldo, aplique LockModeType configurado a la llamada de find(…) en EntityManager .


Si puede usar Spring Data 1.6 o superior, ignore esta respuesta y consulte la respuesta de Oliver.

Las anotaciones pesimistas @Lock Spring Data solo se aplican (como ha señalado) a las consultas. No hay anotaciones que conozca que puedan afectar una transacción completa. Puede crear un método findByOnePessimistic que invoca findByOne con un bloqueo pesimista o puede cambiar findByOne para obtener siempre un bloqueo pesimista.

Si quisiera implementar su propia solución, probablemente podría. Debajo del capó, la anotación @Lock es procesada por LockModePopulatingMethodIntercceptor que hace lo siguiente:

TransactionSynchronizationManager.bindResource(method, lockMode == null ? NULL : lockMode);

Podrías crear algún gestor de bloqueo estático que tuviera una variable de miembro ThreadLocal<LockMode> y luego tener un aspecto envuelto alrededor de cada método en cada repositorio que llamara bindResource con el modo de bloqueo establecido en ThreadLocal . Esto le permitiría configurar el modo de bloqueo por subproceso. Luego, podría crear su propia anotación @MethodLockMode que envolvería el método en un aspecto que establezca el modo de bloqueo específico del subproceso antes de ejecutar el método y lo borre después de ejecutar el método.