MyIbatis-Spring Transaction closeSession - java

When I use MyIbatis with spring transaction, using the Annotation driven declarative method, do I have do bother manage the session.
Without the transaction I usually do like this:
public int insertPnt(Movimenti value) {
try (SqlSession session = sqlSessionFactory.openSession()) {
for (Movimenti value : values) {
session.insert(
"com.sirio.cisl.dal.MovimentiMapper.insertSelective",
value);
}
} catch (Exception e) {
log.error("Error inserting movimenti "+user+" anno "+anno+" "+ e.getMessage());
throw e;
}
}
but from MyIbatis-Spring documentation I read
MyBatis SqlSession provides you with specific methods to handle
transactions programmatically. ...........
. That means that Spring will always handle your
transactions.
You cannot call SqlSession.commit(), SqlSession.rollback() or
SqlSession.close() over a Spring managed SqlSession.
So I wandering if I'm doing correct when I just adding the #Transaction annotation (and the <tx:annotation-driven> config) to the method.
#Transactional(rollbackFor=Exception.class)
public int insertPnt(Movimenti value) {
try (SqlSession session = sqlSessionFactory.openSession()) {
..............
}
Does the transaction manager take care of the session resourse? Or I have to remove the try-catch clause.
Thk

I post the same question to the MyIbatis-user group see. The response was useful:
To use MyBatis you need either an SqlSesion or a Mapper interface.
With "classic" MyBatis you get the sessions out of an
SqlSessionFactory but when using Spring this changes.
So I follow the example of the documentation:
I inject the Service class with a sqlSessionTemplate that internally takes care of opening and close the session.
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:myibatis.xml"/>
<property name="mapperLocations" value="classpath*:mappers/*.xml" />
</bean>
<bean id="asTableService" class="com.sirio.cisl.dal.AsTableService">
<property name="session" ref="sqlSession" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
<constructor-arg index="0" ref="sqlSessionFactory" />
<constructor-arg index="1" value="BATCH" />
</bean>
Then I can use the #Transactional annotation :
#Transactional(rollbackFor=Exception.class)
public int insertPnt(Movimenti value) {
session.create(......)
}
Davide

Related

Spring AOP - Multiple Interceptors not working together

I want to apply advice to a method helloWorld. I would like to throttle calls exceeding 'x' TPS and retry those calls.
So I have 2 interceptors -
Throttle Interceptor, which throttles call rate exceeds 'x'
Retry Interceptor, which retries if call is throttled.
The issue is that when I combine the 2 Interceptors the Throttling
interceptor does not seem work even if I provide the Rate as 0, that is the helloWorld methos gets executed.
If I only
use the Throttling Interceptor it works fine.
My Spring Configuration is like this.
<bean id="retryPolicy" class="xyz.RetryPolicyFactoryBean">
<property name="backoffCoefficient"><value>2</value></property>
<property name="multiplierMillis"><value>100</value></property>
<property name="maxDelayMillis"><value>2000</value></property>
<property name="maxAttempts"><value>2</value></property>
<property name="expirationDurationMillis"><value>60000</value></property>
<bean id="retryInterceptor" class="xyz.RetryInterceptor">
<constructor-arg index="0"><ref bean="retryPolicy"/></constructor-arg>
</bean>
<bean id="throttlingInterceptor" class="xyz.ThrottlingInterceptor">
<constructor-arg index="0"><value>key</value></constructor-arg>
</bean>
<bean id="helloWorld" class="xyz.helloWorld" />
<bean id="helloWorldProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="helloWorld" />
<property name="interceptorNames">
<list>
<value>retryInterceptor</value>
<value>throttlingInterceptor</value>
</list>
</property>
</bean>
Throttling Interceptor is like this:
#Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
if (isDecorated(methodInvocation)) {
if (throttler.isThrottled(key)) {
throw new ThrottlingException("Call throttled.");
}
}
return methodInvocation.proceed();
}
My retry interceptor is like this:
public class RetryInterceptor extends RetryAdvice {
public RetryInterceptor(RetryPolicy retryPolicy) {
super(retryPolicy);
}
#Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
return super.invoke(methodInvocation);
}
What am I missing here?
I followed the newer approach using AspectJ Annotations & got it working (atleast now the interceptors are being called).
Annotate the Class which contains the advice with #Aspect
Here is my Throttle Advice:
#Before(pointcut="execution(* com.company.xyz.method())")
public void invoke() throws ThrottlingException {
if (throttler.isThrottled(throttleKey)) {
throw new ThrottlingException("Call Throttled");
}
}
My retry Interceptor:
#AfterThrowing(pointcut="execution(* com.company.xyz.method())", throwing="exception")
public Object invoke(JoinPoint jp, ThrottlingException exception) throws Throwable {
return RetryingCallable.newRetryingCallable(new Callable<Object>() {
#Override
public Object call() throws Exception {
MethodSignature signature = (MethodSignature) p.getSignature();
Method method = signature.getMethod();
return method.invoke(jp.getThis(), (Object[]) null);
}
}, retryPolicy).call();
}
Relevant Spring Config
<bean id="retryInterceptor" class="com.company.xyz.RetryInterceptor">
<constructor-arg index="0"><ref bean="retryPolicy"/></constructor-arg>
</bean>
<bean id="throttlingInterceptor" class="com.company.xyz.ThrottlingInterceptor">
<constructor-arg><value>throttleKey</value></constructor-arg>
</bean>
<aop:aspectj-autoproxy/>
Another point to take care of is the Order in which the Interceptors are called. Use #Order Annotation to ensure the order.

