有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java事务如何真正工作简单用例(SpingBoot)?

我正在试图了解我在交易方面是否做了正确的事情。我使用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) 个答案

  1. # 1 楼答案

    多笔交易可能有以下缺点:

    案例1。如果T1事务从表A1中读取由另一个并发事务T2写入的数据。如果在T2回滚的过程中,T1获取的数据无效。例如a=2是原始数据。如果T1读取T2写入的a=1。如果T2回滚,则a=1将回滚到a=2,单位为DB。但是,现在,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上的行数不同

    案例1被称为脏读

    案例2称为不可重复读取

    案例3被称为幻影阅读

    所以,隔离级别是场景1、场景2、场景3可以预防的范围。通过实现锁定,可以获得完整的隔离级别。这就防止了对同一数据的并发读写。但它会影响性能。隔离级别取决于应用程序之间需要多少隔离

    隔离读取未提交:允许读取尚未提交的更改。它遭受了案例1、案例2、案例3的折磨

    隔离读取提交:允许读取已提交的并发事务。它可能患有病例2和病例3。因为其他事务可能正在更新数据

    隔离_可重复_读取:对同一字段的多次读取将产生相同的结果,直到它被自己更改。它可能患有病例3。因为其他事务可能正在插入数据

    隔离可序列化:案例1、案例2、案例3从未发生过。这是完全孤立的。它需要完全锁定。由于锁定,它会影响性能

    希望这有帮助!!祝你今天愉快

  2. # 2 楼答案

    为更新而锁定一行,以防止并发事务读取它,这意味着一个排他锁

    使用JPA,这是通过使用PESSIMISTIC_WRITE锁实现的

    您需要使用

     @Lock(LockModeType.PESSIMISTIC_WRITE)
    

    请注意,这将在整个tour表上跨越一个锁,防止任何并发事务读取任何行,这可能意味着线程在重载下会出现争用问题

    另一种方法是选择所有可用的旅行团,并使用entityManager.lock(tour, LockModeType.PESSIMISTIC_FORCE_INCREMENT)(实体必须具有@Version属性)在列表中保留一个随机选择的旅行团,该旅行团预先锁定了它(只有它,而不是整个表),如果更新触发了一个异常(如果另一个事务已经保留了它),只需选择另一个并尝试更新它

    然而,最好的方法仍然是让数据库处理并发问题,并使用单个SQL(或HQL)更新查询保留“巡更”(方法中没有业务逻辑,因此不需要在更新实体之前检索和操作实体)