I am using the jdbi library like so:
try { return jdbi.withHandle(handle -> handle .createQuery("...") .mapTo(String.class) .one()); } catch (final JdbiException e) { if (DatabaseExceptionChecker.isTransientException(e)) { throw new MyCustomException("Transient database exception", e); } else { throw e; } }
This try catch pattern is repeated a few times, with various different queries passed to the jdbi.withHandle
method. Another example:
try { return jdbi.withHandle(handle -> handle .createUpdate("...") .execute()); } catch (final JdbiException e) { if (DatabaseExceptionChecker.isTransientException(e)) { throw new MyCustomException("Transient database exception", e); } else { throw e; } }
The jdbi.withHandle
method has this signature:
public <R, X extends Exception> R withHandle(HandleCallback<R, X> callback) throws X {
I am trying to find a way to reduce the duplication around the try-catch, so that I can use something like this instead:
handleTransientExceptions(() -> jdbi.withHandle(handle -> handle .createQuery("...") .mapTo(String.class) .one()) }
i.e. the handleTransientExceptions
function would contain the try-catch boilderplate.
I’ve begun with the following:
@FunctionalInterface private interface JdbiCall { public <R, X extends Exception> R call() throws X; } public <R, X extends Exception> R handleTransientExceptions(final JdbiCall jdbiCall) throws MyCustomException { try { return jdbiCall.call(); } catch (final JdbiException e) { if (DatabaseExceptionChecker.isTransientException(e)) { throw new MyCustomException("Transient database exception", e); } else { throw e; } } }
however, when I try to call it like this:
return handleTransientExceptions(() -> jdbi.withHandle(handle -> handle .createQuery("...") .mapTo(String.class) .one()) }
I get the error:
reason: cannot infer type-variable(s) R,X (argument mismatch; invalid functional descriptor for lambda expression method <R,X>()R in interface com.acmecorp.MyService.JdbiCall is generic)
Advertisement
Answer
Types cannot be inferred, because your functional interface JdbiCall
doesn’t have neither generics at the class declaration level nor parameters for call
method by which these types can be inferred. So, your FI might look like this:
@FunctionalInterface interface JdbiCall<R, X extends Exception> { R call() throws X; }
and handleTransientExceptions
method:
public <R, X extends Exception> R handleTransientExceptions(final JdbiCall<R, X> jdbiCall) throws Exception { try { return jdbiCall.call(); } catch (final JdbiException e) { if (DatabaseExceptionChecker.isTransientException(e)) { throw new MyCustomException("Transient database exception", e); } else { throw e; } } }
Also it can be simplified to:
@FunctionalInterface interface JdbiCall<R> { R call() throws Exception; }
handleTransientExceptions
method:
public <R> R handleTransientExceptions(final JdbiCall<R> jdbiCall) throws Exception { try { return jdbiCall.call(); } catch (final JdbiException e) { if (DatabaseExceptionChecker.isTransientException(e)) { throw new MyCustomException("Transient database exception", e); } else { throw e; } } }