如何在Quartz JobDataMap中更新值?

7
我正在使用quartz-scheduler 1.8.5。我创建了一个实现StatefulJob接口的Job,使用SimpleTrigger和StdSchedulerFactory进行调度。
似乎我必须更新Trigger的JobDataMap以及JobDetail的JobDataMap才能从Job内部更改JobDataMap。我试图理解为什么需要同时更新两者?我注意到JobDataMap被设置为dirty。也许我需要显式保存它或做些其他的操作?
我想我必须深入Quartz的源代码才能真正理解这里发生了什么,但我想先懒一点并询问一下。感谢您对JobDataMap内部工作原理的任何见解!
这是我的Job:
public class HelloJob implements StatefulJob {

    public HelloJob() {
    }

    public void execute(JobExecutionContext context)
            throws JobExecutionException {

        int count = context.getMergedJobDataMap().getInt("count");
        int count2 = context.getJobDetail().getJobDataMap().getInt("count");
        //int count3 = context.getTrigger().getJobDataMap().getInt("count");
        System.err.println("HelloJob is executing. Count: '"+count+"', "+count2+"'");

        //The count only gets updated if I updated both the Trigger and 
                // JobDetail DataMaps. If I only update the JobDetail, it doesn't persist. 
        context.getTrigger().getJobDataMap().put("count", count++);
        context.getJobDetail().getJobDataMap().put("count", count++);

        //This has no effect inside the job, but it works outside the job
        try {
            context.getScheduler().addJob(context.getJobDetail(), true);
        } catch (SchedulerException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        //These don't seem to persist between jobs
        //context.put("count", count++);
        //context.getMergedJobDataMap().put("count", count++);
    }
}

以下是我如何安排这个工作的:

try {
    // define the job and tie it to our HelloJob class
    JobDetail job = new JobDetail(JOB_NAME, JOB_GROUP_NAME,
            HelloJob.class);
    job.getJobDataMap().put("count", 1);
    // Trigger the job to run now, and every so often
    Trigger trigger = new SimpleTrigger("myTrigger", "group1",
            SimpleTrigger.REPEAT_INDEFINITELY, howOften);

    // Tell quartz to schedule the job using our trigger
    sched.scheduleJob(job, trigger);
    return job;
} catch (SchedulerException e) {
    throw e;
}

更新:

看起来我需要把值放入JobDetail的JobDataMap两次才能使其持久化,这样做有效:

public class HelloJob implements StatefulJob {

    public HelloJob() {
    }

    public void execute(JobExecutionContext context)
            throws JobExecutionException {

        int count = (Integer) context.getMergedJobDataMap().get("count");
        System.err.println("HelloJob is executing. Count: '"+count+"', and is the job stateful? "+context.getJobDetail().isStateful());
        context.getJobDetail().getJobDataMap().put("count", count++);
        context.getJobDetail().getJobDataMap().put("count", count++);
    }
}

这似乎是一个bug,也许?或者我错过了一步,没有告诉JobDetail将其JobDataMap的内容刷新到JobStore中?

4个回答

18
我认为你的问题在于使用后缀++运算符——当你执行以下操作时:
context.getJobDetail().getJobDataMap().put("count", count++);  

你将计数器的值设置为map中的值,然后才增加计数器的值。

在我看来,你想要的是:

context.getJobDetail().getJobDataMap().put("count", ++count);  

只需要做一次的事情。


谢谢,那就是问题所在。我简直不敢相信我没发现它。非常感谢! - Upgradingdave
这篇回答解决了我的问题,非常感谢!点个赞 ;) - Sal Prima
此外,您无需从getTrigger获取JobDatamap。 - Sumit Kumar Saha

3

如您所知,在Quartz中,触发器和作业是分开的,而不是与某些调度程序结合使用。它们可能允许您向datamap添加特定于触发器级别而不是作业级别的值等。

我认为它允许您使用不同的数据集执行相同的最终作业,但仍在作业级别具有一些共同的数据。


谢谢你的回答,这很有道理。但奇怪的是,当我只执行context.getJobDetail.getJobDataMap.put(blah)时,它不会持久化。同样地,当我只执行context.getTrigger.getJobDataMap.put(blah)时,它也不会持久化。我必须同时更新Trigger和JobDetail JobDataMap才能使其持久化。我认为我只需要更新其中一个。但我一定是理解错了什么。 - Upgradingdave

3
正如scpritch76所回答的那样,作业和触发器是分开的,因此可以为给定的作业设置许多触发器(计划)。作业可以在JobDataMap中具有一些基本属性,然后触发器可以在它们自己的JobDataMaps中为特定的作业执行提供附加属性(或覆盖基本属性)。

谢谢,是的,我记得在quartz-scheduler.org网站上读到过,Trigger JobDataMap中定义的键/值对将优先于JobDetail中定义的键/值对。但是,我的问题是似乎必须在两个地方都设置键/值对才能使其在作业执行之间持久化(请参见我对@cspritch76的评论)。再次感谢您的帮助。 - Upgradingdave
只有作业的JobDataMap在执行之间重新持久化(如果作业是有状态的)。 - jhouse
1
如果您想要更新触发器的JobDataMap,您必须重新存储触发器(scheduler.rescheduleJob(..))。 - jhouse
啊,这很有道理,你必须重新安排工作以更新触发器的JobDataMap,谢谢。我更新了我的问题。看起来如果我手动创建一个新的JobDataMap并显式设置它,那么更改就会持久保存。我想这里可能有个bug,也许? - Upgradingdave

2
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class DynamicTestJob implements Job
{

    private static final Logger log = LoggerFactory.getLogger(DynamicTestJob.class);

    @Override
    public void execute(final JobExecutionContext context)
    {
        final JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        Integer counter = (Integer) jobDataMap.get("counter");

        if (counter == null)
        {
            counter = 1;
        }
        else
        {
            counter++;
        }

        jobDataMap.put("counter", counter);

        System.out.println(counter);
    }

}

这是适用于我的情况的可行解答。“@PersistJobDataAfterExecution” 是解决方案。感谢兄弟! - Tharindu Eranga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接