5

I need my app to have the following behavior:

Scenario 1

  1. User A views an order.

  2. User B views the same order.

  3. User A deletes the order.
  4. User B requests to update the order. This should fail.

Scenario 2

  1. User A views an order
  2. User B views the same order
  3. User A updates the order.
  4. User B request to delete the order. This should fail.

Using JPA (Hibernate via Spring Data JPA), I'm trying to use @Version to implement this optimistic locking behavior:

@Entity
public class Order {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Version
    private Integer version;

    // many other fields

On delete, the UI client gives the server a list of order ids along with the version number for each order id. This [post(Spring Data JPA: Delete Optimistic Locking semantics) mentions a standard solution:

if (entity.getVersion() != dto.getVersion()) {
    throw new OptimisticLockException("...");
}

To use this, I have to

  1. lookup the entity from the database use the order id from the client
  2. compare the entity version to the DTO version from the client
  3. Perform the delete.

The problem is that at step 2 the entity and DTO version may be the same. But then at step 3, the version may be different. Is there a way to have hibernate perform the check and update as a single atomic action such as:

 delete from [Order] where orderId = ? and version = ? 

and throw StaleObjectStateException if none deleted.

Update

I found two approaches that should work. Is there a problem with one of these two approaches? The second approach involves less trips to the database. The client will generally send only one order to delete at a time so performance should not be an issue here.

Approach 1

For each order to be deleted:

        Order order = orderRepository.findById(
                orderIdFromClient).orElseThrow(() ->
            new OptimisticLockException());

        if (!order.getVersion().equals(versionFromClient)) {
            throw new OptimisticLockException();
        }

        // We now know the managed entity has the same version
        // as the requested version. If some other transaction
        // has changed the entity, Hibernate will rollback and
        // throw OptimisticLockException.
        orderRepository.delete(order);

Approach 2

Add an OrderRepository method:

int deleteByIdAndVersion(Long id, Integer version);

For each order to be deleted:

        int x = orderRepository.deleteByIdAndVersion(orderIdFromClient, versionFromClient);
        if (x==0) {
            throw new OptimisticLockException();
        }
user8297969
  • 415
  • 1
  • 6
  • 18

0 Answers0