Шаблон для повторной фиксации Hibernate после исключения StaleObjectStateException - PullRequest
0 голосов
/ 10 января 2019

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...