Spring Transactional Rollback does not work

I have following Thread and transactional method, I have get thrown an exception to test rollback of insertion of DB but notthing is changed. What am I missing?
public class CleaningThread extends Thread {
public void run() {
try {
doJob();
} catch (Exception e) {
e.printStackTrace();
}
}
#Transactional(rollbackFor=Exception.class)
private void doJob() throws Exception {
//INSERT OPERATION
final BatchSqlUpdate bs = new BatchSqlUpdate
bs.flush()
throw new Exception("Custom exception")
//UPDATE
}
}
Application Context:
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:conf/offclear.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="cleaningThread" class="CleaningThread" scope="prototype"/>
Using Spring 3.1
You're invoking method doJob() from method run() of the same class. That's why you're working with real method, not proxied one.
Actually, this question was covered in this topic: One Service method invoke inner multiple method for Spring transaction

Implementation of timeout in LDAP

I have been handling an application in which we are using LDAP to fetch user details. Sometimes it will take more time to fetch user details. I want to implement time out on methods that fetch details so that we can avoid hanging transactions in server in worst case.
Here we are using LdapUtil class in which we have configured LdapTemplate class to fetch the required details.
How can we implement timeout on LDAP methods?
(In this case ldapTemplate.search(...) methods)
public class LdapUtil {
#Autowired(required = true)
#Qualifier(value = "ldapTemplateApp")
LdapTemplate ldapTemplate;
public Set < ProductGroup > findProducts(String UserId) {
final Set < ProductGroup > products = newHashSet();
// Lookup the user
String usrFilter = String.format(USERID_FILTER, globalUserId);
ldapTemplate.search("ou=Members", usrFilter, // note this line
new NameClassPairCallbackHandler() {
public void handleNameClassPair(NameClassPair nameClassPair) {
SearchResult result = (SearchResult) nameClassPair;
String user = result.getNameInNamespace();
String GrpFilter = String.format(GROUP_FILTER, user);
List < String > zonePrefixes = ldapTemplate.search("Zones", GrpFilter, // note this line
new AttributesMapper() {
public Object mapFromAttributes(Attributes attributes) throws NamingException {
return substringBeforeLast((String) attributes.get("cn").get(), "-") + "-";
}
});
}
});
products.remove(null);
return newHashSet(products);
}
}
We have one LDAP.xml in which ldapTemplete is configured
<beans xmlns="------">
<!-- LDAP -->
<bean id="contextSourceApp" class="org.springframework.ldap.pool.factory.PoolingContextSource">
<property name="contextSource" ref="contextSourceTargetApp" />
<property name="dirContextValidator">
<bean id="dirContextValidator"
class="org.springframework.ldap.pool.validation.DefaultDirContextValidator"/>
</property>
<property name="testOnBorrow" value="true" />
</bean>
<bean id="contextSourceTargetApp" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="${ldap.url}" />
<property name="base" value="${ldap.base.}" />
<property name="userDn" value="${ldap.user}" />
<property name="password" value="${ldap.password}" />
<property name="pooled" value="false" />
</bean>
<bean id="ldapTemplateApp" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSourceApp" />
</bean>
I have few queries:
How can we implement the TIMEOUT for LDAP methods and how to configure it?(In which class of LDAP framework timeout settings will be there)
Is there any way to configure them in xml file i.e. LDAP.xml(in this case)?
I found a solution.
I have added the following property in my ldap.xml file. So far it worked for me.
<bean id="contextSourceTargetApp"
class="org.springframework.ldap.core.support.LdapContextSource">
<property name="baseEnvironmentProperties">
<map>
<entry key="com.sun.jndi.ldap.connect.timeout" value="5000" />
</map>
</property>
</bean>
Please post any other solution if you have any idea about LDAP timeout implementation.
For ActiveDirectoryLdapAuthenticationProvider the solution with ldap.xml file did not work for me. Instead I added a jndi.properties file to the classpath with the following content:
com.sun.jndi.ldap.connect.timeout=500

