Eu estou desenvolvendo multiple-jobs-in-quartz-spring-example
que é uma combinação de Spring + Quartz + Spring Data JPA
. Estou procurando desenvolver um código que será executado em 5 segundos, chegará ao banco de dados e buscará um registro do banco de dados. Estou quase perto de fazê-lo funcionar, mas vejo um pequeno problema. Em meu JobA.class, não obtenho uma instância de CustomerRepository.java por quê? É sempre nulo e é por isso que o código não consegue acessar o banco de dados. Por favor, guie como resolver este problema?
Código-fonte em:http://www.github.com/test512/multiple-jobs-in-quartz-spring-example.gi t.
JobA.java
@Service
public class JobA extends QuartzJobBean {
private CustomerRepository customerRepository = null;
@Autowired
public JobA(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
public JobA() { }
@Override
protected void executeInternal(JobExecutionContext executionContext) throws JobExecutionException {
System.out.println("~~~~~~~ Job A is runing ~~~~~~~~");
Trigger trigger = executionContext.getTrigger();
System.out.println(trigger.getPreviousFireTime());
System.out.println(trigger.getNextFireTime());
getCustomerList();
}
private List<Customer> getCustomerList(){
List<Customer> customers = customerRepository.findAll();
for (Customer customer : customers) {
System.out.println("------------------------------");
System.out.println("ID : "+customer.getId());
System.out.println("NAME : "+customer.getName());
System.out.println("STATUS : "+customer.getStatus());
}
return customers;
}
}
Customer.java
@Entity
@Table(name="customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="ID")
private Long id;
@Column(name="NAME")
private String name;
@Column(name="STATUS")
private String status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
CustomerRepository.java
public interface CustomerRepository extends JpaRepository<Customer, Long>{
}
dataSourceContext.xml
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<context:property-placeholder location="classpath:database.properties"/>
<!-- <jdbc:initialize-database data-source="dataSource" enabled="true">
<jdbc:script location="classpath:db-schema.sql" />
<jdbc:script location="classpath:db-test-data.sql" />
</jdbc:initialize-database> -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${mysql.driver.class.name}" />
<property name="url" value="${mysql.url}" />
<property name="username" value="${mysql.username}" />
<property name="password" value="${mysql.username}" />
</bean>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
<property name="database" value="MYSQL"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<!-- spring based scanning for entity classes-->
<property name="packagesToScan" value="com.mkyong.*"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"/>
</beans>
Spring-Quartz.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:repository="http://www.springframework.org/schema/data/repository"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository.xsd">
<import resource="classpath:dataSourceContext.xml"/>
<jpa:repositories base-package="com.mkyong.repository" />
<context:component-scan base-package="com.mkyong.*" />
<context:annotation-config />
<bean id="jobA" class="com.mkyong.job.JobA" />
<bean id="jobB" class="com.mkyong.job.JobB" />
<bean id="jobC" class="com.mkyong.job.JobC" />
<bean id="autowiredA" class="com.mkyong.job.JobASpringBeanJobFactory" />
<!-- ~~~~~~~~~ Quartz Job ~~~~~~~~~~ -->
<bean name="JobA" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.mkyong.job.JobA" />
</bean>
<bean name="JobB" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.mkyong.job.JobB" />
</bean>
<bean name="JobC" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.mkyong.job.JobC" />
</bean>
<!-- ~~~~~~~~~~~ Cron Trigger, run every 5 seconds ~~~~~~~~~~~~~ -->
<bean id="cronTriggerJobA" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="JobA" />
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>
<bean id="cronTriggerJobB" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="JobB" />
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>
<bean id="cronTriggerJobC" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="JobC" />
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>
<!-- ~~~~~~~~~~~~~~~~ Scheduler bean Factory ~~~~~~~~~~~~~~~~ -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobFactory" ref="autowiredA"/>
<property name="triggers">
<list>
<ref bean="cronTriggerJobA" />
<!-- <ref bean="cronTriggerJobB" />
<ref bean="cronTriggerJobC" /> -->
</list>
</property>
</bean>
</beans>
JobASpringBeanJobFactory.java
public class JobASpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
App.java
public class App {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Quartz.xml");
}
}
Respostas:
0 para resposta № 1Acessando o seguinte link http://codrspace.com/Khovansa/spring-quartz-with-a-database/ deve ajudar.
Citando o link acima,
Quartz cria uma nova instância de trabalho em cadainvocação. Isso significa que os jobs do Quartz não são beans regulares do Spring e não podemos esperar que a mágica do Spring tenha efeito automaticamente (e o "JobDetailFactoryBean" do Spring não é inteligente o suficiente para fazer isso por nós). Portanto, teremos que implementar nossa própria fábrica de empregos que sobrescreverá a SpringBeanJobFactory padrão.
Então você precisa ter um custom SpringBeanJobFactory
de extending SpringBeanJobFactory & implementing ApplicationContextAware
e finalmente invocar beanFactory.autowireBean(job)
0 para resposta № 2
Vamos abrir a discussão aqui agora. E espere a orientação / ajuda de todos os especialistas aqui.
No exemplo acima, eu modifiquei JobA.java
classe para obter uma instância do repositório CustomerRepository usando injeção de construtor como abaixo:
@Service
public class JobA extends QuartzJobBean {
private CustomerRepository customerRepository = null;
@Autowired
public JobA(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
getCustomerList();
}
public JobA() { }
@Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("~~~~~~~ Job A is runing ~~~~~~~~");
}
private List<Customer> getCustomerList(){
List<Customer> customers = customerRepository.findAll();
for (Customer customer : customers) {
System.out.println("------------------------------");
System.out.println("ID : "+customer.getId());
System.out.println("NAME : "+customer.getName());
System.out.println("STATUS : "+customer.getStatus());
}
return customers;
}
}
Mas o problema é que, quando usamos a instância customerRepository no método executeInternal (), ela a anula por quê? Por quê ? Se de alguma forma a instância não for anulada, terminamos !!!!