propagation NOT_SUPPORTED creates failing transaction in JDBC when using ChainedTransactionManager (JDBC + JPA) - jpa

I'm working on an existing Spring application that uses JDBC (DAO's extend NamedParameterJdbcDaoSupport). There were four datasources configured, each with it's own DataSourceTransactionManager. (though only one was registered with tx:annotation-driven for some reason)
I've recently added JPA (Spring-data-JPA) into the application and configured two entityManagerFactories (for now I don't need the other two datasources). I also configured two JpaTransactionManagers and removed the corresponding DataSourceTransactionManagers for these dataSources, since the JpaTransactionManagers can also be used for JDBC transactions. (correct me if I'm wrong)
It appears I need to be able to have distributed transactions, since the two datasources (to two different databases) need to be accessed (through JPA) in one service method. Since I did not have all I need to set up JTA (missing XA-driver for one of the databases) I've decided to give the Spring ChainedTransactionManager a try. Sadly this didn't work out as expected. All works fine if I just call a service method that only uses JPA.
Though when I call an existing service method that uses a JDBC find that has a class level #transactional annotation with it's propagation set to NOT_SUPPORTED and call another service method after that with a JPA call and a #transactional, I get an exception:
Caused by: java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder#462cf9d9] for key [org.jboss.jca.adapters.jdbc.WrapperDataSource#3fbb4c32] bound to thread [http-/127.0.0.1:8080-5]
at org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:189) [spring-tx-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:403) [spring-orm-3.2.5.RELEASE.jar:3.2.5.RELEASE]
After some debugging, I found out that the transactions in Spring get added to a map on a ThreadLocal in the "TransactionSynchronizationManager.bindResource" method. The problem is that when using a JDBC call with #transactional and propogation NOT_SUPPORTED, a transaction is made anyway and registered through that method. When the JpaTransactionManager tries to bind it's resource, it is already on the map (and not marked as void) which causes the error to occur.
Changing the propagation to the default "REQUIRED" for the service call that encapsulates the JDBC call fixes the problem.
I have no idea why Spring is still creating that transaction when the transactional annotation is NOT_SUPPORTED. And if it creates that transaction, it should not bypass the JpaTransactionManager.
So what I'd like to know is if there is some way to tell Spring to use the JpaTransactionManager also when it creates a transaction itself inside the NamedParameterJdbcDaoSupport. (Well actually the JdbcDaoSupport... Well actually the DataSourceUtils)
We're using Spring 3.2.5, spring-data-jpa 1.6.0 and I've used Hibernate 4.2.0 as JpaVendor.
This problem doesn't occur without the ChainedTransactionManager.
Datasources:
<bean id="dataSourceCompta" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/comptaDS"/>
</bean>
<bean id="dataSourceUnisys" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/insoverDS"/>
</bean>
<bean id="dataSourceInsoverwebMysql" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/insoverWebDS"/>
</bean>
<bean id="dataSourceBatch" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/batchDS"/>
</bean>
Single remaining JDBC transaction manager (no JPA counterpart):
<bean id="transactionManagerBatch" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceBatch"/>
</bean>
JPA Transaction Managers:
<bean id="jpaUnisysTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryUnisys"/>
<qualifier value="unisys" />
</bean>
<bean id="jpaMysqlTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryMysql"/>
<qualifier value="mysql" />
</bean>
My ChainedTransactionManager:
<bean id="chainedTransactionManager" class="org.springframework.data.transaction.ChainedTransactionManager">
<constructor-arg>
<list>
<ref bean="jpaUnisysTransactionManager" />
<ref bean="jpaMysqlTransactionManager" />
</list>
</constructor-arg>
</bean>
JPA Entity manager factories:
<bean name="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
<bean id="entityManagerFactoryUnisys" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/some-persistence.xml"/>
<property name="dataSource" ref="dataSourceUnisys"/>
<property name="persistenceUnitName" value="unisysPU"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="jpaProperties">
<!-- properties -->
</property>
</bean>
<bean id="entityManagerFactoryMysql" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/some-persistence.xml"/>
<property name="dataSource" ref="dataSourceCompta"/>
<property name="persistenceUnitName" value="mysqlPU"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="jpaProperties">
<!-- properties -->
</property>
</bean>
For now I've "fixed" this, by changing all the class-level transactional annotations to have propagation.REQUIRED (default) instead of NOT_SUPPORTED. Though I do not really like this solutions, since it might be somebody set those propagations to NOT_SUPPORTED with a good reason. I've also tried SUPPORTED, but using that had the same issue as NOT_SUPPORTED: a transaction was being made anyway by the Spring DataSourceUtils when the query was being executed by the NamedParameterJdbcDaoSupport DAO.
When no transactional annotation is set on the service, all works well too.

