Я некоторое время боролся с этим. Мне не нравится ответ Джона Скита, потому что разработчик (то есть я) может случайно забыть позвонить commitOnClose()
. Я хочу, чтобы разработчик был вынужден вызвать commit() или rollback(), когда он покидает блок кода.
Lambda и проверил исключения не играют хорошо вместе, чтобы правильное решение приняло немного озадачивает, но в конце концов я и Коллега моего придумали кусок кода, который позволяет работать так:
TransactionEnforcer.DbResult<String> result = transactionEnforcer.execute(db -> {
try {
db.someFunctionThatThrowsACheckedException();
} catch (TheException e) {
return failure("fallback value");
}
return success(db.getAFancyValue());
});
result.ifPresent(v -> System.out.println(v));
Обратите внимание, как вы можете возвращать значения, можете проверить, был ли код успешным или нет, а проверка возврата кодовой страницы java гарантирует, что вы всегда будете явно о том, должен ли код быть совершен или нет.
Это осуществляется с помощью кода ниже:
package nl.knaw.huygens.timbuctoo.database;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
public class TransactionEnforcer {
private final Supplier<DbClass> dbClassFactory;
public TransactionEnforcer(Supplier<DbClass> dbClassFactory) {
this.dbClassFactory = dbClassFactory;
}
public <U> DbResult<U> execute(Function<DbClass, DbResult<U>> actions) {
DbClass db = dbClassFactory.get();
try {
DbResult<U> result = actions.apply(db);
if (result.isSuccess()) {
db.close(true);
} else {
db.close(false);
}
return result;
} catch (RuntimeException e) {
db.close(false);
throw e;
}
}
public static class DbResult<T> {
private final Optional<T> value;
private final boolean success;
private DbResult(T value, boolean success) {
this.value = Optional.of(value);
this.success = success;
}
public static <T> DbResult<T> success(T value) {
return new DbResult<T>(value, true);
}
public static <T> DbResult<T> success() {
return new DbResult<T>(null, true);
}
public static <T> DbResult<T> failure(T value) {
return new DbResult<T>(value, false);
}
public static <T> DbResult<T> failure() {
return new DbResult<T>(null, false);
}
public boolean isSuccess() {
return success;
}
public Optional<T> getValue() {
return value;
}
}
}
(я оставить DbClass в качестве упражнения для читателя)
Я вижу, что вы правы в принципе. Но я разработчик API, и я не контролирую код API-запросов потребителей. Я должен добавить это к вопросу –
Как вы догадались, что это действительно касается транзакций, кстати? :-) –
@LukasEder: Если вы управляете API, вы можете сообщить своим пользователям API, что им нужно вызвать дополнительный метод. Это, вероятно, самый простой вариант для ваших потребителей, и на самом деле вы ничего не можете сделать с прямым методом 'close()', насколько мне известно. –