![](/img/trans.png)
[英]Spring JPA - Injecting transaction manager vs injecting entity manager
[英]Injecting Entity Manager in a programatically managed Quartz job
我有一种奇怪的要求。
我有一个使用JPA Hibernate的Spring托管应用程序。 我已经使用Quartz编写了一些作业类,但是它们没有与Spring框架一起管理/集成。 它们是独立的Java类,具有复杂的逻辑和基于运行时参数的动态触发计划。 因此,我通过LoginController以编程方式安排了这些作业。 现在,当我需要在这些作业类中执行一些数据库事务时,问题就来了。
如果我尝试执行@PersistenceContext private EntityManager实体管理器,则会得到一个空引用,该引用很清楚,因为我无法将这些组件自动连接到非Spring管理的Quartz作业中。
我不得不使用的最后一种方法是在作业类中对我的数据库事务使用JDBC,但这增加了工作量。 有没有可能解决我的问题。 我已经附加了Java代码以使事情变得清楚。
JobScheduler.java
public class JobScheduler extends Object
{
private static final Logger logger = LoggerFactory
.getLogger(JobScheduler.class);
private static final JobScheduler s_instance = new JobScheduler();
private static boolean s_isSchedulerStarted = false;
private static Scheduler s_scheduler = null;
static
{
try
{
s_scheduler = new StdSchedulerFactory().getScheduler();
} catch (SchedulerException e)
{
logger.debug(e.getMessage().toString());
}
}
public static JobScheduler getInstance()
{
if (!s_isSchedulerStarted)
{
try
{
s_scheduler.start();
s_isSchedulerStarted = true;
} catch (SchedulerException e)
{
logger.debug(e.getMessage());
e.printStackTrace();
}
}
return s_instance;
}
public Scheduler getScheduler()
{
return s_scheduler;
}
public void scheduleMonitoring() throws ApplicationException
{
try
{
Class<? extends Job> jobClass = ScheduleMonitoringJob.class;
JobDetail job = JobBuilder.newJob(jobClass).build();
Trigger trigger = ScheduleMonitoringJob.getTriggerWithSchedule();
s_scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e)
{
logger.debug(e.getMessage());
throw new ApplicationException(e);
}
}
}
ScheduleMonitoringJob.java
public class ScheduleMonitoringJob implements InterruptableJob
{
private static final Logger logger = LoggerFactory
.getLogger(ScheduleMonitoringJob.class);
@PersistenceContext
private EntityManager entityManager; //THIS COMES AS NULL
/*
* (non-Javadoc)
*
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
*/
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException
{
List<KpiDefinition> kpisToBeMonitored = getNewOrChangedKPIs();
for (KpiDefinition kpiDef : kpisToBeMonitored)
{
KpiType kpiType = kpiDef.getKpiTypeBean();
Class<? extends MonitorJob> jobClass = null;
if (kpiType.getName()
.equalsIgnoreCase(KpiType.TYPE_DB_CONNECTIVITY))
{
jobClass = DBConnectionMonitorJob.class;
} else if (kpiType.getName().equalsIgnoreCase(
KpiType.TYPE_FTP_SERVER_AVAILABILITY))
{
jobClass = FTPServerMonitorJob.class;
} else if (kpiType.getName().equalsIgnoreCase(
KpiType.TYPE_SOAP_SERVICE_AVAILABILITY))
{
jobClass = SOAPServiceMonitorJob.class;
} else
{
jobClass = EngineEventSQLMonitorJob.class;
}
JobDetail job = JobBuilder.newJob(jobClass).build();
job.getJobDataMap().put("kpiDefId", kpiDef.getKpiDefId());
Trigger trigger = MonitorJob.getTriggerWithSchedule(kpiDef);
try
{
JobScheduler.getInstance().getScheduler()
.scheduleJob(job, trigger);
} catch (SchedulerException e)
{
logger.debug(e.getMessage());
throw new JobExecutionException(e);
}
kpiDef.setKpiStatus(KpiDefinition.KPI_STATUS_PROCESSING_PROCESSED);
}
}
/*
* (non-Javadoc)
*
* @see org.quartz.InterruptableJob#interrupt()
*/
@Override
public void interrupt() throws UnableToInterruptJobException
{
// TODO Auto-generated method stub
}
public static Trigger getTriggerWithSchedule()
{
SimpleTrigger trigger = (SimpleTrigger) newTrigger().withSchedule(
SimpleScheduleBuilder.repeatMinutelyForever(10)).build();
return trigger;
}
public List<KpiDefinition> getNewOrChangedKPIs()
{
String[] statusCodes = { KpiDefinition.KPI_STATUS_NEW,
KpiDefinition.KPI_STATUS_CHANGED };
Query query = entityManager
.createQuery("select kpiDef from KpiDefinition kpiDef where kpiDef.kpiStatus in (:statusCodes)");
query.setParameter("statusCodes", statusCodes);
return query.getResultList();
}
}
由于您在应用程序中使用过Spring,因此强烈建议您使用Spring SchedulerFactoryBean创建Quartz Scheduler实例。 然后,您可以像这样轻松地访问Spring应用程序上下文(以及已经由Spring管理的EntityManager):
public class ScheduleMonitoringJob implements InterruptableJob
{
private static final String SCHEDULER_CONTEXT_APPLICATION_CONTEXT_KEY = "applicationContext";
...
/**
* Returns the {@link ApplicationContext} instance extracted from the scheduler context, or null if not found.
*
* @return the {@link ApplicationContext} instance.
*/
protected EntityManager getEntityManager(JobExecutionContext context)
{
try
{
ApplicationContext springCtx = (ApplicationContext) context.getScheduler().getContext().get( SCHEDULER_CONTEXT_APPLICATION_CONTEXT_KEY );
return springCtx.getBean(EntityManager.class);
}
catch ( SchedulerException e )
{
if ( log.isErrorEnabled() )
log.error( "Error obtaining Spring application context.", e );
return null;
}
}
}
Spring应用程序上下文示例。 请注意applicationContextSchedulerContextKey属性的值,该值包含Quartz作业应用程序上下文属性名称的名称,工厂将Spring应用程序上下文存储在该名称中:
<!--
Quartz scheduler.
-->
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="schedulerName" value="MyScheduler"/>
...
<!--
Name of the Quartz scheduler context property where the factory stores the Spring context.
-->
<property name="applicationContextSchedulerContextKey" value="applicationContext"/>
</bean>
如果您不想迁移代码以使用Spring SchedulerFactoryBean,则可以使用以下方法实现相同的结果:
public class JobScheduler extends Object
{
private static final JobScheduler s_instance = new JobScheduler();
private static boolean s_isSchedulerStarted = false;
private static Scheduler s_scheduler = null;
static
{
try
{
s_scheduler = new StdSchedulerFactory().getScheduler();
s_scheduler.getContext().put("applicationContext", YOUR_SPRING_APPLICATION_CONTEXT);
}
catch (SchedulerException e)
{
...
}
}
...
}
另外,您可以在其中存储EntityManager实例,而不是将Spring应用程序上下文存储在JobScheduler类的Quartz调度程序上下文中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.