rudald
rudald

Reputation: 404

Jooq Jersey custom Transactional annotation - jooq how to share transaction?

I am going in a really narrow scenario since I'm using embedded tomcat 10.1, along with jooq 3.19 and jersey 3.1.

What I am trying to achieve is to create my own @Interceptor that is supposed to act as a @Transactional annotation. I managed to create jooq's DSLContext in the RequestScoped and pass it to the interceptor and all the repositories instances within the request, share the same DSLContext. Then in the interceptor the idea was to wrap method's body within a jooq transaction. Autocommit is set to false.

I found out that any query performed on a database is executed in a different transaction, so the transaction is not shared. It is not shared even within single repository as in TenantRepository class.

How to make DSLContext aware that is in transaction context? Am I missing something obvious? Is DSLContext right object to be injected?

Also if you think I am far away of implementing Transactional interface please let me know what is ahead of me since I don't really want to go to a rabbit hole.

Why all of this? I had an idea that it would be really nice to have service and repository layer split so that there is no DSLContext injected on the Service layer classes.

Was inspired by this article: https://blog.dejavu.sk/intercepting-jersey-resource-method-calls/

I have attached the code below, thank you all in advance!

package com.example.config;

import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jooq.DSLContext;

public class TransactionInterceptor implements MethodInterceptor {
    private Provider<DSLContext> serviceProvider;

    @Inject
    public TransactionInterceptor(Provider<DSLContext> serviceProvider) {
        this.serviceProvider = serviceProvider;
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        DSLContext dslContext = serviceProvider.get();
        dslContext.transactionResult(configuration -> {
            return methodInvocation.proceed();
        });
        return null;
    }
}
@Service
public class TenantService {
    private final TenantRepository tenantRepository;
    private final ShopRepository shopRepository;

    @Inject
    public TenantService(TenantRepository tenantRepository,
                         ShopRepository shopRepository) {
        this.tenantRepository = tenantRepository;
        this.shopRepository = shopRepository;
    }

    @TransactionalMethod
    public void create(TenantCreateRequest createRequest) {
        UUID tenantId = tenantRepository.insert(createRequest.getName());
        UUID shopId = shopRepository.insert(tenantId, createRequest.getShop().toShop());
    }
}

Then I added another 2 queries to see if they are executed within the same transaction, but they are not.

@Service
public class TenantRepository {
    private static final Logger LOGGER =  LoggerFactory.getLogger(TenantService.class);
    private final DSLContext dsl;

    @Inject
    public TenantRepository(DSLContext dsl) {
        this.dsl = dsl;
    }

    public UUID insert(String name) {
        LOGGER.info("Current transaction: " + dsl.select(field("txid_current()", BigInteger.class))
                .fetchOneInto(BigInteger.class));

        UUID tenantId = UUID.randomUUID();
        dsl.insertInto(TENANT)
                .set(TENANT.TENANT_ID, tenantId)
                .set(TENANT.CREATED_AT, LocalDateTime.now())
                .set(TENANT.NAME, name)
                .execute();
        LOGGER.info("Current transaction: " + dsl.select(field("txid_current()", BigInteger.class))
                .fetchOneInto(BigInteger.class));
        return tenantId;
    }
}

Upvotes: 0

Views: 28

Answers (0)

Related Questions