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

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

Related

Choose between muliple transaction managers at runtime

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

Spring Test transaction not rolling back

Using the spring testing framework, my transactions do not roll back and I have absolutely no idea why. I've spent a good few days trying to find answers on SO but to no avail so I decided to post.
Testfile
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/applicationContextTest.xml")
#Transactional
public class joinTest {
#Autowired
private DAO AccountDAO;//My DAO is annotated with #Repository
#Before
public void beforeMethod()
{
//log4j append code
}
#Test
public void saveMethod()
{
Account acct = new Account();
acct.setUsername("USER");
SmokeEvent evt = new SmokeEvent();
evt.setDateSmoked(new DateTime());
evt.setAccount(acct);
AccountDAO.addSmokeEvent(evt);
}
}
applicationContext.xml
<context:component-scan base-package="com.abstinence.Logic"/>
<context:annotation-config/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://82.165.173.127/testdb"/>
<property name="username" value="SA"/>
<property name="password" value=""/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.abstinence.Logic"/>
<property name="hibernateProperties">
<props>
<prop key ="dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="javax.persistence.validation.mode">none</prop>
</props>
</property>
</bean>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="hibernateDAOOperation" expression="execution(* com.abstinence.Logic.AccountDAO.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="hibernateDAOOperation"/>
</aop:config>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
I have checked the logs from log4j. The unusual thing is there is no mention of a rollback anywhere. There is a mention of the logs creating a session and inserting the data into the database but nothing about a rollback.
Someone please help me
Adding the following annotation to your test class #TransactionConfiguration(defaultRollback = true) should fix your issue.
You mention that the AccountDAO is annotated with the #Repository annotation. Have you also annotated the DAO with #Transactional? Without it, no transaction will be created when the addSmokeEvent() method is executed in the test. Consequently, the event will be added to your DAO, but the transaction manager cannot rollback the operation.

Spring JpaTransactionManager not saving Activiti entities to DB

