@Transactional propogation_new откатывается родительской транзакцией - PullRequest
0 голосов
/ 09 мая 2019

У меня есть метод обработчика событий, аннотированный @Transactional, этот метод вызывает реализацию события в том же классе.

Это событие выполняет некоторые проверки и в зависимости от результата оно либо что-то делает, либоизмените состояние и сгенерируйте исключение RuntimeException.

В случае, если состояние изменилось из-за проверки, мне нужно, чтобы статус оставался постоянным, но событие не было выполнено для повторной попытки.

Метод изменения состояниянаходится в другом классе, и метод аннотируется @Transactional (распространением = Propagation.REQUIRES_NEW).

Я ожидаю, что, поскольку внутренняя транзакция завершается, изменение состояния становится постоянным, а транзакция события откатывается.

Что я вижу, так это то, что изменение статуса также откатывается, но я не понимаю, почему оно откатывает все, когда я объясняю это, чтобы создать новую транзакцию для изменения статуса.

Имейте в виду, что это устаревший проект, поэтому основные изменения в архивеtecture невозможны.

Я попытался отладить изменения транзакции, и отладчик переходит к фиксации новой транзакции, но по какой-то причине он не сохраняется в базе данных.

public class t implements it {
    // Do initialisation and class injection. Y is constructor injected
    private final Y y;

    public t(Y y) {
       this.y = y;
    }

    @Override
    @Transactional
    public void handleEvent(EventContext context) {
        switch (context.getEventType()) {
            case event:
                validate(context);
                break;
        }
    }

    private void validate(EventContext context) {
        Object o = crudService.findByProperty(context.getObjectUuid());
        if (!o.check) {
            y.changeStatus(ERROR);
            // break for retry
            throw new RuntimeException("Some serious message log");
        } else {
            // do some stuff
        }
    }
}

public class Y implements IY {

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void changeStatus(Object o, String status) {
        // We do a lot more here then just change this status because of inheriting objects but for the sake of the argument, change status
        o.status = status;
    }
}




Это черновик того, что делает код.

Я ожидаю, что изменение статуса будет сохраняться, потому что внешняя транзакция приостанавливается при запуске транзакции propogation_new.Я также вижу, что коммит вызывается в коде транзакции Spring, но по какой-то причине он не сохраняется в базе данных.

Если я удаляю выброс исключения во время выполнения, он работает, но событие завершается, что нежелаемый.

Чего мне не хватает на этой картинке?Надеюсь, что вы можете помочь.

Спасибо!

РЕДАКТИРОВАТЬ

Я думаю, что нашел проблему, немного изменил пример кода, чтобы сделать его более понятным.

changeStatus изменяет статус объекта, который возвращается crudService.В реальном приложении мы делаем намного больше изменений, потому что объекты, которые зависят от объекта o, также должны меняться при изменении статуса.

Поскольку внешняя транзакция имеет состояние o, означает ли это, что если я внесу изменениявнутри внутренней транзакции, поскольку внешняя транзакция содержит ссылку, она будет откатываться до этого состояния вместо сохранения изменений внутренней транзакции?

1 Ответ

0 голосов
/ 13 мая 2019

Проблема вызвана тем, что первая транзакция содержит ссылку на объект, статус которого был изменен.

Когда мы меняем статус в новой транзакции, мы фиксируем это изменение статуса и возвращаем.Когда мы возвращаемся, внешняя транзакция возобновляется и выдает исключение RuntimeException, которое вызывает откат.Поскольку транзакция содержит состояние объекта, статус которого был изменен, этот объект возвращается к состоянию, которое имеет внешняя транзакция, то есть к старому состоянию.

Чтобы исправить это, вместо того, чтобы иметь только статусизменив новую транзакцию, я переместил всю логику в собственную транзакцию и удалил транзакцию при изменении статуса.

Затем я реализовал проверенное исключение, которое выдается при изменении статуса, которое перехватывается и затем выбрасывается вродитель.Любое другое исключение перехватывается и отправляет RuntimeException, чтобы прерваться.

Исключения перехватываются родителем, и служба генерирует RuntimeException.Поскольку внутренняя транзакция завершена и зафиксирована (в случае проверенного исключения), состояние остается измененным, а событие повторяется неудачно.

В моем сценарии я переместил логику в свой собственный класс / метод, вы также можете оставитькод в том же классе, но вам нужно будет реализовать собственный прокси и использовать этот прокси для вызова, чтобы Spring прокси через ваш метод, в противном случае он будет игнорировать оператор транзакции для метода.

Ниже приведенокончательный вариант того, как это выглядело.

public class t implements it {
    // Do initialisation and class injection. Y is constructor injected
    private final B b;

    public t(B b) {
       this.b = b;
    }

    @Override
    @Transactional
    public void handleEvent(EventContext context) {
        switch (context.getEventType()) {
            case event:
                validate(context);
                break;
        }
    }

    // You can skip this method and simply call b, but in my scenario we do a couple of other things that do not have to be part of the transaction
    private void validate(EventContext context) {
        try {
            b.allLogicMethod(context.getObjectUuid());
        } catch(Exception e) {
            // Here we break the event so we can retry it, but the transaction succeeded in case it was a checked Exception
            throw new RuntimeException(e);
        }
    }
}

public class b implements IB {

    private final Y y;

    Public B(Y y) {
        this.Y = y;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void allLogicMethod(String uuid) {
        try {
            Object o = crudService.findByProperty(context.getObjectUuid());
            if (!o.check) {
                y.changeStatus(o, ERROR);
                // break for retry
                throw new CheckedException("Some serious message log");
            } else {
                // do everything else
            }
        } catch(CheckedException ce) {
            throw ce;
        } catch(Exception e) {
            throw new RuntimeException("some message", e);
        }
    }
}

public class Y implements IY {

    @Override
    public void changeStatus(Object o, String status) {
        // We do a lot more here then just change this status because of inheriting objects but for the sake of the argument, change status
        o.status = status;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...