Обнаружить исключение в AutoCloseable close () - PullRequest
10 голосов
/ 04 декабря 2011

Я хочу создать собственный класс AutoCloseable, чтобы я мог превратить это:

try {
    begin();
    doThings();
    commit();
} finally {
    if (transactionIsActive()) rollback();
}

в более легкий

try (Transaction t = begin()) { // too bad I have to store it in t though I don't use it
    doThings();
}

Transaction будет AutoCloseable здесьи в close() он будет фиксировать или откатывать транзакцию соответствующим образом.

Но чтобы это работало, мне нужно было бы обнаружить в Transaction.close(), произошло ли исключение внутри блока try или оно завершилось нормально.Возможно ли это вообще?

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

Ответы [ 3 ]

6 голосов
/ 04 декабря 2011

Самое близкое, что я мог придумать, все еще требует пометить успешность транзакции вручную как последний оператор блока:

class Transaction implements AutoCloseable {
    private boolean rollback = true;

    public void success() {
        rollback = false;
    }

    public void close() {
        if (rollback) doRollback();
        else doCommit();
        // …
    }
}

class Main {
    public static void main(String[] args) {
        try (Transaction t = new Transaction()) {
            doThings();
            t.success();
        }
    }
}
1 голос
/ 21 сентября 2012

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

Большую часть времени мой код содержит простые запросы, которые автоматически откатываютсяПримерно так:

try(Transaction t : database.beginTransaction()) {
  return t.selectUnique(Employee.class, "id=?", 200);
}  // implicit rollback here

Некоторые базы данных не любят запросы, которые откатываются таким образом, поэтому я решил эту проблему, различая транзакции «запись» и «чтение».Если это транзакция чтения, close () передаст ее, в противном случае откат.Он также проверит, что вы не выполняете никаких действий записи при создании транзакции только для чтения.Теперь я могу написать:

try(Transaction t : database.beginReadOnlyTransaction()) {
  return t.selectUnique(Employee.class, "id=?", 200);
}  // implicit commit here

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

Я понимаю, что это не то, что вы просили, но, возможно, это еще полезно.

0 голосов
/ 30 июля 2014

Самое близкое, что я смог получить, - это явно вызвать commit () и предположить, что любой код, выходящий из блока транзакции без этого, должен быть откатан. Это согласуется с транзакциями на других языках. Хотя вы можете забыть вызвать commit () (как я часто это делаю), по крайней мере, эта часть кода, скорее всего, будет протестирована. И невозможно забыть откатить исключение, которое менее вероятно будет иметь тестовое покрытие.

Это похоже на идею Миллимуза установить флаг:

try (Transaction t = new Transaction()) {
    doThings();
    t.success();
}

за исключением того, что вы просто используете активное состояние в качестве флага. То же количество кода, новый флаг не требуется. Это предполагает, что любая транзакция, для которой commit () не был вызван явным образом, должна быть откатана, что приведет к следующему коду:

try (Transaction t = new Transaction()) {
    doThings();
    t.commit(); // marks the transaction as successful...
}

class Transaction implements AutoCloseable {
    public void close() {
        if (isActive())
            doRollback();
    }

    ...
}

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

...