如何在Spring JPA中为quartz工作运行更新查询

时间:2016-12-18 22:13:59

标签: spring-data-jpa quartz-scheduler spring-4

我在春季4有一个石英工作,我正在使用JPA hibernate通过石英工作更新数据库值,但我得到了javax.persistence.TransactionRequiredException: Executing an update/delete query

我不明白石英作业中缺少什么样的配置。我提到SpringBeanAutowiringSupport示例仍然更新失败,但选择正常。

以下是我的代码

@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;
  }
}

编写quartz executeInternal方法的类

@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;
}

}

任何人都可以帮我解决如何解决春季JPA中的石英转换问题

3 个答案:

答案 0 :(得分:1)

谢谢大家的帮助。最后,我自动安装了EntityManagerFactory而不是persity EntityManager,它运行正常。我尝试了所有场景,但没有任何工作在石英中注入弹簧事务,所以最后autoriwed entitymanagerfactory

以下是我的回购类代码。

@Repository
public class BillingCroneRepoImpl implements BillingCroneRepo {
 /*@PersistenceContext
 private EntityManager entityManager;*/

 @Autowired
 EntityManagerFactory entityManagerFactory;

 public boolean updateTable(){
    EntityManager entityManager =  entityManagerFactory.createEntityManager();
    EntityTransaction entityTransaction = entityManager.getTransaction();
    entityTransaction.begin(); // this will go in try catch
    Query query = entityManager.createQuery(updateSql);
    // update table code goes here

    entityTransaction.commit(); // this will go in try catch
 }
}

答案 1 :(得分:0)

我不是春季专家,但我认为new ...不适用于@Transactional

@Service
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class BillingCroneSvcImpl extends QuartzJobBean implements BillingCroneSvc  {

@Autowired
BillingCroneRepo billingCroneRepo;

@Override
@Transactional
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
       SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(context);
       billingCroneRepo.updateBalance();
}
}

答案 2 :(得分:0)

这是因为quartz正在使用bean而不是为@Transactional生成的代理。

使用MethodInvokingJobDetailFactoryBean(而不是继承QuartzJob)或使用一个专用的包装器quarz bean(继承自QuartzJob)来调用带有@Transactionnal注释的spring bean(不是继承自QuartzJob)。

编辑:这实际上不是问题

问题在于:

 JobDetailFactoryBean factory = new JobDetailFactoryBean();
  factory.setJobClass(BillingCroneSvcImpl.class);

通过传递类,我假设Quartz将自己实例化它,因此Spring不会创建它并且不会将bean包装在处理@Transactionnal行为的Proxy中。

相反,你必须使用沿线的东西:

 @Bean(name = "billingCroneSvc")
 public BillingCroneSvc getSvc(){
        return new BillingCroneSvcImpl();
 }


@Bean
public JobDetailFactoryBean jobDetailBalanceCarryForward(){
  JobDetailFactoryBean factory = new JobDetailFactoryBean();
  getSvc();// just make sure the bean is instantiated
  factory.setBeanName("billingCroneSvc");
  ...
}