事务如何真正起作用-简单的用例(启动引导)?

时间:2018-08-08 07:33:40

标签: java spring postgresql spring-boot transactions

我试图了解我是否在做正确的交易。我使用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);
    }

2 个答案:

答案 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永远不会发生,它是完全隔离的,涉及完全锁定,由于锁定而影响性能。

希望这会有所帮助!祝你有美好的一天