Choose between muliple transaction managers at runtime - spring

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?

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").

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.

Problem is solved through AOP.
Define multiple data sources and corresponding Transacation Manager (as I have shown in my
base question)
First_dataSource mapped with First_TransactionManager and Second_dataSource mapped with Second_TransactionManager
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;
}
}
AOP Configuration:

<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.*(..))

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

Related

How to join Spring JMS transactions from two different connection factories?

I am using different connection factories for sending and receiving messages, having trouble with partial commit issues incase of delivey failures. jms:message-driven-channel-adapter uses the receiveConnectionFactory ro receive the messages from the queue. jms:outbound-channel-adapter uses the deliverConnectionFactory to send the messages multiple to downstream queues. We have only one JmsTransactionManager which uses the receiveConnectionFactory and the jms:outbound-channel-adapter configured with session-transacted="true".
<beans>
<bean id="transactionManager"
class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="receiveConnectionFactory" />
</bean>
<bean id="receiveConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory">
<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName" value="${mq.host}" />
<property name="channel" value="${mq.channel}" />
<property name="port" value="${mq.port}" />
</bean>
</property>
<property name="sessionCacheSize" value="${receive.factory.cachesize}" />
<property name="cacheProducers" value="${receive.cache.producers.enabled}" />
<property name="cacheConsumers" value="${receive.cache.consumers.enabled}" />
</bean>
<bean id="deliverConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory">
<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName" value="${mq.host}" />
<property name="channel" value="${mq.channel}" />
<property name="port" value="${mq.port}" />
</bean>
</property>
<property name="sessionCacheSize" value="${send.factory.cachesize}" />
<property name="cacheProducers" value="${send.cache.producers.enabled}" />
<property name="cacheConsumers" value="${send.cache.consumers.enabled}" />
</bean>
<tx:advice id="txAdviceNew" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="send" propagation="REQUIRES_NEW" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdviceNew" pointcut="bean(inputChannel)" />
<aop:advisor advice-ref="txAdviceNew" pointcut="bean(errorChannel)" />
</aop:config>
<jms:message-driven-channel-adapter
id="mdchanneladapter" channel="inputChannel" task-executor="myTaskExecutor"
connection-factory="receiveConnectionFactory" destination="inputQueue"
error-channel="errorChannel" concurrent-consumers="${num.consumers}"
max-concurrent-consumers="${max.num.consumers}" max-messages-per-task="${max.messagesPerTask}"
transaction-manager="transactionManager" />
<jms:outbound-channel-adapter
connection-factory="deliverConnectionFactory" session-transacted="true"
destination-expression="headers.get('Deliver')" explicit-qos-enabled="true" />
</beans>
When there is MQ exception on any one destination, the partial commit occurs and then the failure queue commit happens. I am looking to see if I am missing some configuration to join the transactions so that the partial commit never happens.
I tried with only one connection factory for both send and receive (receiveConnectionFactory) and the parital commit is not happening, everything works as expected.
I tried with only one connection factory for both send and receive (receiveConnectionFactory) and the parital commit is not happening, everything works as expected.
That's the right way to go in your case.
I see that your two ConnectionFactories are only different by their objects. Everything rest looks like the same target MQ server.
If you definitely can't live with only one ConnectionFactory, you should consider to use JtaTransactionManager or configure org.springframework.data.transaction.ChainedTransactionManager for two JmsTransactionManagers - one per connection factory.
See Dave Syer's article on the matter: https://www.javaworld.com/article/2077963/open-source-tools/distributed-transactions-in-spring--with-and-without-xa.html

Why does AOP managed transactions not work in my unit tests?

I've configured Spring solely based on XML. Transactions are managed by Hibernate, and I'm using AOP to manage transaction boundaries.
However, when running unit tests, why is it that my AOP configuration not kicks in? I need to add annotations such as #Transactional to the test method to make sure that database operations gets wrapped in transactions, even though I've configured AOP to wrap calls to service methods in transactions.
Why doesn't my AOP configuration apply to tests also?
applicationcontext.xml
<aop:aspectj-autoproxy/>
<!-- Data Source -->
<bean id="companyDomainDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.master.url}" />
<property name="user" value="${jdbc.master.username}" />
<property name="password" value="${jdbc.master.password}" />
<property name="acquireIncrement" value="1" />
<property name="minPoolSize" value="2" />
<property name="maxPoolSize" value="20" />
<property name="maxIdleTime" value="4" />
</bean>
<!-- Session Factory -->
<bean id="companyDomainSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="companyDomainDataSource"/>
<property name="mappingResources">
<list>
<value>/com/company/pas/entity/mapping/partner.hbm.xml</value>
</list>
</property>
</bean>
<!-- Transaction Manager -->
<bean id="companyDomainTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="companyDomainSessionFactory"/>
</bean>
<!-- Transaction Advice -->
<tx:advice id="companyDomainTransactionAdvise" transaction-manager="companyDomainTransactionManager">
<tx:attributes>
<!--<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>-->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethods" expression="within(com.company.pas.dao.*)"/>
<aop:advisor advice-ref="companyDomainTransactionAdvise" pointcut-ref="serviceMethods"/>
</aop:config>
unit test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/applicationContext.xml"})
#TransactionConfiguration(transactionManager="companyDomainTransactionManager", defaultRollback=true)
public class PartnerCreateTest extends AbstractActionTest {
#Autowired #Qualifier("createPartnerAction") AbstractAction action;
#Test
#Transactional
public void testExecute() throws Exception {
// Create partner.
Representation rep = mock(Representation.class);
Request req = mock(Request.class);
Response resp = mock(Response.class);
when(rep.getText()).thenReturn(getContentsOf("com/company/pas/entity/xml/partner-create.xml"));
when(req.getEntity()).thenReturn(rep);
AbstractRequestModel crm = action.getRequestModelParser().parse(req, resp);
action.execute(crm);
}
}
When you add #Transactional to a test method you get a rollback by default. When you don't you should expect transactions to commit (they don't know they are in a test).