Spring Transaction management

In java Spring, I am facing an issue regarding transaction rollback.
Example:
I have 3 DAOs in my code (A, B, C). All of them extend JDBCTemplate:
#Transaction(propagation=new_required)
public void serviceClassProc() throws Exception {
A.db1();
B.db2();
C.db3();
}
Now with the above code if I throw an exception in B.db2(), nothing gets rolled back.
Now if I modify B.db2 as following:
#Transaction(propagation=nested,rollbackon=Exception.class)
public void db2() throws Exception{
...
throw new Exception();
}
And then call serviceClassProc(), only the transaction in B.db2, gets rolled back.
I want an implementation where all transactions inside serviceClassProc() get rolled back.
Here are the 2 configurations I am using:
<bean id="bonddao" class="com.marki.bonds.staticdata.dao.MuniStaticDataDaoImpl"> <property name="dataSource" ref="c3p0DataSource" /> </bean> <bean id="dcldao" class="com.bonds.staticdata.dao.DclSettingsDaoImpl"> <constructor-arg ref="c3p0DataSource" /> </bean> <bean id="batchlogdao" class="com.bonds.staticdata.dao.MuniFeedHandlerBatchLogDaoImpl"> <constructor-arg ref="c3p0DataSource" /> </bean>
<bean id="bondsApplication" class="com.markit.bonds.staticdata.service.MuniRefDataSyncApp"> <property name="refdataService" ref="refDataSynchService" /> <property name="mailService" ref="mailSender"></property> <property name="batchLogger" ref="batchlogdao"></property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="c3p0DataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />
Where I am going wrong? Is it wrong to have 3 DAOs all extending JDBC template? Should all of them share same JDBCTemplate?
You should add rollbackon=Exception.class to the annotation of your service method and remove the transaction annotation entirely from the DAO methods. It is a bad idea to have transaction control at DAO level.
you can use also the : **org.springframework.transaction.interceptor.TransactionAspectSupport;**
here is an example that you can handle it:
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, transactionManager = "transactionManager")
public void messageHandler() {
try {
//TODO your own code
} catch (Exception ex) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
} finally {
}
}

Problem with spring quartz