Nothing about Activiti is being saved to database. Application entities ARE being saved to database. Below, in order are, the spring file, persitence.xml file and the test case.
Using SQL Server profiler, I see a separate database transaction being started for the database interaction caused by Activiti and further I see that separate transaction being rolled back instead of being committed. Other application db interaction is happening on another transaction and this particular transaction is being committed.
I thought, given my configurations, Activiti database interaction would happen on the same transaction as the rest of the application. I have gone through my configuration files and code numerous times and do not see anything wrong therein. Any ideas why a separate transaction is being started for the Activiti db interactions?
Of course the previous item is the critical question. However, it would also be interesting to know why is that separate transaction being rolled back?
Spring file :
<context:annotation-config/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="ActivitiTrialDataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver" />
<property name="url" value="jdbc:jtds:sqlserver://localhost:1433/ActivitiTrial" />
<property name="username" value="ActivitiTrial" />
<property name="password" value="ActivitiTrial" />
<property name="defaultAutoCommit" value="false" />
<property name="initialSize" value="5" />
</bean>
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="ActivitiTrialDataSource" />
<property name="persistenceUnitName" value="ActivitiTrial"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- Activiti -->
<bean id="activitiDataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="ActivitiTrialDataSource" />
</bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="databaseType" value="mssql" />
<property name="dataSource" ref="activitiDataSource" />
<property name="transactionsExternallyManaged" value="true" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="false" />
<property name="history" value="audit" />
<property name="jobExecutorActivate" value="false" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="activitiRepositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="activitiRuntimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="activitiTaskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="activitiHistoryService" factory-bean="processEngine" factory-method="getHistoryService" />
<bean id="activitiManagementService" factory-bean="processEngine" factory-method="getManagementService" />
persitence.xml file:
<persistence-unit name="ActivitiTrial">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.archive.autodetection" value="hbm,class"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.hbm2ddl.auto" value="none"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.ejb.metamodel.generation" value="disabled"/>
</properties>
</persistence-unit>
Test case:
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionConfiguration(defaultRollback=false)
#ContextConfiguration({"classpath:/springApplicationContext.xml"})
public class TrialTest {
#Autowired
RepositoryService activitiRepositoryService;
#Autowired
RuntimeService activitiRuntimeService;
#Autowired
TaskService activitiTaskService;
#PersistenceContext(unitName="ActivitiTrial")
EntityManager entityManager;
#Test
#Transactional
public void trialTest() throws Exception {
long entryMilliseconds = new Date().getTime();
activitiRepositoryService.createDeployment().addClasspathResource("process-definitions/neville.bpmn20.xml").deploy();
ApplicationEntity applicationEntity1 = new ApplicationEntity();
applicationEntity1.name = "App entity 1";
applicationEntity1.createDate = new Date();
Session hibernateSessionBeforeActiviti = ((Session) entityManager.getDelegate());
entityManager.persist(applicationEntity1);
entityManager.flush();
Map<String, Object> processVariables = new HashMap<String, Object>();
processVariables.put("ApplicationEntityID", applicationEntity1.id);
ProcessInstance processInstance = activitiRuntimeService.startProcessInstanceByKey("neville", processVariables);
String processInstanceId = processInstance.getId();
Task userTask = activitiTaskService.createTaskQuery().processInstanceId(processInstanceId).list().get(0);
ApplicationEntity applicationEntity2 = new ApplicationEntity();
applicationEntity2.name = "App entity 2";
applicationEntity2.createDate = new Date();
Session hibernateSessionAfterActiviti = ((Session) entityManager.getDelegate());
entityManager.persist(applicationEntity2);
entityManager.flush();
System.out.println("Leaving trialTest() in : " + (new Date().getTime() - entryMilliseconds) + " milliseconds.");
}
}
I've solved this problem by resolving conflict between MyBatis (JDBC) and Hibernate (JPA):
You should add jpaVendorAdapter property to entityManagerFactory bean:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="ActivitiTrialDataSource" />
<property name="persistenceUnitName" value="ActivitiTrial"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
For more details see answer of this question.
I've already waste a lot of time trying to pass common transaction manager to Spring and Activiti - and no result. No mean of full answer to your question, but I hope it will save you some time
Activiti forum:
http://forums.activiti.org/content/activiti-doesnt-submit-rollback
http://forums.activiti.org/content/activitispringhibernate-common-transaction-manager
Demo:
These repos illustrate that Activiti don't accept jpa/hibernate transactions:
https://github.com/JOLO-/activiti-jpa-spring-transaction-demo.git
https://github.com/JOLO-/activiti-hibernate-spring-transaction-demo.git
You also can see demo https://github.com/Activiti/Activiti (Activiti + Spring + Bitronix) it may help (I have no time to check it yet).
P.S.
As an alternative to common transaction I see compensation events.

JTA 2PC - with multiple datasource

I am new to JTA and i am trying to implement the JTA 2 pc .In business method
I want if fun1 fails then fun3 should also but its not happening.
#Transactional
fun(){
testDao1.fun1();
testDao2.fun3();
}
I had following things .
Create datasource as .I have multiple datasources
<bean id="dataSource" class="org.apache.commons.dbcp.managed.BasicManagedDataSource"
destroy-method="close">
<property name="driverClassName" value="org.postgresql.xa.PGXADataSource" />
<property name="url" value="${database.interimws.email.url}" />
<property name="username" value="${database.interimws.email.username}" />
<property name="password" value="${database.interimws.email.password}" />
<property name="defaultAutoCommit" value="false" />
<property name="maxActive" value="${database.maxactive}" />
<property name="maxIdle" value="${database.maxidle}" />
<property name="maxWait" value="${database.maxwait}" />
</bean>
having persistence unit which is of JTA type
created entityAMangerFactory using the above dataSource and Persistence unit
4.getting the javax.transaction.TransactionManager and Usertransaction object from Server and configured spring jta for annotation
Having a bussiness class which is annotated as transactional.
Make sure that you have XA-type drivers for every database that participates in two-phase commit; otherwise, it won't work.
How are you testing this? Do you have a mock version of DAO1 that throws an exception?

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