springmvc using excel template to export

The code works well but when I setUrl to use my template in Excel, I get this error:
ApplicationObjectSupport instance [key.kotori.sms.utils.reports.StockReportsExcel: unnamed] does not run in an ApplicationContext'.
public class StockReportsExcel extends AbstractExcelView {
private StockReports reports;
public StockReportsExcel(StockReports reports) {
this.reports = reports;
//super.setUrl("report/stock.xls");
}
#Override
protected void buildExcelDocument(
Map<String, Object> model,
HSSFWorkbook workbook,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
workbook.createSheet().createRow(0).createCell(0).setCellValue("kotori");
RequestEncoding.encoding(request, response, "test.xls");
response.setContentType("application/vnd.ms-excel");
}
}
Controller
#RequestMapping("/excel.do")
public View excel(Params params) {
StockReports reports = this.stockReportService.reports(params);
StockReportsExcel excel = new StockReportsExcel(reports);
return excel;
}
I didn't set any ViewResolver in applicationContext.xml. Is there anything I'm missing in appicationContext.xml?
Here is the applicationContext.xml file.
<context:component-scan base-package="key.kotori.sms"></context:component-scan>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/sms"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mappingDirectoryLocations">
<list>
<value>classpath:key/kotori/sms/entity</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
hibernate.format_sql=true
</value>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:advice id="txAdivce" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get" read-only="true" />
<tx:method name="list*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdivce" pointcut="execution(* key.kotori.sms.service.*.*(..))" />
</aop:config>

Spring DefaultMessageListenerContainer MDP Initialization

What is the best way to perform initialization on DefaultMessageListenerContainer initialization? Currently I am waiting for first message, and keep track of it using a boolean variable, which isn't so pretty. Is there a better way ? I want to read and load certain data into the cache once the Message Driven POJO is started up, so the message processing is faster.
(Edited)
Spring Config Fragement:
<bean id="itemListener" class="com.test.ItemMDPImpl" autowire="byName" />
<bean id="itemListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="itemListener" />
<property name="defaultListenerMethod" value="processItems" />
<property name="messageConverter" ref="itemMessageConverter" />
</bean>
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="itemMqConnectionFactory" />
<property name="destinationName" value="${item_queue_name}" />
<property name="messageListener" ref="itemListenerAdapter" />
<property name="transactionManager" ref="jtaTransactionManager" />
<property name="sessionTransacted" value="true" />
<property name="concurrentConsumers" value="1" />
<property name="receiveTimeout" value="3000" />
</bean>
I would like to have some initialization done before any message is received by the listener.
Can't you just use #PostConstruct to annotate a method on ItemMDPImpl to perform startup initialization, just like any other Spring bean?
4.9.6 #PostConstruct and #PreDestroy

Apply Transaction Management Spring

I have a j2ee application running on spring framework. I am trying to apply transaction management using aop but apparently it won't work. I am trying to apply transaction to a function from a class named RegisterBLogic with function name execute(ParamObject obj). My function inserts into a database. I also put a throw ne Exception my function to force the throwing of exception.
Inside userManagerContext:
<bean id="RegisterBLogic"
scope="singleton"
class="jp.co.anicom.fw.web.usermanager.blogic.RegisterBLogic">
<property name="queryDAO"
ref="queryDAO" /> <property
name="updateDAO" ref="updateDAO" />
</bean>
Inside ApplicationContext
<bean id="TerasolunaDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="PrototypeDataSource" />
</bean>
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="execute*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="*" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- AOPの設定 -->
<aop:config>
<aop:pointcut id="blogicBeans" expression="bean(*BLogic)" />
<aop:pointcut id="serviceBeans" expression="bean(*Service)" />
<aop:advisor pointcut-ref="blogicBeans" advice-ref="transactionInterceptor" />
<aop:advisor pointcut-ref="serviceBeans" advice-ref="transactionInterceptor" />
</aop:config>
Yeah these beans already exist. I have this bean declaration under userManagerCOntext.xml. This xml is loaded in the struts config through
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/userManager/userManagerContext.xml"/>
</plug-in>
I found the problem. Currently i am using a data source from a JNDI.
class="org.springframework.jndi.JndiObjectFactoryBean">
i changed it to normal data source with property defaultAutoCommit set to false
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="oracle.jdbc.OracleDriver" />
<property name="url"
value="jdbc:oracle:thin:#192.168.178.82:1521:anicom" />
<property name="username" value="jay" />
<property name="password" value="jay" />
<property name="initialSize" value="5" />
<property name="maxActive" value="10" />
<property name="defaultAutoCommit" value="false" />
</bean>
How do I use JNDI data source but be able to set a property somewhat similar to defaultAutoCommit to false. I am using Oracle weblogic server and my data source is stored in it accessed through JNDI
First of all, this XML is malformed (should be </tx:advice>).
Are the beans you want to wrap in AOP already present when this configuration is processed?

Resources