2

First of all take a look at below code, I have three main model to manage StoreHouseand StoreHouseInvetory's and have other model named StoreHouseInventoryLock to temporary lock/unlock some amount of StoreHouseInvetory's when other processes wants to use these models:

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);
    }
}

the problem is that when storeHouseInventoryService.sumOfLock is called in StoreHouseService.syncCurrentInventory, it does not be aware change of storeHouseInventoryLockService.add method in PermitService.addDetailToPermitFromStoreHouseInventory method, and calcuates sum of locks incorrectly.

I think this is because of session was not flushed when I call storeHouseInventoryLockService.add. If it's true, why hibernate does not flsuh session during this changes? If not, what should I do?

Rasool Ghafari
  • 4,128
  • 7
  • 44
  • 71
  • For starters you code is flawed, you should never catch exception and swallow, your code breaks proper tx management. Data is visible from within the same transaction and hibernate automatically flushes pending changes when queurying the database. If that doesn't happen you are doing things around the back of hibernate probably. Add the code for the sync method (and related objects). – M. Deinum Feb 21 '17 at 11:20
  • Sorry, it was my fault, i removed `try, catch` block – Rasool Ghafari Feb 21 '17 at 11:40

2 Answers2

0

As IStoreHouseInventoryLockService.add is called from within a method which already has a transaction attached to it, then the same transaction is propagated to the add method, thus the flush does not happen until the outer addDetailToPermitFromStoreHouseInventory finishes.

A quick solution for you would be to mark the add method with a REQUIRES_NEW transaction propagation type:

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

Now after that method finishes, even though there is an outer transaction, all the persistence changes will be flushed to the database on exit.

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
  • Within the same transaction the data is visible already, you don't need new transaction for that. If the data isn't visible there is something else going on. – M. Deinum Feb 21 '17 at 11:19
  • @M.Deinum, you mean there isn't nedd to use `@Transactional(propagation=Propagation.REQUIRES_NEW)`? how can i solve this problem? – Rasool Ghafari Feb 21 '17 at 11:25
  • During the query in sumOfLock method.. maybe the changes are not flushed to the database just before the call. In theory yes, they should be if the query uses entities which had been affected in current transaction. – Maciej Kowalski Feb 21 '17 at 11:27
0

If the current executing query doesn’t overlap with the unflushed table statements, the a flush can be safely ignored.

ali akbar azizkhani
  • 2,213
  • 5
  • 31
  • 48