transaction annotation or xml - java

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

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.

ClassCastException Proxy36 cannot be cast to SessionImplementor after Hibernate/Spring 4 upgrade

EDIT: I am not asking what a ClassCastException is. I am asking what is causing it in DetachedCriteria under this specific configuration of Spring 4/Hibernate 4.
I'm trying to upgrade some legacy code to Spring 4/Hibernate 4 and I've hit a wall, as Google isn't turning up much.
I am trying to run a JUnit test on a very simple Hibernate repository, and it is failing with
java.lang.ClassCastException: com.sun.proxy.$Proxy36 cannot be cast to org.hibernate.engine.spi.SessionImplementor
at org.hibernate.criterion.DetachedCriteria.getExecutableCriteria(DetachedCriteria.java:84)
at com.my.app.rest.domain.repository.AbstractHibernateRepository$6.doInHibernate(AbstractHibernateRepository.java:163)
...
This is happening in Hibernate's org.hibernate.criterion.DetachedCriteria class:
/**
* Get an executable instance of Criteria to actually run the query.
*
* #param session The session to associate the built Criteria with
*
* #return The "executable" Criteria
*/
public Criteria getExecutableCriteria(Session session) {
impl.setSession( (SessionImplementor) session );
return impl;
}
When it tries to set the Session (which attempts to cast it to a SessionImplementor), it throws the ClassCastException.
I suspect this may be an AOP issue, but am not sure where to start looking.
I'm using Spring 4.3.2.RELEASE, and Hibernate 4.3.5.Final.
hibernate-context.xml:
<bean id="xxxSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="xxxDataSource" />
<property name="mappingResources">
<list>
<value>hibernate/xxxUploadDocResponseInfo.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${xxx.hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${xxx.hibernate.showsql}</prop>
<prop key="hibernate.hbm2ddl.auto">${xxx.hibernate.hbm2ddl}</prop>
<prop key="format_sql">${xxx.hibernate.formatsql}</prop>
<prop key="hibernate.query.substitutions">true 1, false 0</prop>
</props>
</property>
<alias name="xxxSessionFactory" alias="sessionFactory" />
</bean>
transaction-context.xml:
<bean id="xxxTransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice id="xxxTxAdvice" transaction-manager="xxxDatasourceTransactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
<!-- all methods begin with save have the transaction -->
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="inactivate*" propagation="REQUIRED"/>
<tx:method name="complete*" propagation="REQUIRED"/>
<tx:method name="reset*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="flag*" read-only="true"/>
<tx:method name="doWork*" propagation="REQUIRES_NEW" />
</tx:attributes>
</tx:advice>
<bean id="xxxDatasourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="xxxDataSource" />
</bean>
<aop:config>
<aop:pointcut id="allBusiness" expression="execution(public * com.blah.xxx.rest.business.*Business.*(..))"/>
<aop:advisor advice-ref="xxxTxAdvice" pointcut-ref="allBusiness"/>
</aop:config>
AbstractHibernateRepository.java:
public abstract class AbstractHibernateRepository<E extends Entity, S extends Serializable> extends HibernateDaoSupport {
...
#SuppressWarnings("unchecked")
protected E get(final DetachedCriteria detachedCriteria) {
return (E) getHibernateTemplate().execute(new HibernateCallback<E>() {
public E doInHibernate(Session session) {
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
return (E) criteria.uniqueResult();
}
});
}
...
}
Faced same issue on legacy code after upgrade to spring boot 2.4.0. Fixed by using entityManager.unwrap(SessionImplementor.class) to retrieve the session to be used for DetachedCriteria in my scenario.
See HibernateTemplate#doExecute
enforceNativeSession - whether to enforce exposure of the native Hibernate Session to callback code
As you can see at GrepCode:
protected Session createSessionProxy(Session session) {
return (Session) Proxy.newProxyInstance(
session.getClass().getClassLoader(), new Class<?>[] {Session.class},
new CloseSuppressingInvocationHandler(session));
}
the created proxy implements only the interface Session not the interface SessionImplementor.
You have to replace HibernateTemplate#execute with HibernateTemplate#executeWithNativeSession.
If anyone having this issue with FullTextEntityManager, the answer from #Gardella works, however, this link suggests to update the Hibernate Search ORM dependency to 5.11.6.Final, which also resolves the issue.

How to create transactional proxy through factory-method?

I have a spring bean that is created through factory-method and I also need to use #Transactional facility. So, when I create it as follows:
<bean id="myBean" class="pack.age.MyBean" factory-method="create">
<constructor-arg ref="someBean" />
</bean>
where
public class MyBean implements MyInterface{
private final SomeBean someBean;
public static MyInterface create(SomeBean someBean){
MyBean retVal = new MyBean(someBean);
//Create a thread and run it.
//Do some other job that doesn't suit for constructor
}
private MyBean(SomeBean someBean){
this.someBean = someBean;
}
}
Now, when I try to inject the bean into anothe bean:
public class MyAnotherBean{
private MyInterface myInterface;
public boid setMyInterface(MyInterface myInterface){
this.myInterface = myInterface;
}
}
declared as
<bean id="myAnotherBean" class="pack.age.MyAnotherBean">
<property name="myInterface" ref="myBean" />
</bean>
The actual myBean instance, not a proxy is injected. Since it's not a proxy, I can't use Spring #Transactional facility.
How can I inject a proxy when constructing an object through static factory-method?
In this case just enabling transaction annotation under the beans declaration should work:
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>
But if not you can try enable the transaction declaratively:
<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 MyInterface interface -->
<aop:config>
<aop:pointcut id="myBeanOperation" expression="execution(* x.y.service.MyInterface.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myBeanOperation"/>
</aop:config>

Spring boot with transactional configuration

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.

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

Categories