Home > Mobile >  How to use transaction interceptor in a multi-tenant spring boot program?
How to use transaction interceptor in a multi-tenant spring boot program?

Time:12-07

I have a Spring Boot 2 Hibernate 5 Multi-tenant application that connected to multiple oracle databases. when I use JpaRepository, everything is fine, ie after recieving http request, the 'Interceptor' detects the data source and the 'MultiTenantConnectionProvider' implementation selects the correct DataSource to be used by TransactionManager. What is the problem? I want to use TransactionInterceptor to handle transactions on some methods(with method names) from specific beans(that extends JdbcDaoSupport or inject JdbcDaoSupport implementations). Sorry if I explained a little confusing. But for every http request, the data source of that beans is immutable(the default data source). while injected repositories operate on the correct data source within the correct transaction.

<bean id="txProxyTemplate" abstract="true"
          depends-on="transactionManager"
          class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager">
            <ref bean="transactionManager" />
        </property>
        <property name="transactionAttributes">
            <props>
                <prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
            </props>
        </property>
</bean>
<bean id="genericJdbcDao" parent="txProxyTemplate">
        <property name="target">
            <bean class="org.broker.dao.impl.BaseGenericJdbcDAOImpl">
                <property name="jdbcTemplate" ref="jdbcTemplate"/>
            </bean>
        </property>
</bean>

Can anyone help solve this problem?

CodePudding user response:

I finally solved the problem. Follow the code section:

@Bean("jdbcTemplate")
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public JdbcTemplate jdbcTemplate(
        @Qualifier("tenantIdentifierResolver") TenantIdentifierResolverImpl tenantResolver,
        @Qualifier("tenantConnectionProvider") DataSourceMultiTenantConnectionProviderImpl dataSourceProvider) {
    return new RhaJdbcTemplate(dataSourceProvider.selectDataSource(tenantResolver.resolveCurrentTenantIdentifier()));
}

@Bean(value = "transactionInterceptor")
public TransactionInterceptor transactionInterceptor(
        @Qualifier("txProxyTemplateConfig") Properties txProxyTemplateConfig,
        @Qualifier("transactionManager") PlatformTransactionManager transactionManager) {
    TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
    transactionInterceptor.setTransactionAttributes(txProxyTemplateConfig);
    transactionInterceptor.setTransactionManager(transactionManager);
    return transactionInterceptor;
}

@Bean("genericJdbcDAO")
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ProxyFactoryBean genericJdbcDAO(
        @Qualifier("jdbcTemplate") JdbcTemplate jdbcTemplate) throws ClassNotFoundException {
    BaseGenericJdbcDAOImpl genericJdbcDAO = new BaseGenericJdbcDAOImpl();
    genericJdbcDAO.setJdbcTemplate(jdbcTemplate);
    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    proxyFactoryBean.setTarget(genericJdbcDAO);
    proxyFactoryBean.setProxyInterfaces(new Class[] {BaseGenericJdbcDAO.class});
    proxyFactoryBean.setInterceptorNames("transactionInterceptor");
    return proxyFactoryBean;
}

Then inject BaseGenericJdbcDAO into the controller, as a sample:

@RestController
public class VoucherLineRest {
    @Resource(name = "genericJdbcDAO")
    private BaseGenericJdbcDAO genericJdbcDAO;
}

You will see that for every request the correct data source selected and TransactionInterceptor intercept every method called from BaseGenericJdbcDAO bean and apply the correct transaction propagation on it.

I hope you enjoy this experience.

  • Related