Related

How to correctly mix JTA and JPA transaction in Spring application

My Spring application was using JPA transaction manager but for some situations I would like to use JTA transaction manager. Therefore I updated Spring config to something like that:
<beans>
<!-- Old config -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- ...-->
<property name="jpaPropertyMap" ref="jpaPropertyMap"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven proxy-target-class="true"/>
<!-- ...-->
<!-- Updated config-->
<util:map id="jpaPropertyMap" key-type="java.lang.String">
<!-- ... ->
<entry key="javax.persistence.transactionType" value="jta"/>
<entry key="hibernate.transaction.jta.platform" value="Atomikos"/>
</util:map>
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
<property name="forceShutdown" value="false"/>
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"/>
<bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager"/>
<property name="userTransaction" ref="atomikosUserTransaction"/>
</bean>
<!-- ...-->
</beans>
Now in Java code when I need several JPA transactions (to different databases) to act as one transaction I annotate the starting service method with #Transactional("jtaTransactionManager"), while for situation when I need a single transaction I use standard #Transactional annotation.
But I have observed that, because of the properties
<entry key="javax.persistence.transactionType" value="jta"/>
<entry key="hibernate.transaction.jta.platform" value="Atomikos"/>
even if I start a sigle JPA transaction, Hibernate internally is also using Atomikos transaction manager. Is it possible to have Hibernate use Atomikos for JTA transaction and to not use it for single JPA transactions?

CrafterCMS: How to use crafter engine properties in an application-context bean?

I am connecting to an external database with a class that extends JdbcTemplate. My problem is that I can't use the globalProperties of the Groovy API because of the Jdbc.
I added these properties I needed in the server-config.properties:
studio.db.driverClassName
studio.db.url
studio.db.username
studio.db.password
I am trying to access them in my application-context.xml with this:
<bean id="jdbc" class="com.dbJdbcTemplate">
<constructor-arg ref="datasource"/>
</bean>
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="${studio.db.url}"/>
<property name="driverClassName" value="${studio.db.driverClassName}"/>
<property name="username" value="${studio.db.username}"/>
<property name="password" value="${studio.db.password}"/>
</bean>
I receive this error:
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not
get JDBC Connection; nested exception
org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '${studio.db.driverClassName}'
How do I access the properties from my bean correctly?
Add a <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" parent="crafter.properties"/> in your site application-context.xml, like is shown in here https://docs.craftercms.org/en/3.0/site-administrators/engine/engine-site-configuration.html#id3. That lines gives you access to Engine's global properties.

Invalid property 'durability' of bean class [org.springframework.scheduling.quartz.SchedulerFactoryBean]

