我试图了解我是否在做正确的交易。我使用PostgreSQL在Spring Boot上构建了一个小的REST API。
此案例为“保留”-传入请求应找到某个实体并将其状态设置为“保留”。必须避免的是两个请求返回相同的实体。
当前,我将整个端点处理程序包装在一个事务中(如下所示)。我了解系统基本上会制作当前状态的快照,然后第一个请求将修改表。
问题是,当第二个请求进入时,在第一个请求仍在事务内时,会发生什么? 我需要find()查询将等到第一笔交易结束后再继续。至少在理论上会如此吗?
@Transactional
@RequestMapping(value = "/newTour", method = RequestMethod.GET, headers = "Accept=application/xml",
consumes = "application/xml", produces = "application/xml")
public @ResponseBody ResponseEntity<?> addTourReservation(@RequestBody PartialTourUpdate partialUpdate) {
try{
List<Tour> tours = tourRepo.findFirstPessimisticByTourTypeInAndStatusOrderByPriorityDesc(partialUpdate.getTourType(), Tour.STATUS_OPEN);
if (tours != null && tours.size() > 0) {
Tour tour = tours.get(0);
tour.setReservationID(partialUpdate.getReservationID());
tour.setStatus(Tour.STATUS_TO_RESERVE);
tourRepo.save(tour);
orderRepo.updateReservationStatus(true, tour.getTourID());
return new ResponseEntity<Tour>(tour, HttpStatus.CREATED);
} else {
rM.setValue(ResultMessage.ErrorCode.LOS_NOT_FOUND);
rM.log();
return new ResponseEntity<ResultMessage>(rM, HttpStatus.OK);
}
} catch (Exception e)
{
rM.setValue(ResultMessage.ErrorCode.LOS_UNKNOWN);
rM.log();
return new ResponseEntity<ResultMessage>(rM, HttpStatus.OK);
}
答案 0 :(得分:1)
锁定更新行以防止并发事务读取它意味着排他锁。
使用JPA,可以使用PESSIMISTIC_WRITE锁定
您需要使用
注释存储库方法 @Lock(LockModeType.PESSIMISTIC_WRITE)
请注意,这将跨越整个tour
表的锁,防止任何并发事务读取任何行,这可能暗示在高负载下线程争用问题。
另一种方法是选择所有可用的游览,并在列表中保留一个随机选择的游览,该游览事先已使用entityManager.lock(tour, LockModeType.PESSIMISTIC_FORCE_INCREMENT)
(实体必须具有{{ 1}}属性),如果更新触发了异常(如果另一个事务已经保留了该异常),请选择另一个并尝试更新它。
不过,最好的方法仍然是让数据库处理并发问题并使用单个SQL(或HQL)更新查询保留“行程”(方法中没有业务逻辑,因此您无需检索并在更新实体之前对其进行操作)。
答案 1 :(得分:0)
多次交易可能具有以下缺点:
情况1.如果T1事务从另一个并发事务T2写入的表A1中读取数据。如果在T2回滚的途中,则T1获得的数据无效。例如a = 2是原始数据。读取T2写入的a = 1。如果T2回滚,那么DB中的a = 1将回滚到a = 2。但是,现在,T1有a = 1,但在DB表中它已更改为a = 2。>
案例2:如果T1事务从表A1读取数据;如果另一个并发事务(T2)更新表A1上的数据,则T1读取的数据与表A1不同;因为T2已更新表A1上的数据例如,如果T1读取a = 1,而T2更新了a = 2。那么a!= b。
情况3:如果T1事务从表A1中读取具有一定行数的数据。如果另一个并发事务(T2)在表A1上插入更多行,则T1读取的行数与表A1上的行不同
第一种情况称为脏读。
第2种情况称为不可重复读取。
第3种情况称为幻像读取。
因此,隔离级别是可以防止方案1,方案2,方案3扩展的范围。您可以通过执行锁定来获得完全隔离级别。这可以防止发生对同一数据的并发读写。但这会影响性能。隔离级别取决于应用程序对应用程序需要多少隔离。
ISOLATION_READ_UNCOMMITTED:允许读取尚未提交的更改。它受到案例1,案例2,案例3的影响
ISOLATION_READ_COMMITTED:允许从已提交的并发事务中读取。可能会遇到情况2和情况3。因为其他事务可能正在更新数据。
ISOLATION_REPEATABLE_READ:对同一字段的多次读取将产生相同的结果,直到被自己更改为止。它可能会遇到情况3,因为其他事务可能正在插入数据
ISOLATION_SERIALIZABLE:情况1,情况2,情况3永远不会发生,它是完全隔离的,涉及完全锁定,由于锁定而影响性能。
希望这会有所帮助!祝你有美好的一天