java - 悲观锁不起作用 Spring Boot Data JPA

标签 java spring hibernate spring-boot spring-data-jpa

我正在使用 Spring Boot 2 和 Spring Data JPA。

我有一个带有 @Transactional 注释的服务,它从存储库中读取记录,然后添加记录(如果不存在)并保存所有记录。 我创建了一个并行执行服务方法 5 次的测试方法。 由于我使用 @Lock(LockModeType.PESSIMISTIC_WRITE) 我希望一个人在读取可用性时会获得锁,而其他 4 个线程必须等待事务 (createReservation) 完成,但方法会运行 5 次并返回没有记录,因此所有线程都尝试插入新记录,但它们都因唯一索引或主键冲突而失败(第一个记录除外)。 对于测试,我使用 H2 数据库。

预订服务:

@Service
public class ReservationService {

  @Autowired
  private AvailabilityService availabilityService;
  @Autowired
  private ReservationRepository repository;

  @Transactional
  public Reservation createReservation(Reservation r) {
    availabilityService.updateAvailability( r);
    return reservationRepository.save( r);
  }
}

可用性服务:

@Service
public class DayAvailabilityService {

  @Autowired
  private AvailabilityRepository availabilityRepository;

  public List<Availability> updateAvailability(Reservation reservation) {
    List<LocalDate> dates = reservation.getStart().datesUntil(reservation.getEnd()).collect(Collectors.toList());
    List<Availability> availabilities = availabilityRepository.findAllById(dates);
    // check availability, add records to this list if a record does not exist
    /// ...
    return availabilityRepository.saveAll(availabilities);
  }

}

public interface AvailabilityRepository extends JpaRepository<Availability, LocalDate> {

@Override
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Availability> findAllById(Iterable<LocalDate> iterable);

}

可用性实体:

@Entity
@Table(name = "Availability")
public class Availability {

  @Column(name = "Date")
  @Id
  @NotNull
  private LocalDate date;
  @Column(name = "Availability")
  private int availability;
  @Column(name = "MaxAvailability")
  private int maxAvailability;
}

这是测试类:

@SpringBootTest
@RunWith(SpringRunner.class)
public class ReservationServiceIntegrationTest {

  @Autowired
  private ReservationService service;
  @Autowired
  private ReservationRepository repository;

  @Test
  public void testConcurrentCreateReservation() throws InterruptedException {
    Reservation reservation = new Reservation("John", "Doe", "johndoe@mail.com",
            LocalDate.now().plusDays(4), LocalDate.now().plusDays(6), 30);
    runMultithreaded(() -> {
        try {
            service.createReservation(reservation);
        } catch (NoAvailabilityException e) {
            System.out.println("no availability.");
        }
    }, 5);

    long count = repository.count();
    assertEquals(3, count);

  }

  public static void runMultithreaded(Runnable  runnable, int threadCount) throws InterruptedException {
    List<Thread> threadList = new LinkedList<>();

    for(int i = 0 ; i < threadCount; i++) {
        threadList.add(new Thread(runnable));
    }

    for( Thread t :  threadList) {
        t.start();
    }

    for( Thread t :  threadList) {
        t.join();
    }
  }
}

在日志中我看到为每个 createReservation 方法创建了一个事务。

Getting transaction for [com.company.app.service.ReservationService.createReservation]

然后我看到 5 个这样的日志:

Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAllById]

然后我看到选择查询执行了 5 次,末尾带有“for update”。所以锁应该可以工作,但我没有看到我期望的结果。

我的代码有什么问题吗?

谢谢。

最佳答案

我认为问题是你想在每个线程中插入具有相同ID的记录。

// check availability, add records to this list if a record does not exist

锁不适用于新记录。你必须以某种方式锁定整个表。如果您完全确定您的服务器将只运行一个实例,您可以同步您的方法,或者您可以创建一个带有“锁记录”的特殊表,并在创建该记录之前使用锁读取该记录。实际表中的新记录,然后释放该锁。

第一种方法非常简单,但第二种方法更安全。

关于java - 悲观锁不起作用 Spring Boot Data JPA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54953003/

相关文章:

java 一个月的最后一个星期日

java - JPQL:EclipseLink 和 Hibernate 之间的区别

spring - 在 Spring STS 中通过 maven 项目连接到 postgreSQL

java - Spring Mongo的查找操作在windows server 2008机器上卡住

java - Spring Boot 无法呈现(非常)简单的 index.jsp

spring - 我可以使用AOP注解在spring中注入(inject)代码吗?

java - 无法为测试上下文的 @Transactional 测试检索 PlatformTransactionManager

java - java中同时min和minindex的内置函数?

Java导入包

java - 完美的圆角矩形