Grails和Quartz:类型long的值不正确



我正在尝试将quartz作业保存到数据库中。我已经设置了表,创建了quartz.properties文件,但当我尝试运行应用程序时,会出现以下异常:

2012-02-01 17:36:23,708 [main] ERROR context.GrailsContextLoader  - Error executing bootstraps: org.quartz.JobPersistenceException: Couldn't store trigger 'expirationTrigger' for 'com.pldtglobal.svngateway.ExpirationCheckerJob' job:Bad value for type long : 2543550005sr0025org.quartz.JobDataMap237260203350277251260313020000xr00&org.quartz.utils.StringKeyDirtyFlagMap20210350303373305](020001Z0023allowsTransientDataxr0035org.quartz.utils.DirtyFlagMap23346.255(v12316020002Z0005dirtyL0003mapt0017Ljava/util/Map;xp01sr0021java.util.HashMap050733230130326`321030002F0012loadFactorI0011thresholdxp?@000000000014w100000002000000001t00'org.grails.plugins.quartz.grailsJobNamet00.com.pldtglobal.svngateway.ExpirationCheckerJobx00 [See nested exception: org.postgresql.util.PSQLException: Bad value for type long : 2543550005sr0025org.quartz.JobDataMap237260203350277251260313020000xr00&org.quartz.utils.StringKeyDirtyFlagMap20210350303373305](020001Z0023allowsTransientDataxr0035org.quartz.utils.DirtyFlagMap23346.255(v12316020002Z0005dirtyL0003mapt0017Ljava/util/Map;xp01sr0021java.util.HashMap050733230130326`321030002F0012loadFactorI0011thresholdxp?@000000000014w100000002000000001t00'org.grails.plugins.quartz.grailsJobNamet00.com.pldtglobal.svngateway.ExpirationCheckerJobx00]
org.codehaus.groovy.runtime.InvokerInvocationException: org.quartz.JobPersistenceException: Couldn't store trigger 'expirationTrigger' for 'com.pldtglobal.svngateway.ExpirationCheckerJob' job:Bad value for type long : 2543550005sr0025org.quartz.JobDataMap237260203350277251260313020000xr00&org.quartz.utils.StringKeyDirtyFlagMap20210350303373305](020001Z0023allowsTransientDataxr0035org.quartz.utils.DirtyFlagMap23346.255(v12316020002Z0005dirtyL0003mapt0017Ljava/util/Map;xp01sr0021java.util.HashMap050733230130326`321030002F0012loadFactorI0011thresholdxp?@000000000014w100000002000000001t00'org.grails.plugins.quartz.grailsJobNamet00.com.pldtglobal.svngateway.ExpirationCheckerJobx00 [See nested exception: org.postgresql.util.PSQLException: Bad value for type long : 2543550005sr0025org.quartz.JobDataMap237260203350277251260313020000xr00&org.quartz.utils.StringKeyDirtyFlagMap20210350303373305](020001Z0023allowsTransientDataxr0035org.quartz.utils.DirtyFlagMap23346.255(v12316020002Z0005dirtyL0003mapt0017Ljava/util/Map;xp01sr0021java.util.HashMap050733230130326`321030002F0012loadFactorI0011thresholdxp?@000000000014w100000002000000001t00'org.grails.plugins.quartz.grailsJobNamet00.com.pldtglobal.svngateway.ExpirationCheckerJobx00]
    at org.grails.tomcat.TomcatServer.start(TomcatServer.groovy:212)
    at grails.web.container.EmbeddableServer$start.call(Unknown Source)
    at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy:158)
    at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy)
    at _GrailsSettings_groovy$_run_closure10.doCall(_GrailsSettings_groovy:280)
    at _GrailsSettings_groovy$_run_closure10.call(_GrailsSettings_groovy)
    at _GrailsRun_groovy$_run_closure5.doCall(_GrailsRun_groovy:149)
    at _GrailsRun_groovy$_run_closure5.call(_GrailsRun_groovy)
    at _GrailsRun_groovy.runInline(_GrailsRun_groovy:116)
    at _GrailsRun_groovy.this$4$runInline(_GrailsRun_groovy)
    at _GrailsRun_groovy$_run_closure1.doCall(_GrailsRun_groovy:59)
    at RunApp$_run_closure1.doCall(RunApp:33)
    at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
    at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
    at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
    at gant.Gant.withBuildListeners(Gant.groovy:427)
    at gant.Gant.this$2$withBuildListeners(Gant.groovy)
    at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
    at gant.Gant.dispatch(Gant.groovy:415)
    at gant.Gant.this$2$dispatch(Gant.groovy)
    at gant.Gant.invokeMethod(Gant.groovy)
    at gant.Gant.executeTargets(Gant.groovy:590)
    at gant.Gant.executeTargets(Gant.groovy:589)
