【问题标题】:Hibernate does not flush session when save object using session.save?使用 session.save 保存对象时,Hibernate 不刷新会话?
【发布时间】:2017-07-10 23:10:39
【问题描述】:

首先看一下下面的代码,我有三个主要模型来管理StoreHouseStoreHouseInvetory,还有一个名为StoreHouseInventoryLock的模型来临时锁定/解锁一些StoreHouseInvetory当其他进程想要使用这些模型时:

public class StoreHouse {
    private String  name;
    private Double  currentAmount;
}

public class StoreHouseInventory {
    private StoreHouse  storeHouse;
    private Good        good;
    private Double      amount;
}

public class StoreHouseInventoryLock {
    private StoreHouseInventory     inventory;
    private Double                  amount;
}

@Service
public class PermitService implements IPermitService {

    @Autowired
    private IStoreHouseInventoryLockService storeHouseInventoryLockService;

    @Autowired
    private IStoreHouseService storeHouseService;

    @Override
    @Transactional
    public void addDetailToPermitFromStoreHouseInventory(long permitId, long storeHouseId, long inventoryId, double amount) {

        // do some business logic here

        /* save is simple method
         * and uses Session.save(object)
        */
        storeHouseInventoryLockService.add(inventoryId, +amount);

        // do some business logic here
        storeHouseService.syncCurrentInventory(storeHouseId);   
    }
}

@Service
public class StoreHouseService implements IStoreHouseService {

    @Autowired
    private IStoreHouseInventoryService storeHouseInventoryService;

    @Autowired
    private IStoreHouseInventoryLockService storeHouseInventoryLockService;

    @Transactional
    public void syncCurrentInventory(storeHouseId) {
        /* is a simeple method that use query like below
         * select sum(e.amount)
         *   from StoreHouseInventory
         *  where e.storeHouse.id = :storeHouseId
         */
        Double sumOfInventory = storeHouseInventoryService.sumOfInventory(storeHouseId);
        /* is a simeple method that use query like below
         * select sum(e.amount)
         *   from StoreHouseInventoryLock
         *  where e.storeHouseInventory.storeHouse.id = :storeHouseId
         */
        Double sumOfLock = storeHouseInventoryService.sumOfLock(storeHouseId);

        // load method is a simple method to load object by it's id
        // and used from Session.get(String entityName, Serializable id)
        StoreHouse storeHouse = this.load(storeHouseId);
        storeHouse.setCurrentAmount(sumOfInventory - sumOfLock);

        this.save(storeHouse);
    }
}

问题是在StoreHouseService.syncCurrentInventory中调用storeHouseInventoryService.sumOfLock时,不知道storeHouseInventoryLockService.add方法在PermitService.addDetailToPermitFromStoreHouseInventory方法中的变化,导致锁总和计算错误。

我认为这是因为当我调用 storeHouseInventoryLockService.add 时会话未刷新。如果是真的,为什么休眠在此更改期间不刷新会话?如果没有,我该怎么办?

【问题讨论】:

  • 对于初学者来说,你的代码有缺陷,你不应该捕获异常并吞下,你的代码会破坏正确的 tx 管理。数据在同一个事务中是可见的,并且当查询数据库时,hibernate 会自动刷新挂起的更改。如果那没有发生,那么您可能是在 hibernate 后面做的事情。添加同步方法(和相关对象)的代码。
  • 对不起,是我的错,我删除了try, catch

标签: java spring hibernate spring-mvc hibernate-session


【解决方案1】:

由于IStoreHouseInventoryLockService.add 是从已经附加了事务的方法中调用的,因此相同的事务会传播到add 方法,因此在外部addDetailToPermitFromStoreHouseInventory 完成之前不会发生刷新。

一个快速的解决方案是使用 REQUIRES_NEW 事务传播类型标记 add 方法:

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void add(..){..}

现在该方法完成后,即使有外部事务,所有持久性更改都会在退出时刷新到数据库。

【讨论】:

  • 在同一个事务中,数据已经可见,你不需要新的事务。如果数据不可见,则说明发生了其他事情。
  • @M.Deinum,你的意思是没有必要使用@Transactional(propagation=Propagation.REQUIRES_NEW)?我该如何解决这个问题?
  • 在 sumOfLock 方法中的查询期间.. 可能在调用之前更改不会刷新到数据库中。理论上是的,如果查询使用在当前事务中受到影响的实体,它们应该是。
【解决方案2】:

如果当前执行的查询不与未刷新的表语句重叠,则可以安全地忽略刷新。

猜你喜欢
  • 2012-08-07
  • 2011-07-17
  • 1970-01-01
  • 2013-10-08
  • 1970-01-01
  • 1970-01-01
  • 2014-02-18
  • 2015-03-01
  • 1970-01-01
相关资源
最近更新 更多