繁体   English   中英

悲观锁不起作用Spring Boot Data JPA

[英]Pessimistic Lock not working Spring Boot Data JPA

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

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

ReservationService:

@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);
  }
}

AvailabilityService:

@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

锁不适用于新记录。 您必须以某种方式锁定整个表。 如果您完全确定服务器将仅运行一个实例,则可以synchronize方法,也可以使用'lock-records'创建一个特殊的表,并在创建实际表中的新记录之前用锁读取该记录,然后释放该锁。

第一种方法很简单,但是第二种方法更安全。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM