私はしばらくこれに苦労してきました。Jon Skeet の回答は嫌いです。なぜなら、開発者 (つまり私) が誤ってcommitOnClose()
. 開発者がコードのブロックを離れたときに、commit() または rollback() のいずれかを強制的に呼び出す方法が必要です。
ラムダの例外とチェック例外はうまく連携しないため、適切な解決策を見つけるには少し戸惑いましたが、最終的に私と私の同僚は、次のような作業を可能にするコードを思いつきました:
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 の codepath 戻り値チェックにより、コードをコミットする必要があるかどうかを常に明示するように強制されることに注意してください。
以下のコードを使用して実装されます。
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 は演習として読者に任せます)