Caused by: org.quartz.JobPersistenceException: Couldn't store trigger 'expirationTrigger' for 'com.pldtglobal.svngateway.ExpirationCheckerJob' job:Bad value for type long : 2543550005sr0025org.quartz.JobDataMap237260203350277251260313020000xr00&org.quartz.utils.StringKeyDirtyFlagMap20210350303373305](020001Z0023allowsTransientDataxr0035org.quartz.utils.DirtyFlagMap23346.255(v12316020002Z0005dirtyL0003mapt0017Ljava/util/Map;xp01sr0021java.util.HashMap050733230130326`321030002F0012loadFactorI0011thresholdxp?@000000000014w100000002000000001t00'org.grails.plugins.quartz.grailsJobNamet00.com.pldtglobal.svngateway.ExpirationCheckerJobx00 [See nested exception: org.postgresql.util.PSQLException: Bad value for type long : 2543550005sr0025org.quartz.JobDataMap237260203350277251260313020000xr00&org.quartz.utils.StringKeyDirtyFlagMap20210350303373305](020001Z0023allowsTransientDataxr0035org.quartz.utils.DirtyFlagMap23346.255(v12316020002Z0005dirtyL0003mapt0017Ljava/util/Map;xp01sr0021java.util.HashMap050733230130326`321030002F0012loadFactorI0011thresholdxp?@000000000014w100000002000000001t00'org.grails.plugins.quartz.grailsJobNamet00.com.pldtglobal.svngateway.ExpirationCheckerJobx00]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1241)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$5.execute(JobStoreSupport.java:1147)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$40.execute(JobStoreSupport.java:3670)
    at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:242)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInLock(JobStoreSupport.java:3666)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1143)
    at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:790)
    at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:254)
    at org.quartz.Scheduler$scheduleJob.call(Unknown Source)
    at QuartzGrailsPlugin$_closure5_closure24.doCall(QuartzGrailsPlugin.groovy:223)
    at QuartzGrailsPlugin$_closure5.doCall(QuartzGrailsPlugin.groovy:218)
    at QuartzGrailsPlugin.invokeMethod(QuartzGrailsPlugin.groovy)
    at QuartzGrailsPlugin$_closure3_closure21.doCall(QuartzGrailsPlugin.groovy:169)
    at QuartzGrailsPlugin$_closure3.doCall(QuartzGrailsPlugin.groovy:167)
    ... 23 more
Caused by: org.postgresql.util.PSQLException: Bad value for type long : 2543550005sr0025org.quartz.JobDataMap237260203350277251260313020000xr00&org.quartz.utils.StringKeyDirtyFlagMap20210350303373305](020001Z0023allowsTransientDataxr0035org.quartz.utils.DirtyFlagMap23346.255(v12316020002Z0005dirtyL0003mapt0017Ljava/util/Map;xp01sr0021java.util.HashMap050733230130326`321030002F0012loadFactorI0011thresholdxp?@000000000014w100000002000000001t00'org.grails.plugins.quartz.grailsJobNamet00.com.pldtglobal.svngateway.ExpirationCheckerJobx00
    at org.postgresql.jdbc2.AbstractJdbc2ResultSet.toLong(AbstractJdbc2ResultSet.java:2796)
    at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getLong(AbstractJdbc2ResultSet.java:2019)
    at org.postgresql.jdbc4.Jdbc4ResultSet.getBlob(Jdbc4ResultSet.java:52)
    at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getBlob(AbstractJdbc2ResultSet.java:335)
    at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.getObjectFromBlob(StdJDBCDelegate.java:3462)
    at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.selectJobDetail(StdJDBCDelegate.java:904)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1197)
    ... 36 more
Application context shutting down...
Application context shutdown.

我不知道实际的问题在哪里。当作业没有保存在数据库中时,代码还可以运行。

在grails app/conf/quartz.properties中,替换

org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

带有

org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

即使使用正确的委托,我也会遇到同样的错误,所以没有承诺。

对于spring-boot,您还可以使用application.properties-中的以下属性指定PG驱动程序

spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

对于任何使用Quartz和Spring Boot的人来说,在从Tomcat中使用Quarty迁移到Spring Boot后,我也遇到了同样的问题。在Tomcat中,我们使用了一个石英属性文件,并在创建Scheduler时手动加载它。其中一个特性是:

org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

在Spring Boot中,调度程序是通过自动配置自动创建的,因此我们的属性没有被应用。

我们的解决方案是使用SchedulerFactoryBeanCustomizer并设置Quartz属性。这个自定义程序是在创建调度器之前应用的,所以它是配置Quartz的好地方。

@Bean
public SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer()
{
  return new SchedulerFactoryBeanCustomizer()
  {
     @Override
     public void customize(SchedulerFactoryBean bean)
     {
        bean.setQuartzProperties(createQuartzProperties());
     }
  };
}
private Properties createQuartzProperties()
{
    // Could also load from a file
    Properties props = new Properties();
    props.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
    return props;
}

这里是我们从迁移的完整quartz.properties,供参考

org.quartz.scheduler.instanceName=ProcessAutomation
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.jmx.export=true
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=10
org.quartz.threadPool.threadPriority=5
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.dataSource=QuartzDS
org.quartz.jobStore.nonManagedTXDataSource=springNonTxDataSource.ProcessAutomation
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.clusterCheckinInterval=20000
@Bean
public Properties quartzProperties() throws IOException {
    PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
    propertiesFactoryBean.setLocation(new ClassPathResource("application.properties"));
    Properties props = new Properties();
    props.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
    propertiesFactoryBean.setProperties(props);
    propertiesFactoryBean.afterPropertiesSet();
    return propertiesFactoryBean.getObject();
}

或者,如果你想设置所有的石英属性,如集群、线程池等。不要在这里键入它们,而是创建一个quartz.properties文件并使用下面的;

@Autowired
private QuartzProperties quartzProperties;
@Autowired
DataSource dataSource;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
    SchedulerFactoryBean factory = new SchedulerFactoryBean();
    factory.setOverwriteExistingJobs(true);
    factory.setDataSource(dataSource);
    factory.setQuartzProperties(quartzProperties());
    AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
    jobFactory.setApplicationContext(applicationContext);
    factory.setJobFactory(jobFactory);
    return factory;
}
@Bean
public Properties quartzProperties() throws IOException {
    PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
    propertiesFactoryBean.setLocation(new ClassPathResource("/application.properties"));
    Properties props = new Properties();
    props.putAll(quartzProperties.getProperties());
    propertiesFactoryBean.setProperties(props);
    propertiesFactoryBean.afterPropertiesSet(); //it's important
    return propertiesFactoryBean.getObject();
}

