Spring JPA 锁定概念
Posted
技术标签:
【中文标题】Spring JPA 锁定概念【英文标题】:Spring JPA Locking Concept 【发布时间】:2020-01-15 07:05:48 【问题描述】:有人知道如何在 Spring JPA 中使用 Locking with UPDATE 语句,以便一次只能一个线程更新记录吗?
在这里,我正在尝试更新表名 aircraft_route 的可用性,并且在更新之前,我想获取该行的锁定,以便没有其他线程可以同时更新它。
错误:非法尝试在本机 SQL 查询上设置锁定模式
任何帮助将不胜感激。
谢谢
AirCraftRoute.java
@Builder
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "aircraft_route")
public class AirCraftRoute implements Serializable
@EmbeddedId
private AirCraftRoutePK pk = new AirCraftRoutePK();
@ManyToOne
@MapsId("airCraftId")
@JoinColumn(name = "aircraft_id")
private AirCraft airCraft;
@ManyToOne
@MapsId("routeId")
@JoinColumn(name = "route_id")
private Route route;
@Column(name = "journey_date")
private Date journeyDate;
@Column(name = "departure_time")
private Time departureTime;
@Column(name = "arrival_time")
private Time arrivalTime;
@Column(name = "fare")
private float fare;
@Column(name = "availability")
private int availability;
@Override
public boolean equals(Object o)
if (this == o) return true;
if (!(o instanceof AirCraftRoute)) return false;
AirCraftRoute that = (AirCraftRoute) o;
return Float.compare(that.getFare(), getFare()) == 0 &&
Objects.equals(getAirCraft(), that.getAirCraft()) &&
Objects.equals(getRoute(), that.getRoute()) &&
Objects.equals(getJourneyDate(), that.getJourneyDate()) &&
Objects.equals(getDepartureTime(), that.getDepartureTime()) &&
Objects.equals(getArrivalTime(), that.getArrivalTime());
@Override
public int hashCode()
return Objects.hash(getAirCraft(), getRoute(), getJourneyDate(), getDepartureTime(), getArrivalTime(), getFare());
AirCraftRouteRepository.java
@Repository
public interface AirCraftRouteRepository extends JpaRepository<AirCraftRoute,Integer>
@Transactional
@Query(value = "SELECT ar.availability FROM aircraft_route ar WHERE ar.aircraft_id = :airCraftId AND ar.route_id = :routeId FOR UPDATE",nativeQuery = true)
int getAvailability(@Param("airCraftId") Long airCraftId, @Param("routeId") Long routeId);
@Modifying
@Transactional
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query(value = "UPDATE aircraft_route ar SET ar.availability = ar.availability - :numberOfTickets WHERE ar.aircraft_id = :airCraftId AND ar.route_id = :routeId AND ar.availability > 0",nativeQuery = true)
int updateAvailability(@Param("numberOfTickets") int numberOfTickets,@Param("airCraftId") Long airCraftId, @Param("routeId") Long routeId);
【问题讨论】:
【参考方案1】:我认为对于本机 SQL,您根本不需要 Lock。您可以直接在原生查询中FOR UPDATE
。像这样
@Transactional
@Query(value = "SELECT * FROM aircraft_route ar WHERE ar.aircraft_id = :airCraftId AND ar.route_id = :routeId FOR UPDATE",nativeQuery = true)
List<Integer> getAvailability(@Param("airCraftId") Long airCraftId, @Param("routeId") Long routeId);
【讨论】:
知道如何在 UPDATE 命令中使用“FOR UPDATE”吗? @Query(value = "UPDATE aircraft_route ar SET ar.availability = ar.availability - :numberOfTickets WHERE ar.aircraft_id = :airCraftId AND ar.route_id = :routeId AND ar.availability > 0",nativeQuery = true) 我们可以使用 UPDATE使用上述命令? 更新时无需使用FOR UPSATE
。你已经在更新了。【参考方案2】:
我认为您可以简单地避免对这种简单语法使用本机查询:
@Transactional
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query(value = "SELECT * FROM AircraftRoute ar WHERE ar.id = :airCraftId AND ar.routeId = :routeId")
List<Integer> getAvailability(@Param("airCraftId") Long airCraftId, @Param("routeId") Long routeId);
类似上面的东西。您必须对实体名称和字段使用正确的语法,然后您可以使用 @Lock
Spring 注释及其数据库锁管理,而不是手动处理。
【讨论】:
您的解决方案抛出异常,因为“方法 public abstract java.util.List com.abc.gds.repository.AirCraftRouteRepository.getAvailability 的查询验证失败”,我认为,这是由于: ***.com/questions/44647630/… 你能发布你的 AircraftRoute 实体吗?以及更新的接口getAvailability方法?以上是关于Spring JPA 锁定概念的主要内容,如果未能解决你的问题,请参考以下文章
Spring Data JPA / Hibernate中锁的范围是什么?