我使用的是Spring Boot 2和Spring数据JPA。
我有一个带有@Transactional annotation的服务,它从存储库中读取记录,如果记录不存在,则添加记录并保存所有记录。我创建了一个测试方法,并行执行服务方法5次。由于我使用的是@Lock(LockModeType.悲观写入),我希望其中一个线程在读取可用性时会得到锁,而其他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]
然后我看到select查询执行了5次,最后是“for update”。所以锁应该可以工作,但我没有看到我预期的结果。
我的代码有什么问题?
谢谢
我认为问题是您想在每个线程中插入具有相同ID的记录。
// check availability, add records to this list if a record does not exist
锁对新记录不起作用。你得设法把整张桌子都锁上。如果您完全确定服务器将只运行一个实例,则可以同步您的方法,或者您可以使用“锁定记录”创建一个特殊表,并在实际表中创建新记录之前使用锁读取该记录,然后释放该锁。
第一种方法非常简单,但第二种方法更容易防故障。
问题内容: 我在java下有spring项目,使用hibernate查询,我喜欢使用悲观锁定。 在Spring + Hibernate中如何进行悲观锁定? 编辑: 问题: 我想在一个方法中使用悲观锁定,并且我将此方法称为从不同的方法。当我从第一个方法调用它时,悲观的工作效果很好,但是当我从第二个方法调用它时,它给出了(无法提交事务) 例外: 问题答案: http://www.amicabile.c
我正在使用Spring Boot、JPA、Oracle 12C和下面的类型化查询来选择要处理的“新”项目。一旦我选择了“新”项目,我就会更新其状态,使其不再符合选择条件,但我看到一个并发问题,相同的项目被选中。 我在这里读到,我需要设置一个'LockModeType.PESSIMISTIC_WRITE'的查询,以防止其他线程选择相同的行,但它似乎不起作用。 我是否遗漏了下面的内容,或者我是否需要另
我在用 Spring Boot 1.4.2 Spring Data JPA 1.10.5 PostgreSQL 9.5数据库 不幸的是,这不会刷新实体管理器缓存中我的实体的上一个实例。我有两个同时更新注册状态的请求 第二个等待第一个事务的事务提交 第二个不考虑第一个所做的更改。 从而破坏行为。 下面是请求的示例代码:
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 乐观锁与悲观锁的具体区别: http://www.cnblogs.com/Bob-FD/p/3352216.html
我遵循grails文档,它说要做悲观锁定,我可以这样做: 所以这会锁定计划实例,直到保存完成。现在在我的例子中,我想一次锁定多个计划,如下所示: 我在默认情况下是事务性的 grails 服务中执行此操作,但上述行没有按预期工作。它不会锁定所有行,并在执行并发事务时引发。 如何在阅读时锁定多行? 有关更多信息,请参见相关问题:grails中的并发事务导致数据库陈旧状态异常
我没有成功地将Spring数据发送给postgres a以进行NoWait更新。 我已经在存储库中尝试过了: 配置(代码段) 我甚至将EntityManager注入到服务中,结果返回0: info(em.getproperties().get(“javax.persistence.lock.timeout”)); 但是上面只给了我“用于更新”,没有设置NOWAIT部分。我设置的事情是正确的(从另一