quartz.properties文件示例如下:-

org.quarter.schedur.instanceName=springBootQuartzApporg.quarter.schedur.instanceId=AUTOorg.quartz.threadPool.threadCount=50org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegateorg.quarter.jobStore.useProperties=true#org.quarter.jobStore.miffireThreshold=60000org.quarter.jobStore.isClustered=真org.quarter.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPluginorg.quartz.plugin.shutdownHook.cleanShutdown=TRUE

我也面临这个问题,我只是补充道:

properties.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");

以下的全bean配置

@Bean
public SchedulerFactoryBean scheduler(Trigger... triggers) {
    SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
    Properties properties = new Properties();
    properties.setProperty("org.quartz.scheduler.instanceName", "MY_INSTANCE_NAME");
    properties.setProperty("org.quartz.scheduler.instanceId", "INSTANCE_ID_01");
    properties.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
    schedulerFactory.setOverwriteExistingJobs(true);
    schedulerFactory.setAutoStartup(true);
    schedulerFactory.setQuartzProperties(properties);
    schedulerFactory.setDataSource(dataSource);
    schedulerFactory.setJobFactory(springBeanJobFactory());
    schedulerFactory.setWaitForJobsToCompleteOnShutdown(true);
    if (ArrayUtils.isNotEmpty(triggers)) {
        schedulerFactory.setTriggers(triggers);
    }
    return schedulerFactory;
}

