How to create transactional proxy through factory-method? - java

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>

Related

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

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.

#Async and Mockito issue

I have this class.
#Service
public class ConcurrentService{
public Map<String, Object> createList(){
this.asynCall();
}
#Async("taskExecutor")
private Future<Map<String, Object>> asynCall(){
.....
return new AsyncResult<Map<String, Object>>(mapResultMap);
}
}
My spring config is:
<task:annotation-driven executor="taskExecutor" mode="aspectj" />
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="50" />
<property name="maxPoolSize" value="300" />
<property name="queueCapacity" value="30" />
</bean>
And My Mockito Unit test
#RunWith(MockitoJUnitRunner.class)
public class ConcurrentServiceTest{
#InjectMocks
private ConcurrentService concurrentService;
#Mock(name = "taskExecutor")
private ThreadPoolTaskExecutor taskExecutor;
#Test
public void test1(){
Assert.assertNotNull(concurrentService.createList();
}
}
If I run this I obtained java.lang.IllegalArgumentException: BeanFactory must be set on AnnotationAsyncExecutionAspect to access qualified executor 'taskExecutor' from the org.springframework.scheduling.aspectj.AbstractAsyncExecutionAspect.ajc$around$org_springframework_scheduling_aspectj_AbstractAsyncExecutionAspect
If I remove the qualifier in the annotation and just left #Async, this test run perfectly, but If I add the #Async("taskExecutor") the error comes again.
I believe Spring does not need to run as this is just Unit test, what can I do to disable aop in Mockito or what should I do to use the name "taskExecutor"
With mode="aspectj" in
<task:annotation-driven executor="taskExecutor" mode="aspectj" />
you are using Aspectj weaving, and you needs to use aspectj-maven-plugin to weave aspects in compile time. For using Spring's AOP default framework with proxies in runtime, change to
<task:annotation-driven executor="taskExecutor" />

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.

JPA Inject same EntityMgr in multiple components

I will describe the issue short:
I have a SessionLayer on which I want to manage transactions
I have servicelayer which actually performs data access logic
Servicelayer has the entityMgr injected using JPA annotation:
#PersistenceContext private EntityManager eMgr;
Session Layer does not have the entityMgr injected, but by using following spring config, it does correctly manage the transactions:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost/test" p:username="test" p:password="test" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:packagesToScan="com.model"> <!-- scans for entities (model) -->
<property name="persistenceProvider">
<bean class="org.hibernate.ejb.HibernatePersistence" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" />
<context:component-scan base-package="com.session" />
<context:component-scan base-package="com.session.util" />
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="testSessionOperation" expression="execution(* com.session.TestSession.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="testSessionOperation" />
</aop:config>
The above works fine ... However what if i wanted a reference to the entitymgr in the SessionLayer.
I have tried adding the #PersistenceContext private EntityManager eMgr; annotation. This injects an entityMgr, but not the same one as the servicelayer, so the changes on servicelayer are not committed when using that enitymgr to commit.
What would be the way to go if I wanted the entityMgr used by the servicelayer also on the session layer (without passing it back and forth offcourse).
I though about moving enitymgr to session and passing it service, this works but i don't want to pass it everywhere it is required, it should be injected.
Thx in advance!
Use additional class that contains common entity manager
#Component
public class EntityManagerProvider implements IEntityManagerProvider
{
#PersistenceContext(unitName = "PU_NAME")
private EntityManager entityManager;
public EntityManager getEntityManager()
{
return entityManager;
}
}
your business bean
#Service
pucli class MyService
{
#Autowired
private IEntityManagerProvider entityManagerProvider;
//.. entityManagerProvider.getEntityManager() -- accessss to entity manager
}

Categories