I'm trying to invoke method based on some interval time, here are some beans inside applicationContext.xml
<bean id="MngtTarget"
class="com.management.engine.Implementation"
abstract="false" lazy-init="true" autowire="default" dependency-check="default">
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="MngtTarget" />
<property name="targetMethod" value="findItemByPIdEndDate"/>
</bean>
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<!-- 10 seconds -->
<property name="startDelay" value="10000" />
<!-- repeat every 50 seconds -->
<property name="repeatInterval" value="20000" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="simpleTrigger" />
</list>
</property>
</bean>
Here is the method I'm trying to invoke :
public List<Long> I need findItemByPIdEndDate() throws Exception {
List<Long> list = null;
try{
Session session = sessionFactory.getCurrentSession();
Query query = session.getNamedQuery("endDateChecker");
list = query.list();
for(int i=0; i<list.size(); i++)
{
System.out.println(list.get(i));
}
System.out.println("Total " + list.size());
}catch (HibernateException e){
throw new DataAccessException(e.getMessage());
}
return list;
}
Here is the exception message that I get :
Invocation of method 'findItemByPIdEndDate' on target class [class com.management.engine.Implementation] failed; nested exception is No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I've spent time googling alot so far also I've tried to modify my method like this :
public List<Long> I need findItemByPIdEndDate() throws Exception {
List<Long> list = null;
try{
Session session = sessionFactory.openSession();
Query query = session.getNamedQuery("endDateChecker");
list = query.list();
for(int i=0; i<list.size(); i++)
{
System.out.println(list.get(i));
}
System.out.println("Total " + list.size());
session.close();
}catch (HibernateException e){
throw new DataAccessException(e.getMessage());
}
return list;
}
And I get different error msg, I get : Invocation of method 'findItemByPIdEndDate' on target class [class com.management.engine.Implementation] failed; nested exception is could not execute query] , anyone knows what is this all about, any suggestions ? thank you
Also my queries.hbm.xml
<hibernate-mapping>
<sql-query name="endDateChecker">
<return-scalar column="PId" type="java.lang.Long"/>
<![CDATA[select
item_pid as PId
from
item
where
end_date < trunc(sysdate)]]>
</sql-query>
</hibernate-mapping>
For the second error ("could not execute the query"), I don't know and I'm really wondering what the session looks like.
In deed, AFAIK, the persistent context is not available to Quartz Jobs as nothing take care of establishing a Hibernate Session for them (Quartz runs outside the context of Servlets and the open session in view pattern doesn't apply here). This is why you get the first error ("No hibernate session bound to thread").
One solution for this is described in AOP – Spring – Hibernate Sessions for background threads / jobs. In this post, the author shows how you can use Spring AOP proxies to wire a hibernate interceptor that gives you access to the persistence context and it takes cares of closing and opening the sessions for you.
Didn't test it myself though, but it should work.
I too was facing the same "HibernateException: No Hibernate Session bound to thread" exception
2012-01-13 13:16:15.005 DEBUG MyQuartzJob Caught an exception
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687)
at com.company.somemodule.dao.hibernate.AbstractHibernateDaoImpl.getSession(AbstractHibernateDaoImpl.java:107)
at com.company.somemodule.dao.hibernate.SomeDataDaoImpl.retrieveSomeData(SomeDataDaoImpl.java:264)
and I solved it by following the example here.
Relevant code
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import com.company.somemodule.dao.SomeDataDao;
import com.company.somemodule.SomeData;
public class MyQuartzJob extends QuartzJobBean implements Runnable {
private boolean existingTransaction;
private JobExecutionContext jobExecCtx;
private static Logger logger = LoggerFactory.getLogger(MyQuartzJob.class);
private SomeDataDao someDataDao; //set by Spring
private Session session;
private SessionFactory hibernateSessionFactory; //set by Spring
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException
this.jobExecCtx = ctx;
run();
}
private void handleHibernateTransactionIntricacies() {
session = SessionFactoryUtils.getSession(hibernateSessionFactory, true);
existingTransaction = SessionFactoryUtils.isSessionTransactional(session, hibernateSessionFactory);
if (existingTransaction) {
logger.debug("Found thread-bound Session for Quartz job");
} else {
TransactionSynchronizationManager.bindResource(hibernateSessionFactory, new SessionHolder(session));
}
}
private void releaseHibernateSessionConditionally() {
if (existingTransaction) {
logger.debug("Not closing pre-bound Hibernate Session after TransactionalQuartzTask");
} else {
TransactionSynchronizationManager.unbindResource(hibernateSessionFactory);
SessionFactoryUtils.releaseSession(session, hibernateSessionFactory);
}
}
#Override
public void run() {
// ..
// Do the required to avoid HibernateException: No Hibernate Session bound to thread
handleHibernateTransactionIntricacies();
// Do the transactional operations
try {
// Do DAO related operations ..
} finally {
releaseHibernateSessionConditionally();
}
}
public void setHibernateSessionFactory(SessionFactory hibernateSessionFactory) {
this.hibernateSessionFactory = hibernateSessionFactory;
}
public void setSomeDataDao(SomeDataDao someDataDao ) {
this.someDataDao = someDataDao ;
}
}
Relevant bean configuration inside applicationContext.xml
<bean name="myJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.somecompany.worker.MyQuartzJob" />
<property name="jobDataAsMap">
<map>
<entry key="hibernateSessionFactory" value-ref="sessionFactory" />
<entry key="someDataDao" value-ref="someDataDao" />
</map>
</property>
</bean>
There's bug spring https://jira.spring.io/browse/SPR-9020
And there's workaround.
Configure session with hibernate.current_session_context_class property with this class:
https://gist.github.com/seykron/4770724

Categories

Resources