Как сделать транзакцию без потери инкапсуляции? - PullRequest
0 голосов
/ 04 мая 2011

У меня есть код, который сохраняет компонент и обновляет другой компонент в БД через Hibernate.Это необходимо сделать в одной и той же транзакции, потому что если что-то не так (f.ex запускает исключение), необходимо выполнить откат для двух операций.

public class BeanDao extends ManagedSession {

public Integer save(Bean bean) {
    Session session = null;
    try {
        session = createNewSessionAndTransaction();

        Integer idValoracio = (Integer) session.save(bean);  // SAVE
        doOtherAction(bean);                                 // UPDATE

        commitTransaction(session);

        return idBean;
    } catch (RuntimeException re) {
        log.error("get failed", re);
        if (session != null) {
            rollbackTransaction(session);
        }
        throw re;
    }
}

private void doOtherAction(Bean bean) {
    Integer idOtherBean = bean.getIdOtherBean();
    OtherBeanDao otherBeanDao = new OtherBeanDao();
    OtherBean otherBean = otherBeanDao.findById(idOtherBean);
    .
    . (doing operations)
    .
    otherBeanDao.attachDirty(otherBean)
}
}

Проблема:

В случае, если

session.save(bean)

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

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

Каков наилучший способ реорганизовать это?

Ответы [ 3 ]

0 голосов
/ 05 мая 2011

Я большой поклонник декларативного управления транзакциями.Если вы можете сэкономить время, чтобы заставить его работать (кусок пирога с сервером приложений , таким как GlassFish или JBoss, и просто с Spring ).Если вы аннотируете свой бизнес-метод с помощью @TransactionAttribute(REQUIRED) (его можно даже установить по умолчанию) и он вызывает два метода DAO, вы получите именно то, что вам нужно: все будет зафиксировано за один раз или откатится через исключение.Это решение примерно так же слабо связано.

0 голосов
/ 05 мая 2011

Другие правы в том, что они принимают во внимание, что является обычной практикой в ​​настоящее время.

Но это не очень помогает вам в вашей текущей практике.

Что вам нужно сделать, это создать два новых метода DAO.Такие как CreateGlobalSession и CommitGlobalSession.

То, что они делают, аналогично вашим текущим процедурам создания и принятия.

Разница в том, что они устанавливают "глобальную" переменную сеанса (скорее всего, лучше всего это делать с ThreadLocal).Затем вы изменяете текущие подпрограммы, чтобы они проверяли, существует ли этот глобальный сеанс.Если твое создание обнаруживает глобальный сеанс, то просто верни его.Если ваш коммит обнаруживает глобальный сеанс, то он ничего не делает.

Теперь, когда вы хотите использовать его, вы делаете это:

try {
    dao.createGlobalSession();
    beanA.save();
    beanb.save();
    Dao.commitGlobalSession();
} finally {
    dao.rollbackGlobalSession();
}

Убедитесь, что вы обернули процесс в блок try, такчто вы можете сбросить свой глобальный сеанс в случае ошибки.

Хотя другие методы считаются наилучшей практикой, и в идеале вы могли бы однажды развиться до чего-то подобного, это поможет вам преодолеть горб с чуть более чем 3новые методы и изменение двух существующих методов.После этого остальная часть вашего кода остается прежней.

0 голосов
/ 05 мая 2011

Обычной практикой является управление транзакциями на уровне выше DAO, в службах или других классах бизнес-логики.Таким образом, вы можете, основываясь на логике бизнеса / сервиса, в одном случае выполнять две операции DAO в одной транзакции, а в другом - выполнять их в отдельных транзакциях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...