I am upgrading Spring from Spring 2.x to Spring 4.2.4 and Quartz from 1.8 to 2.3
Following bean throws "Invalid property 'durability' of bean class [org.springframework.scheduling.quartz.SchedulerFactoryBean]"
If I remove the durability property, I get "org.quartz.SchedulerException: Jobs added with no trigger must be durable"
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="durability" value="true"/>
<property name="recover" value="false"/>
<property name="jobDetails">
<list>
<ref bean="deleteTempFileJobScheduler" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="deleteTempFileJobSchedulerTrigger" />
</list>
</property>
</bean>
Any suggestion?
Thanks in advance.
A Quartz-Job has a attribute called durability.
This property determines whether a Job without triggers should automatically deleted.
I.e. if you set
<property name="durability" value="true"/>
than Jobs remain in the JobStore even if no triggers point to it anymore.
But if you set
<property name="durability" value="false"/>
than jobs should be removed from the JobStore if no triggers point to it.
In that case Quartz gives you the mentioned Exception if you try to add a Job without Triggers to the JobStore (since you add a Job which will be immediately removed).
To prevent such an Exception you could add a Job together with an Trigger to the JobStore.
You need set durability property to JobDetailFactoryBean
(in your case, this is deleteTempFileJobScheduler) and not to SchedulerFactoryBeam
When we are creating job itself , you can add storeDurably(true)
. It is work for me .
JobDetail job = newJob(JobScheduler.class)
.withIdentity( sc.getId()+ "_Job")
.usingJobData(getJobDataMap(sc))
.storeDurably(true)
.build();

How to disable automatic Bean Validation in JPA entities

I'm using Bean Validation to check constraints on my model, but I don't know how to configure it so it only validates when I want it to. I found on that I could put this tag in my persistence.xml, <validation-mode>NONE</validation-mode> but it doesn't work.
I appreciate any kind of help.
I remember that i also had problems with that, here is my working example:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="punit" />
<property name="jpaPropertyMap">
<map>
<entry key="javax.persistence.validation.mode" value="none"/>
</map>
</property>
</bean>

Error with Spring ldap pooling

i build async jersey web services, and now i need to make some operations with ldap.
I have configure Spring beam.xml in this mode:
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="${ldap.url}" />
<property name="base" value="${ldap.base}" />
<property name="userDn" value="${ldap.userDn}" />
<property name="password" value="${ldap.password}" />
<property name="pooled" value="false" />
</bean>
<bean id="contextSource"
class="org.springframework.ldap.pool.factory.PoolingContextSource">
<property name="contextSource" ref="contextSourceTarget" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSource" />
</bean>
<bean id="ldapTreeBuilder" class="com.me.ldap.LdapTreeBuilder">
<constructor-arg ref="ldapTemplate" />
</bean>
<bean id="personDao" class="com.me.ldap.PersonDaoImpl">
<property name="ldapTemplate" ref="ldapTemplate" />
</bean>
But when i try to use ldap i have this error:
Error creating bean with name 'contextSource' defined in class path resource [config/Beans.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool/KeyedPoolableObjectFactory
In my project i have commons-pool2-2.2.jar lib, but still i have this error..i try to add commons-pool2-2.2.jar in TOMCAT_PATH/lib but not works..
UPDATE:
If i put commons-pool-1.6.jar it works.. but if i want to use pool2 how i can do? only i must change class inn commons-pool2-2.2.jar?
Updated Answer:
Since at least Spring LDAP 2.3.2 you can now use commons-pool2. Spring LDAP now provides two classes:
For commons-pool 1.x:
org.springframework.ldap.pool.factory.PoolingContextSource
For commons-pool 2.x:
org.springframework.ldap.pool2.factory.PooledContextSource
Details can be found here:
https://github.com/spring-projects/spring-ldap/issues/351#issuecomment-586551591
Original Answer:
Unfortunately Spring-Ldap uses commons-pool and not commons-pool2. As you have found the class org.apache.commons.pool.KeyedPoolableObjectFactory does not exist in commons-pool2 (it has a different package structure), hence the error.
There is a Jira issue for the Spring-ldap project asking them to upgrade/support commons-pool2:
https://jira.spring.io/browse/LDAP-316
Until that has been completed you will have to use commons-pool 1.6.