1

I have 2 clients using same Spring based REST Application deployed in tomcat. Depending on client I need to choose between data sources and transaction manager. How do I choose at runtime, which transaction manager to use?

    <bean id="First_dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
            <property name="url" value="${First_jdbc.url}" />
            <property name="driverClassName" value="${First_jdbc.driverClassName}" />
            <property name="username" value="${First_jdbc.username}" />
            <property name="password" value="${First_jdbc.password}" />
            <property name="removeAbandoned" value="true" />
            <property name="initialSize" value="20" />
            <property name="maxActive" value="30" />
            <!-- <property name="defaultAutoCommit" value="false" /> -->
   </bean>

    <bean id="Second_dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
            <property name="url" value="${Second_jdbc.url}" />
            <property name="driverClassName" value="${Second_jdbc.driverClassName}" />
            <property name="username" value="${Second_jdbc.username}" />
            <property name="password" value="${Second_jdbc.password}" />
            <property name="removeAbandoned" value="true" />
            <property name="initialSize" value="20" />
            <property name="maxActive" value="30" />
            <!-- <property name="defaultAutoCommit" value="false" /> -->
   </bean>

<bean id="First_TransactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        scope="singleton">
        <property name="dataSource" ref="First_dataSource" />
        <qualifier value="SC_TM"></qualifier>
</bean>

<bean id="Second_TransactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          scope="singleton">
          <property name="dataSource" ref="Second_dataSource" />
          <qualifier value="Second_TM"></qualifier>
</bean>   

In code how do choose @Transactional("????") at run time. If it is not possible with org.springframework.jdbc.datasource.DataSourceTransactionManager is there any other way of doing it?

user3488163
  • 49
  • 1
  • 1
  • 5

5 Answers5

1

Consider using the Spring provided AbstractRoutingDataSource instead of going down the path of choosing between multiple transaction managers. It will be a much cleaner solution.

See my answer to a similar question here :
https://stackoverflow.com/a/44167079/2200690

Suketu Bhuta
  • 1,871
  • 1
  • 18
  • 26
0

Using @Transactional, you can specify the transaction manager like this:

@Transactional("First_TransactionManager")

or

@Transactional("Second_TransactionManager")

depending on which one you want to use. Make sure to use inside the transactional method the correct Entity Manager / session factory. Those you also have to specify which one you want to inject with @PersistenceContext("nameOfPersistenceUnit").

Angular University
  • 42,341
  • 15
  • 74
  • 81
  • 1
    Thanks for the reply. I am aware of what you have answered that with @Transactional("First_TransactionManager") we can choose. But I don't want to hard-code. Can you give an example of @PersistenceContext with reference to above example – user3488163 Apr 06 '14 at 12:13
0

I do not know why you want to change between the 2 transaction manager may be you need to check Transaction manager chain solution, but in case you need this you can add your @transactional on Repo methods and do 2 Repos and manage it from the service layer as switch, otherwise I'm believing that there is solution could be done using AOP but it will need more time to think about it.

Bassem Reda Zohdy
  • 12,662
  • 3
  • 33
  • 39
  • As mentioned I am using same source code for two different clients. Depending on orgID, I need to select particular DB, and its corresponding Transaction Manager. I tried doing as you suggested through AOP, but I doubt its actually setting a transaction. For testing purpose, I deliberately threw a RunTimeException after execution of Stored Procedure that inserts a record. But my transaction was not rolledback.I will post the code after this. – user3488163 Apr 09 '14 at 14:16
  • Let me suggest another solution for you, if you aware about proxy design pattern, why not make proxy to be called when need to insert in database, inside this proxy check the orgID, and based on it you can call different Repository for each org, and inside these repositories you can mention the transaction manager name, is it could solve your issue ?? – Bassem Reda Zohdy Apr 10 '14 at 08:43
0

Problem is solved through AOP.

  1. Define multiple data sources and corresponding Transacation Manager (as I have shown in my base question)
  2. First_dataSource mapped with First_TransactionManager and Second_dataSource mapped with Second_TransactionManager
  3. Select which data source to use programatically depending on your business rules. In my case it was orgId

    public class DataSourceProvider { @Autowired DataSource First_dataSource; @Autowired DataSource Second_dataSource;

        public DataSource getDataSource(int orgId) {
    
            if (orgId == Constants.BUSINESS_PARTY_1)
                return Second_dataSource;
            else
                return First_dataSource;
        }
    
        public DataSource getFirst_dataSource() {
            return First_dataSource;
        }
    
        public void setFirst_dataSource(DataSource First_dataSource) {
            First_dataSource = First_dataSource;
        }
    
        public DataSource getSecond_dataSource() {
            return Second_dataSource;
        }
    
        public void setSecond_dataSource(DataSource Second_dataSource) {
            Second_dataSource = Second_dataSource;
        }
    }
    
  4. AOP Configuration:

user3488163
  • 49
  • 1
  • 1
  • 5
0
<tx:advice id="First_txAdvice" transaction-manager="First_TransactionManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" rollback-for="Exception" />
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="First_daoimplOperation"
        expression="execution(* in.company.common.service.CommonServiceImpl.*(..))" />

    <aop:advisor advice-ref="First_txAdvice" pointcut-ref="First_daoimplOperation" />
</aop:config>

<tx:advice id="Second_txAdvice" transaction-manager="Second_TransactionManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" rollback-for="Exception" />
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="Second_daoimplOperation"
        expression="execution(* in.company.common.service.CommonServiceImpl.*(..))" />

    <aop:advisor advice-ref="Second_txAdvice" pointcut-ref="Second_daoimplOperation" />
</aop:config>

all database related services should be in matching pointcut like in this case it is: in.company.common.service.CommonServiceImpl.*(..))

user3488163
  • 49
  • 1
  • 1
  • 5