由于这个问题对我们来说真的很难解决,而且这些答案都没有太大帮助,也不会特别引用Grails,所以我将添加我们需要做的事情来解决这个问题:

我们在Grails 5.2.5项目中使用了类似的石英插件:

implementation("org.quartz-scheduler:quartz:2.2.3") {
    exclude group: 'slf4j-api', module: 'c3p0'
}
implementation ('org.grails.plugins:quartz:2.0.13')

Grails自带Spring,Spring自带Quartz调度器,因此我们需要关闭Spring调度器,这样它就不会干扰我们自己的配置(在某个时候,我们有两次运行作业,每个作业都由自己的调度器运行)。我想他们可能在互相阅读道具什么的

为了关闭弹簧,我们在application.yml中放置了以下内容:

spring:
    quartz:
        auto-startup: false

这起到了作用。

然后我们将其添加到应用程序中。在顶级上重新配置石英:

quartz {
    jdbcStore = true // we needed this, because we were running more nodes and required jobs to run only once per set of nodes.
    autoStartup = false // autoStartup is set then to true per required environments.
    dataSource {
        quartzDataSource {
            driver = 'org.postgresql.Driver'
            maxConnections = 30
        }
    }
    scheduler.instanceName = 'ourInstanceName'
    scheduler.instanceId = 'AUTO'
    threadPool.'class' = 'org.quartz.simpl.SimpleThreadPool'
    threadPool.threadCount = 15
    threadPool.threadPriority = 5
    jobStore.'class' = 'org.quartz.impl.jdbcjobstore.JobStoreTX'
    jobStore.driverDelegateClass = 'org.quartz.impl.jdbcjobstore.PostgreSQLDelegate'
    jobStore.dataSource = 'quartzDataSource' // this references the above datasource. The name needs to match but can be arbitrary
    jobStore.useProperties = false
    jobStore.tablePrefix = 'qrtz_'
    jobStore.isClustered = true
    jobStore.clusterCheckinInterval = 5000
    plugin.shutdownhook.'class' = 'org.quartz.plugins.management.ShutdownHookPlugin'
    plugin.shutdownhook.cleanShutdown = true
}

然后根据每个环境,我们会设置它的其余部分,例如:

environments {
    development {
        quartz {
            autoStartup = true
            dataSource {
                quartzDataSource {
                    user = 'grails'
                    password = 'grails'
                }
                quartzDataSource.URL = 'ourUrl' // I had to move URL outside like this, because if it was just URL without the prefix inside the above curly braces, the app wouldn't run with error "you tried to assign a value to the class 'java.net.URL'"
            }
        }
    }
}

有趣的是,各种石英特性直接设置在quartz节点中,而不是像以前那样设置在quartz.propsquartz.properties节点中。在那里是行不通的。

如果您也需要它,下面是创建所需石英数据库表的脚本。我们已经找了一段时间了。https://github.com/quartznet/quartznet/blob/main/database/tables/tables_postgres.sql

最新更新