Spring boot with transactional configuration - java

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.

Related

Migrating from Spring MVC XML to Spring Boot Java based configuration with similar configuration

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.

transaction annotation or xml

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

Is it possible to write transactional unit tests that test transactional code?

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

How to run Destroy-method of Proxied Bean in Spring

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.

Spring 3.1 + Hibernate 4.1 Propagation.Supports issue

I'm migrating my project from Spring 3.0 +hibernate 3.6.x to S3.1 + H4.1
my new code is the following
<context:component-scan base-package="x.y.z">
</context:component-scan>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.x</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>x.y.z.entities.Student</value>
</list>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<aop:config>
<aop:pointcut id="daoServicePoint"
expression="execution(* x.y.z.StudentDao.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="daoServicePoint"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
When running getStudent method marker as SUPPORTS and read only I'm getting
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1024)
It used to be ok with Spring 3.0 and Hibernate 3.6.x now it was changed. I undestood from Spring forums that mow I need mark transaction REQUIRED if I need to use sessionFactory.getCurrentSession();
I used lower level technique in order to get maximum concurrent speed in my code.
When performing operations which require several get/save/update/ queries i did it the following way:
called method marked as SUPPORTS.
Performed all get queries which are also marked as SUPPORTS inside
first method.
then started queries which marked as REQUIRED inside the same method and this is a point
where my roll-able back transaction begins.
I got good performance improvement using this technique, but marking all my methods as REQUIRED destroys it.
How can work around it?
I encountered the same issue when experimenting with Spring and Hibernate 3 / 4.
It looks like this is a known issue, which is described in the following JIRA link.
https://jira.springsource.org/browse/SPR-9020
It looks like Hibernate 4 version of SpringSessionContext does not open a new session if there is no existing transaction/session open and the called method #Transactional is configured with propagation = Propagation.SUPPORTS.
i think you can still mark transaction as readonly. not sure if it has performance impact.

Categories