feature: Add optimistic offline lock (#1306) (#2551)

This commit is contained in:
Eugene
2023-08-27 14:01:46 +03:00
committed by GitHub
parent 70692f647f
commit 8b11e76f46
9 changed files with 399 additions and 0 deletions
@@ -0,0 +1,18 @@
package com.iluwatar.api;
/**
* Service for entity update.
*
* @param <T> target entity
*/
public interface UpdateService<T> {
/**
* Update entity.
*
* @param obj entity to update
* @param id primary key
* @return modified entity
*/
T doUpdate(T obj, long id);
}
@@ -0,0 +1,16 @@
package com.iluwatar.exception;
/**
* Exception happens in application during business-logic execution.
*/
public class ApplicationException extends RuntimeException {
/**
* Inherited constructor with exception message.
*
* @param message exception message
*/
public ApplicationException(String message) {
super(message);
}
}
@@ -0,0 +1,37 @@
package com.iluwatar.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Bank card entity.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Card {
/**
* Primary key.
*/
private long id;
/**
* Foreign key points to card's owner.
*/
private long personId;
/**
* Sum of money.
*/
private float sum;
/**
* Current version of object.
*/
private int version;
}
@@ -0,0 +1,33 @@
package com.iluwatar.repository;
/**
* Imitation of Spring's JpaRepository.
*
* @param <T> target database entity
*/
public interface JpaRepository<T> {
/**
* Get object by it's PK.
*
* @param id primary key
* @return {@link T}
*/
T findById(long id);
/**
* Get current object version.
*
* @param id primary key
* @return object's version
*/
int getEntityVersionById(long id);
/**
* Update object.
*
* @param obj entity to update
* @return number of modified records
*/
int update(T obj);
}
@@ -0,0 +1,35 @@
package com.iluwatar.service;
import com.iluwatar.api.UpdateService;
import com.iluwatar.exception.ApplicationException;
import com.iluwatar.model.Card;
import com.iluwatar.repository.JpaRepository;
import lombok.RequiredArgsConstructor;
/**
* Service to update {@link Card} entity.
*/
@RequiredArgsConstructor
public class CardUpdateService implements UpdateService<Card> {
private final JpaRepository<Card> cardJpaRepository;
@Override
public Card doUpdate(Card obj, long id) {
float additionalSum = obj.getSum();
Card cardToUpdate = cardJpaRepository.findById(id);
int initialVersion = cardToUpdate.getVersion();
float resultSum = cardToUpdate.getSum() + additionalSum;
cardToUpdate.setSum(resultSum);
//Maybe more complex business-logic e.g. HTTP-requests and so on
if (initialVersion != cardJpaRepository.getEntityVersionById(id)) {
String exMessage =
String.format("Entity with id %s were updated in another transaction", id);
throw new ApplicationException(exMessage);
}
cardJpaRepository.update(cardToUpdate);
return cardToUpdate;
}
}