I am writing an application using Spring. I want to write my JDBC code to be transactional, which I can achieve by using AOP:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="MyCustomException"/>
<tx:method name="*" rollback-for="MyCustomException" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.me.jdbc.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc" />
</aop:config>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="simpleDataSource" />
</bean>
<bean id="simpleDataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="org.h2.Driver" />
<property name="username" value="sa" />
<property name="password" value="" />
<property name="url" value="jdbc:h2:mem:aas;MODE=Oracle;DB_CLOSE_DELAY=-1;MVCC=true" />
</bean>
So all my methods in com.me.jdbc should be transactional and rollback if a MyCustomException occurs. This works so far.
I now want to write a unit test, but I want my unit test to be transactional, such that, once the test is complete the entire transaction will roll back and leave the database the way it was at the start.
I am able to achieve this by declaring:
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class })
#Transactional
on my Test class. The problem however is that I lose the transactions on the application code, i.e. after each test the transactions are rolled back, but if an exception occurs in the application code the insert is not rolled back and my unit test fails because I expect the table to be empty, but it isn't.
How would I go about achieving these two levels of transaction? Is it possible? I can work around the problem by writing a setup method for my unit tests that clears out all of the tables before each test. I am fine with this, but I thought it would be good to be able to use the rollback method of achieving the same thing.
As posted in the comments section, the issue is that by making the test method encapsulate a transaction, the rollback will only occur if the test throws the exception. This makes it impossible to verify the behaviour under test before the exception is thrown. I have gone with the approach of just emptying all the tables in the setup method (before each test).
Related
I am starting a new project. Previously I have used Spring MVC, XML based configuration. I am new to Spring Boot and tried to follow resources on web to start the new project, but I couldn't get it. Added required libraries and some simple setup but without success. Even Spring Security login is not successful, which I am doing of XML based one successfully.
My project setup is in Spring Boot 1.5.9 RELEASE, which includes Spring Data, Spring AOP/AspectJ, Spring Security, Spring WebMvc, Hibernate, MySQL, HikariCP, Transaction Management, Jackson and other relevant tech. I am not using ThymeLeaf. With the current configuration, I even can't access the static contents form resources/static/**.
I did tested 'aspect' for '#Controller' calls and works well. And now I tried Spring Security basic login with 'JSP' but couldn't get pass. I have uploaded the project setup in GitHubhttps://github.com/shakyasudeep/Spring-Boot-Setup-Config.
And I reviewed the #Transactional annotation for transaction management but it needs to be declared on every #Service which will be calling DAO #Repository, previously I implemented this one time in XML to reflect on the overall desired package.
The code is :
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
<!-- MUST have transaction manager, using aop and aspects -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<jpa:repositories base-package="com.eeposit.hub.dao"/>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="*" propagation="REQUIRED" rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="userServicePointCut"
expression="execution(* com.eeposit.hub.service.impl.*ServiceImpl.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="userServicePointCut" />
</aop:config>
I used EntityManagerFactory previously but now will be using Spring Data. I couldn't find a good reference for this(i.e.Declarative Transaction using Java code). How can we implement with just Java Code ?
Also I will also need #Async as well as Scheduler yet to configure.
And need to use message converters using Jackson, previously implemented by following code :
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="serializationInclusion" value="NON_NULL"/>
</bean>
</property>
<property name="supportedMediaTypes" value="application/json"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
File upload using :
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10000000" />
</bean>
And external resource mapping :
Please provide some help/advice by committing to the GitHub repo. I will be here to review your valuable advice.
In my project, I need to process more databases in one transaction.
1: using annotation, this reports error "duplicate annotation"
public class TransactionalService {
#Transactional("order")
#Transactional("account")
public void processTwoDatabases(String name) { ... }
}
xml segment as follow
<bean id="transactionManager1"
class="org.springframework.jdbc.DataSourceTransactionManager">
<qualifier value="order"/>
</bean>
<bean id="transactionManager2"
class="org.springframework.jdbc.DataSourceTransactionManager">
<qualifier value="account"/>
</bean>
2: But using xml, it works fine:
<tx:advice id="txAdvice1" transaction-manager="transactionManager1">
<!-- 定义方法的过滤规则 -->
<tx:attributes>
<tx:method name="process*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution (* com.service.impl.*.*(..))" id="services1"/>
<aop:advisor advice-ref="txAdvice1" pointcut-ref="services1"/>
</aop:config>
<tx:advice id="txAdvice2" transaction-manager="transactionManager2">
<tx:attributes>
<tx:method name="process*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution (* com.service.impl.*.*(..))" id="services2"/>
<aop:advisor advice-ref="txAdvice2" pointcut-ref="services2"/>
</aop:config>
Java does not allow multiple annotations of the same type on the same element, unless the annotation is (meta)-annotated with #Repeatable: Multiple annotations of the same type on one element?
But Transactional is not (meta)-annotated with #Repeatable, hence only one instance of #Transactional is allowed on a type or method.
You have to use XML in this case.
But please note that you do not get one transaction. You actually get two different transactions, one per transaction manager.
Also, you don't specify any datasource in your transaction manager definitions, so they both use the same datasource (and the same database).
To actually have one transaction, you may need XA Transaction support. Here are some links:
http://www.javaworld.com/article/2077714/java-web-development/xa-transactions-using-spring.html
http://www.javaworld.com/article/2077963/open-source-tools/distributed-transactions-in-spring--with-and-without-xa.html
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-jta.html
I am new to Spring Boot. I was trying to use Spring Boot with hibernate and mysql DB. I was trying to search for how to use spring's transactional configuration using spring boot. In normal spring application where you have xml files you define transaction using aop as below
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!--the transactional advice (what 'happens'; see the
<aop:advisor/>bean below)-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--the transactional semantics...-->
<tx:attributes>
<!--all methods starting with 'get' are read-only-->
<tx:method name="get*" read-only="true"/>
<!--other methods use the default transaction settings (see below)-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface-->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
<!--don't forget the DataSource-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<!--similarly, don't forget the PlatformTransactionManager-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
Using above config you can ask spring to attach a read-only transaction to only get* method and default transaction to all other methods.
How do you achieve this(defining transaction aop on methods using wildcard) using Spring Boot?
Tried searching this on google but couldn't find anything. :(
Please guide me to the solution or any preexisting link.
Thanks
From the reference doc, You can do this
#Configuration
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}
In you case you can disable the configuration altogether.
Link here.
http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html
As M. Deinum commented that if you can not skip the xml configuration then you can use it using #ImportResource annotation and provide your xml file name. The xml should be available on the classpath.
I have a situation where I need to setup a Proxy of a Pooled DataSource, my code is as follows:
<bean id="dataSourceBean" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="properties">
<props>
<prop key="c3p0.minPoolSize">0</prop>
<prop key="hc3p0.maxPoolSize">100</prop>
<prop key="hc3p0.timeout">60000</prop>
<prop key="c3p0.acquire_increment">10</prop>
<prop key="c3p0.max_statement">50</prop>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
</props>
</property>
</bean>
<bean id="dataSourceLockAdvice"
class="com.ndot2.datasource.DataSourceLockAdvice"/>
<bean id="dataSource" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="dataSourceBean"/>
<property name="interceptorNames">
<list>
<value>dataSourceLockAdvice</value>
</list>
</property>
</bean>
The problem that I'm having is that the connections aren't being closed anymore and it would seem that the destroy method of the proxied Datasource is no longer being called...
How would I call the Close method of the Proxied Bean? Or should I be implementing the Advice differently?
I've tried searching the Internet but I can't seem to find the answer to this, Help much appreciated!
EDIT:
As requested, here is my transaction management declarations (I'm using Appfuse)
<aop:config>
<aop:advisor id="userManagerTx" advice-ref="userManagerTxAdvice" pointcut="execution(* *..service.UserManager.*(..))" order="0"/>
<aop:advisor id="userManagerSecurity" advice-ref="userSecurityAdvice" pointcut="execution(* *..service.UserManager.saveUser(..))" order="1"/>
<aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* *..service.*Manager.*(..))" order="2"/>
</aop:config>
<!-- Enable #Transactional support -->
<tx:annotation-driven/>
<!-- Enable #AspectJ support -->
<aop:aspectj-autoproxy/>
<!-- Activates scanning of #Autowired -->
<context:annotation-config/>
<!-- Activates scanning of #Service -->
<context:component-scan base-package="com.ndot2.service"/>
<tx:advice id="txAdvice">
<tx:attributes>
<!-- Read-only commented out to make things easier for end-users -->
<!-- http://issues.appfuse.org/browse/APF-556 -->
<!--tx:method name="get*" read-only="true"/-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<tx:advice id="userManagerTxAdvice">
<tx:attributes>
<tx:method name="save*" rollback-for="UserExistsException"/>
</tx:attributes>
</tx:advice>
<bean id="userSecurityAdvice" class="com.ndot2.service.UserSecurityAdvice"/>
I don't have any #Transactional or #AspectJ driven Transaction management...
If you have the connection leaks in your application, the first step to do is to try to localize the place where the leaks occur, using the appropriate monitoring tools. As for c3p0, I believe it provides connection monitoring via JMX, as it's discussed in the related question.
So you can check with the debugging and monitoring tools if the leak occurs during some specific service call.
Then you should watch for different peculiarities: for example, in your configuration UserManager has more than one transaction advice, which could be the cause. In Spring container with annotated configuration a common error is that the bean in not being wrapped by transaction proxy, because the annotated transaction management is configured for the different IoC container. Another possible cause is that your method tries to manage transactions manually, and don't succeed in doing this correctly.
I have the following code in a Spring JdbcTemplate based dao -
getJdbcTemplate().update("Record Insert Query...");
int recordId = getJdbcTemplate().queryForInt("SELECT last_insert_id()");
The problem is that my sometimes my update and queryForInt queries get executed using different connections from the connection pool.
This results in an incorrect recordId being returned since MySql last_insert_id() is supposed to be called from the same connection that issued insert query.
I have considered the SingleConnectionDataSource but do not want to use it since it degrades the application performance. I only want single connection for these two queries. Not for all the requests for all the services.
So I have two questions:
Can I manage the connection used by the template class?
Does JdbcTemplate perform automatic transaction management? If i manually apply a transaction to my Dao method does that mean two transactions will be created per query?
Hoping that you guys can shed some light on the topic.
Update - I tried nwinkler's approach and wrapped my service layer in a transaction. I was surprised to see the same bug pop up again after sometime. Digging into the Spring source code i found this -
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
throws DataAccessException {
//Lots of code
Connection con = DataSourceUtils.getConnection(getDataSource());
//Lots of code
}
So contrary to what I thought, there isn't necessarily one database connection per transaction, but one connection for each query executed.
Which brings me back to my problem. I want to execute two queries from the same connection. :-(
Update -
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.jdbc.url}" />
<property name="username" value="${db.user}" />
<property name="password" value="${db.password}" />
<property name="maxActive" value="${db.max.active}" />
<property name="initialSize" value="20" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
autowire="byName">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception" timeout="30" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* service.*.*(..))" />
<aop:pointcut id="pointcut2" expression="execution(* *.ws.*.*(..))" />
<aop:advisor pointcut-ref="pointcut" advice-ref="transactionAdvice" />
<aop:advisor pointcut-ref="pointcut2" advice-ref="transactionAdvice" />
</aop:config>
Make sure your DAO is wrapped in a transaction (e.g. by using Spring's Interceptors for Transactions). The same connection will then be used for both calls.
Even better would be to have the transactions one level higher, at the service layer.
Documentation: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
Update:
If you take a look at the JavaDoc of the DataSourceUtils.getConnection() method that you referenced in your update, you will see that it obtains the connection associated with the current thread:
Is aware of a corresponding Connection bound to the current thread, for example
when using {#link DataSourceTransactionManager}. Will bind a Connection to the
thread if transaction synchronization is active, e.g. when running within a
{#link org.springframework.transaction.jta.JtaTransactionManager JTA} transaction).
According to this, it should work like you have set it up. I have used this pattern plenty of times, and never ran into any issues like you described...
Please also take a look at this thread, someone was dealing with similar issues there: Spring Jdbc declarative transactions created but not doing anything
This is my approach to do this:
namedJdbcTemplate.execute(savedQuery, map, new PreparedStatementCallback<Object>() {
#Override
public Object doInPreparedStatement(PreparedStatement paramPreparedStatement)
throws SQLException, DataAccessException {
paramPreparedStatement.execute("SET #userLogin = 'blabla123'");
paramPreparedStatement.executeUpdate();
return null;
}
});