It's not supported on a pretty deep level, so no way (or at least I couldn't find any) of somehow adding it by putting up a custom extension.
What I did is a small dirty trick - my uses of @ReactiveTransactional
with a custom @MyAppTransactional
(call it however you want) interceptor.
This is solution is very specific for Postgres, and uses schema multi-tenancy method.
@Interceptor
@MyAppTransactional
// right after @ReactiveTransactional
@Priority(Interceptor.Priority.PLATFORM_BEFORE + 200 + 1)
@RequiredArgsConstructor
class MyAppTransactionalInterceptor {
// this is a request-scoped bean that holds my tenant id
@Inject
TenantId tenantId;
@Inject
Mutiny.Session session;
@Inject
Validator validator;
@Inject
MultiTenancyConfig multiTenancyConfig;
@AroundInvoke
Object intercept(InvocationContext ic) throws Exception {
if (!multiTenancyConfig.enabled()) {
return ic.proceed();
}
Class<?> returnType = ic.getMethod().getReturnType();
if (returnType != Uni.class) {
throw new RuntimeException("only Uni return types are supported with transactional methods");
}
if (!session.isOpen()) {
// just a sanity check - inheritance from @ReactiveTransactional makes sure that transaction is active by now
throw new IllegalStateException(
"unexpected state: Hibernate session is not active in tenant transaction interceptor");
}
String sessionTenantId = tenantId.get();
if (!validator.validate(sessionTenantId).isEmpty()) {
// double check just in case to avoid potential SQL injection
// (hint appreciated: how to properly escape postgres string literal here instead?)
throw new IllegalStateException(
"unexpected state: Hibernate session is not active in tenant transaction interceptor");
}
return session.createNativeQuery("SET LOCAL SCHEMA '" + tenantId.get() + "'")
.executeUpdate()
.flatMap((ignore) -> {
try {
return (Uni<?>) ic.proceed();
} catch (Exception e) {
return Uni.createFrom().failure(e);
}
});
}
}
Annotation itself:
@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
@ReactiveTransactional
public @interface MyAppTransactional {
}
Note for myself: need to improve this to avoid unneeded calls to "SET SCHEMA" with nested transactional method invocations.