StaleObjectStateExceptions может являться неизбежной частью использования Hibernate в приложении с более чем одним сервером / потоком, и, как правило, действия для проигравшего повторяются после небольшой задержки безопасным способом.
В интересах наилучшей практики я хотел создать среду, обеспечивающую безопасность, чтобы в случае повторной попытки вызвать потерю данных, она была прервана и было сгенерировано исключение, заставляющее разработчика выяснить, почему у объекта есть поля, которые являются предметом к состоянию гонки и исправить соответственно.
К сожалению, то, что я до сих пор произвел, кажется неоптимальным и содержит то, что, как я считаю, содержит много кода, что означает, что другие разработчики вряд ли будут использовать его в случае необходимости.
Как можно улучшить нижеприведенный пример ExampleRetryableUpdate, особенно для исполняемого файла обновления, в котором имеется множество полей для одновременного обновления?
public class ExampleRetryableUpdate extends SafeRetryableUpdateRunnable<Model>{
private Long id;
private String originalState;
private String targetState;
public ExampleRetryableUpdate(Model targetState, Model currentState){
this.originalState = currentState.getFieldToUpdate();
this.targetState = targetState.getFieldToUpdate();
}
@Override
boolean safeToUpdate(Model current) {
return originalState.equals(current.getFieldToUpdate());
}
@Override
String getStaleFields(Model current) {
StringBuilder sb = new StringBuilder();
if(!this.originalState.equals(current.getFieldToUpdate())){
sb.append("FieldToUpdate differs: Stale - ");
sb.append(this.originalState);
sb.append(", Current - ");
sb.append(current.getFieldToUpdate());
}
//Repeatable for each relevant field
return sb.toString();
}
@Override
public void run() {
try{
Model current = Model.findById(id);
if(safeToUpdate(current)){
current.setFieldToUpdate(targetState);
Model.persist(current);
} else{
throw new UnsafeRetryException(getStaleFields(current));
}
} catch (StaleObjectStateException ex){
System.out.println("Failed due to StaleObject exception, may retry.");
throw ex;
}
}
}
Контекстные классы:
public abstract class SafeRetryableUpdateRunnable<T> implements Runnable {
abstract boolean safeToUpdate(T Model);
abstract String getStaleFields(T current);
}
public class Model {
private Long id;
private String fieldToUpdate;
private String irrelevantField;
private Integer oca;
public String getFieldToUpdate() {
return fieldToUpdate;
}
public void setFieldToUpdate(String fieldToUpdate) {
this.fieldToUpdate = fieldToUpdate;
}
...
}
public class BusinessLogicClass {
public boolean someMethod() throws InterruptedException {
//Business logic
//...
//Get current from Db, create a copy and alter fieldToUpdate
//...
ExampleRetryableUpdate runnable = new ExampleRetryableUpdate(targetState, currentState);
int i = 0;
do{
try{
runnable.run();
return true;
} catch(StaleObjectStateException ex){
System.out.println("Expected, potentially retryable.");
Thread.sleep(1000L);
} catch(UnsafeRetryException ex){
System.out.println("Field has been updated by an alternate thread/server. Aborting.");
return false;
}
}while (i++ <= 3);
return false;
}
}