Problem with spring quartz - java

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

Related

Spring Batch Deadlock - Could not increment identity; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException

We are in the process of moving to Azure SQL Server from Oracle DB for our Spring Batch application.
I am getting the following error while trying to execute two different jobs at the same time that updates different tables however uses the same common BATCH_ tables
Caused by: org.springframework.dao.DataAccessResourceFailureException:
Could not increment identity; nested exception is
com.microsoft.sqlserver.jdbc.SQLServerException: Transaction (Process
ID 167) was deadlocked on lock resources with another process and has
been chosen as the deadlock victim. Rerun the transaction. at
org.springframework.jdbc.support.incrementer.SqlServerMaxValueIncrementer.getNextKey(SqlServerMaxValueIncrementer.java:124)
~[bat-applybatch-jobs-2.2.12-SNAPSHOT.jar:?] at
org.springframework.jdbc.support.incrementer.AbstractDataFieldMaxValueIncrementer.nextLongValue(AbstractDataFieldMaxValueIncrementer.java:125)
My Job Repository configuration
<job-repository id="jobRepository" isolation-level-for-create="READ_COMMITED" />
Database deadlock
<deadlock>
<victim-list>
<victimProcess id="process2a41675a4e8" />
</victim-list>
<process-list>
<process id="process2a41675a4e8" taskpriority="0" logused="280" waitresource="RID: 6:9:24682488:29" waittime="4984" ownerId="696000712" transactionname="implicit_transaction" lasttranstarted="2021-12-29T12:18:30.153" XDES="0x29a22bc4428" lockMode="U" schedulerid="4" kpid="52760" status="suspended" spid="173" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-12-29T12:18:30.157" lastbatchcompleted="2021-12-29T12:18:30.153" lastattention="1900-01-01T00:00:00.153" clientapp="Microsoft JDBC Driver for SQL Server" hostname="ServerName" hostpid="0" loginname="LoginName" isolationlevel="read committed (2)" xactid="696000712" currentdb="6" currentdbname="Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="unknown" queryhash="0xadc42a7474869694" queryplanhash="0x238c4f9df8a5d6cc" line="1" stmtstart="26" stmtend="146" sqlhandle="0x020000007654041849f4ffe980c136b592ccbe8260983e220000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" queryhash="0xadc42a7474869694" queryplanhash="0x238c4f9df8a5d6cc" line="1" stmtend="126" sqlhandle="0x0200000045a2af306ade799ae9ffa65edc0f722c526e26330000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
delete from LoginName.BATCH_STEP_EXECUTION_SEQ where ID < 10899 </inputbuf>
</process>
<process id="process2a42d680ca8" taskpriority="0" logused="420" waitresource="RID: 6:9:24682490:8" waittime="4984" ownerId="696000707" transactionname="implicit_transaction" lasttranstarted="2021-12-29T12:18:30.153" XDES="0x2a41ae18428" lockMode="U" schedulerid="7" kpid="53280" status="suspended" spid="129" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-12-29T12:18:30.153" lastbatchcompleted="2021-12-29T12:18:30.153" lastattention="1900-01-01T00:00:00.153" clientapp="Microsoft JDBC Driver for SQL Server" hostname="ServerName" hostpid="0" loginname="LoginName" isolationlevel="read committed (2)" xactid="696000707" currentdb="6" currentdbname="Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="unknown" queryhash="0xadc42a7474869694" queryplanhash="0x238c4f9df8a5d6cc" line="1" stmtstart="26" stmtend="146" sqlhandle="0x020000007654041849f4ffe980c136b592ccbe8260983e220000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" queryhash="0xadc42a7474869694" queryplanhash="0x238c4f9df8a5d6cc" line="1" stmtend="126" sqlhandle="0x02000000a0f1f51de77e1eefa19367c42fc9d1938c2075020000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
delete from LoginName.BATCH_STEP_EXECUTION_SEQ where ID < 10898 </inputbuf>
</process>
</process-list>
<resource-list>
<ridlock fileid="9" pageid="24682488" dbid="6" objectname="162589bb-bc36-4834-8bdc-e58a2deca742.LoginName.BATCH_STEP_EXECUTION_SEQ" id="lock2a043bbcc00" mode="X" associatedObjectId="72057594071547904">
<owner-list>
<owner id="process2a42d680ca8" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process2a41675a4e8" mode="U" requestType="wait" />
</waiter-list>
</ridlock>
<ridlock fileid="9" pageid="24682490" dbid="6" objectname="162589bb-bc36-4834-8bdc-e58a2deca742.LoginName.BATCH_STEP_EXECUTION_SEQ" id="lock29f5f1b7f00" mode="X" associatedObjectId="72057594071547904">
<owner-list>
<owner id="process2a41675a4e8" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process2a42d680ca8" mode="U" requestType="wait" />
</waiter-list>
</ridlock>
</resource-list>
</deadlock>
tried:
<job-repository id="jobRepository" isolation-level-for-create="READ_UNCOMMITED" />
<job-repository id="jobRepository"
isolation-level-for-create="ISOLATION_REPEATABLE_READ" />
<job-repository id="jobRepository"
isolation-level-for-create="SERIALIZABLE" />
I have created the tables as highlighted below
CREATE TABLE BATCH_STEP_EXECUTION_SEQ (
ID BIGINT IDENTITY(<last Oracle sequence value>, 1)
);
CREATE TABLE BATCH_JOB_EXECUTION_SEQ (
ID BIGINT IDENTITY(<last Oracle sequence value>, 1)
);
CREATE TABLE BATCH_JOB_SEQ (
ID BIGINT IDENTITY(<last Oracle sequence value>, 1)
);
what is the issue? How do I fix this?
Update: Job Definition
<bean id="simpleStep" class="org.springframework.batch.core.step.factory.SimpleStepFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="jobRepository" ref="jobRepository" />
<property name="startLimit" value="100" />
<property name="commitInterval" value="1" />
</bean>
Update#2:
Can I try something like this?
<bean id="informixIncrementer" class="com.bah.batch.informixsupport.InformixMaxValueIncrementerFactory"><property name="dataSource" ref="dataSource" />
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean" isolation-level-for-create="READ_COMMITTED" table-prefix="BATCH_">
<property name="incrementerFactory" ref="informixIncrementer"/>
</bean>
I'm afraid this is a known bug in Spring Batch 4.x that is currently only planned to be resolved with Spring Batch 5, which is not to be expected for a couple of months: https://github.com/spring-projects/spring-batch/issues/3927
You can emulate the fix by making local adjustments as in the commit of the fix: https://github.com/spring-projects/spring-batch/commit/fe911c8456bb49a69b1c84c78c0a0e0fdf224803, i.e.
Adjust the schema to contain sequences
Change the DataFieldMaxValueIncrementerFactory that is used to build the JobRepository.
I don't think the latter is feasible with pure XML configuration. The discussion on this issue contains some hints how it can be done in Java: https://github.com/spring-projects/spring-batch/issues/1448
The concrete customization depends on the customizations that you already have but it should work at least roughly as follows. You can apply the incrementer factory with a BatchConfigurer:
#Bean
public BatchConfigurer batchConfigurer(DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource) {
#Override
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(getDataSource());
factory.setTransactionManager(getTransactionManager());
factory.setIncrementerFactory(new MyIncrementerFactory(getDataSource()));
factory.afterPropertiesSet();
return factory.getObject();
}
};
}
where
public class MyIncrementerFactory implements DataFieldMaxValueIncrementerFactory {
private final DataSource dataSource;
public MyIncrementerFactory(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public DataFieldMaxValueIncrementer getIncrementer(String databaseType, String incrementerName) {
return new SqlServerSequenceMaxValueIncrementer(dataSource, incrementerName);
}
#Override
public boolean isSupportedIncrementerType(String databaseType) {
return true;
}
#Override
public String[] getSupportedIncrementerTypes() {
return null; // method should not get called anyway
}
}
and SqlServerSequenceMaxValueIncrementer should be the incrementer from the commit.
Basically, you need to overwrite incrementerFactory in JobRepository like so:
#Bean
public JobRepository jobRepository(DataSource dataSource, PlatformTransactionManager transactionManager) throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setIncrementerFactory(new DefaultDataFieldMaxValueIncrementerFactory(dataSource) {
#Override
public DataFieldMaxValueIncrementer getIncrementer(String incrementerType, String incrementerName) {
return new SqlServerSequenceMaxValueIncrementer(dataSource, incrementerName);
}
});
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
public SimpleJobLauncher jobLauncher(JobRepository jobRepository) {
SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
simpleJobLauncher.setJobRepository(jobRepository);
return simpleJobLauncher;
}
And here is the source code of SqlServerSequenceMaxValueIncrementer which you can include in your codebase.

MyIbatis-Spring Transaction closeSession

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

Transaction does not rollback in hibernate

I have a problem with rollback transaction. Below I wrote some of the configuration of beans. I do 2 SQL-queries: delete and update. And when UPDATE generates exception (constraint of foreign ket), the first query (DELETE) does not rollback. Could anyone please tell me where the problem is? I wrote only some of the configuration for sake of clarity, so if it's needed more information please let me know. Thanks in adnvance!
CONTEXT:
I have DAO layer with method removeUser:
public void removeUser(final Long id) {
getHibernateTemplate().execute(new HibernateCallback() {
#Override
public Object doInHibernate(Session session) throws HibernateException, SQLException {
executeUpdate("delete from table1 where user_id = ?", session, id);
executeUpdate("update table2 set user_id = null where user_id = ?", session, id);
return null;
}
private void executeUpdate(String queryString, Session session, Long... params) {
SQLQuery query = session.createSQLQuery(queryString);
for (int paramIndex = 0; paramIndex < params.length; paramIndex++) {
Long param = params[paramIndex];
query.setParameter(paramIndex, param);
}
query.executeUpdate();
}
});
}
This method is called from within service:
public void removeUser(Long id) {
userDao.removeUser(id);
}
This service is configured via spring:
<bean name="adminUserService" parent="txProxyServiceTemplate">
... setting properties ...
</bean>
<bean id="txProxyServiceTemplate" abstract="true"
class="com.xalmiento.desknet.ui.server.service.transaction.GWTTransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="remove*">PROPAGATION_NESTED</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="nestedTransactionAllowed" value="true"/>
</bean>
Try with
<prop key="remove*">PROPAGATION_REQUIRED</prop>
I don't think all the database server does support the transaction.

Spring 3's Quartz 2 implementation doesn't support requestsRecovery anymore

According to the Quartz 2's documentation
RequestsRecovery - if a job "requests recovery", and it is executing
during the time of a 'hard shutdown' of the scheduler (i.e. the
process it is running within crashes, or the machine is shut off),
then it is re-executed when the scheduler is started again. In this
case, the JobExecutionContext.isRecovering() method will return true.
Spring3 has support for both Quartz 1.x and Quartz 2.x. If we use Quartz 1.x then we need to use following configuration for creating JobDetail bean:
<bean id="ldapSynch" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.edfx.adb.scheduling.job.LDAPSynchronizer" />
<property name="requestsRecovery" value="true" />
</bean>
Internally org.springframework.scheduling.quartz.JobDetailBean extends org.quartz.JobDetail and in Quartz 1.x org.quartz.JobDetail has a setter public void setRequestsRecovery(boolean shouldRecover).
But if we use Quartz 2.x implementation of Spring3 then the aforesaid configuration changes as:
<bean id="ldapSynch" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.edfx.adb.scheduling.job.LDAPSynchronizer" />
<property name="requestsRecovery" value="true" />
</bean>
And also in Quartz 2.x there is no such org.quartz.JobDetail class, instead it is an interface and eventually org.springframework.scheduling.quartz.JobDetailFactoryBean doesn't takes the parameter <property name="requestsRecovery" value="true" /> anymore.
How could we pass this important parameter to the Quartz's scheduler?
After looking into the source code of the class org.springframework.scheduling.quartz.JobDetailFactoryBean and source code of Quartz 2.0 I have found that there is a kind of init method in org.springframework.scheduling.quartz.JobDetailFactoryBean which is public void afterPropertiesSet(); within this method the instance of org.quartz.JobDetail is being created. Fortunately this instance of org.quartz.JobDetail can be accessible via a method public JobDetail getObject() of the class org.springframework.scheduling.quartz.JobDetailFactoryBean.
In Quartz 2.0 the class org.quartz.impl.JobDeialImpl implements the interface org.quartz.JobDetail; so the instance of org.quartz.JobDetail in org.springframework.scheduling.quartz.JobDetailFactoryBean is actually the instance of org.quartz.impl.JobDeialImpl.
So I created a class com.edfx.adb.scheduling.ADBJobDetailFactoryBean which extends org.springframework.scheduling.quartz.JobDetailFactoryBean and overrides the afterPropertiesSet() method as:
package com.edfx.adb.scheduling;
import org.quartz.impl.JobDetailImpl;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
public class ADBJobDetailFactoryBean extends JobDetailFactoryBean {
private boolean requestsRecovery;
public ADBJobDetailFactoryBean() {
super();
}
#Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
JobDetailImpl jobDetail = (JobDetailImpl) getObject();
jobDetail.setRequestsRecovery(isRequestsRecovery());
}
public boolean isRequestsRecovery() {
return requestsRecovery;
}
public void setRequestsRecovery(boolean requestsRecovery) {
this.requestsRecovery = requestsRecovery;
}
}
And changed the spring bean configuration as:
<bean id="ldapSynch" class="com.edfx.adb.scheduling.ADBJobDetailFactoryBean">
<property name="jobClass" value="com.edfx.adb.scheduling.job.LDAPSynchronizer" />
<property name="requestsRecovery" value="true" />
</bean>
And voila. I have tested this by stopping running server while the scheduler was executing the task and when I restarted the server the scheduler was started executing the unfinished job.
Hope it will help someone.
#tapas-bose while searching for some quartz info I come across your post. It looks like Spring 3.2 support setting the property requestsRecovery. I don't know which version of Spring 3.? you have mentioned in your answer with which you faced the problem.
I set requestsRecovery property directly through setter injection and it worked for me.
<bean name="continousMonitoringProfileNotificationPushProcessing" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.mycompany.projectApp.jobs.ContinousMonitoringNotificationPushJobBean" />
<property name="jobDataAsMap">
<map>
<entry key="executionSuccessful" value="true"/>
</map>
</property>
<property name="durability" value="true" />
<property name="requestsRecovery" value="true"/>
</bean>
When I decompiled code for JobDetailFactoryBean it is overriding afterPropertiesSet method which creates JObDetailImpl instance and set requestsRecovery, (piece of decompiled code as below)
public class JobDetailFactoryBean implements FactoryBean<JobDetail>, BeanNameAware, ApplicationContextAware, InitializingBean
{
private String name;
private String group;
private Class jobClass;
private JobDataMap jobDataMap = new JobDataMap();
private boolean durability = false;
private boolean requestsRecovery = false;
...
..
..
public void afterPropertiesSet()
{
if (this.name == null) {
this.name = this.beanName;
}
if (this.group == null) {
this.group = "DEFAULT";
}
if (this.applicationContextJobDataKey != null) {
if (this.applicationContext == null) {
throw new IllegalStateException("JobDetailBean needs to be set up in ApplicationContext to be able to handle an 'applicationContextJobDataKey'");
}
getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext);
}
Class<?> jobDetailClass;
try
{
jobDetailClass = getClass().getClassLoader().loadClass("org.quartz.impl.JobDetailImpl");
}
catch (ClassNotFoundException ex) {
jobDetailClass = JobDetail.class;
}
BeanWrapper bw = new BeanWrapperImpl(jobDetailClass);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", this.name);
pvs.add("group", this.group);
pvs.add("jobClass", this.jobClass);
pvs.add("jobDataMap", this.jobDataMap);
pvs.add("durability", Boolean.valueOf(this.durability));
pvs.add("requestsRecovery", Boolean.valueOf(this.requestsRecovery));
So we don't need to manually extend the JobFactoryBean class and set the requestsRecovery property.

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 {
}
}

Categories

Resources