I have a quartz job in spring 4 and I am using JPA hibernate to update database value through quartz job but I am getting javax.persistence.TransactionRequiredException: Executing an update/delete query
I don't understand what kind of configuration is missing in quartz job. I referred to SpringBeanAutowiringSupport example still update is failing but select is working fine.
Below is my code
@Configuration
@ComponentScan("com.stock")
public class QuartzConfiguration {
@Autowired
private ApplicationContext applicationContext;
@Bean
public JobDetailFactoryBean jobDetailBalanceCarryForward(){
JobDetailFactoryBean factory = new JobDetailFactoryBean();
factory.setJobClass(BillingCroneSvcImpl.class);
Map<String,Object> map = new HashMap<String,Object>();
map.put("task", "balanceCarryForward");
factory.setJobDataAsMap(map);
factory.setGroup("BalanceCarryForwardJob");
factory.setName("balance carry forward");
return factory;
}
@Bean
public CronTriggerFactoryBean cronTriggerBalanceCarryForward(){
CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
stFactory.setJobDetail(jobDetailBalanceCarryForward().getObject());
stFactory.setStartDelay(3000);
stFactory.setName("balancCarryForwardTrigger");
stFactory.setGroup("balanceCarryForwardgroup");
stFactory.setCronExpression("0 0/1 * 1/1 * ? *");
return stFactory;
}
@Bean
public SpringBeanJobFactory springBeanJobFactory() {
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
schedulerFactory.setJobFactory(springBeanJobFactory());
schedulerFactory.setTriggers(cronTriggerBalanceCarryForward().getObject());
return schedulerFactory;
}
}
Below class where quartz executeInternal method is written
@Service
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Autowired
private BillingCroneRepo billingCroneRepo;
public class BillingCroneSvcImpl extends QuartzJobBean implements BillingCroneSvc {
@Override
@Transactional
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(context);
billingCroneRepo.updateBalance();
// this method throws exception javax.persistence.TransactionRequiredException: Executing an update/delete query
}
}
App config class
@EnableWebMvc
@EnableTransactionManagement
@Configuration
@ComponentScan({ "com.stock.*" })
@Import({ SecurityConfig.class })
@PropertySource("classpath:jdbc.properties")
public class AppConfig extends WebMvcConfigurerAdapter {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
@Resource
private Environment env;
@Bean(name = "dataSource")
public DriverManagerDataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
driverManagerDataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
driverManagerDataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
driverManagerDataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return driverManagerDataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class);
entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setJpaProperties(hibProperties());
return entityManagerFactoryBean;
}
private Properties hibProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT,env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL,env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
return properties;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
@Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource=new ReloadableResourceBundleMessageSource();
String[] resources= {"classpath:messages"};
messageSource.setBasenames(resources);
return messageSource;
}
@Bean
public LocaleResolver localeResolver() {
final CookieLocaleResolver ret = new CookieLocaleResolver();
ret.setDefaultLocale(new Locale("en_IN"));
return ret;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor localeChangeInterceptor=new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/Angular/**").addResourceLocations("/Angular/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/email_templates/**").addResourceLocations("/email_templates/");
registry.addResourceHandler("/fonts/**").addResourceLocations("/fonts/");
registry.addResourceHandler("/img/**").addResourceLocations("/img/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/Landing_page/**").addResourceLocations("/Landing_page/");
}
@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
org.springframework.core.io.Resource[] resources = new ClassPathResource[] { new ClassPathResource("application.properties") };
pspc.setLocations(resources);
pspc.setIgnoreUnresolvablePlaceholders(true);
return pspc;
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
// through below code we directly read properties file in jsp file
@Bean(name = "propertyConfigurer")
public PropertiesFactoryBean mapper() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("application.properties"));
return bean;
}
}
Can anybody please assist me how to resolve transational issue in spring JPA with quartz
I'm not the Spring specialist, but I think
new ...
doesn't work with @TransactionalThanks you all for your help. Finally I autowired EntityManagerFactory instead of persitance EntityManager and it is working fine. I tried all scenario but nothing worked to inject spring transactional in quartz so finally autoriwed entitymanagerfactory
Below is my repo class code.
It's because quartz is using the bean instead of the proxy generated for @Transactional.
Use either MethodInvokingJobDetailFactoryBean (instead of inheriting QuartzJob) or use a dedicated wrapper quarz bean (inheriting from QuartzJob) that call the spring bean (not inheriting from QuartzJob) having the @Transactionnal annotation.
EDIT : this is in fact not the problem
The problem is here :
By passing the class, I presume that Quartz will instantiate it itself, so Spring won't create it and won't wrap the bean in a Proxy that handle the @Transactionnal behaviour.
Instead you must use something along the line :