편집
한 가지입니다.
반드시 필요하지는 않습니다. 어느 방향 으로든 경고가 있습니다. 나는 doWork (...) 메소드 위의 수정 된 클래스 블로우 (JobRunnerService)에서 이들 중 일부를 언급했다. 그 노트는 가치가 있습니다.
내가 실현하려하고 싶은 것은 doWork 정기적으로 작업의 진행 상황을 설정할 수 있다는 것입니다
이 수도 있고 당신이 doWork (...)를 할 것인지 여부에 따라 달성하기 어려울 수 없습니다 트랜잭션에 바인딩되고 각 작업이 동일한 방식으로 분리 될 수 있는지 여부 (즉, 업데이트는 항상 코드의 정적 위치에서 발생합니다). 나는 모든 요구 사항을 모릅니다. 그래서이 질문에 정말로 대답 할 수 없습니다. 그러나 스프링 배치를 조사 할 때 필자의 조언을 되풀이합니다.
JobRunnerService
import me.mike.jobs.model.Job;
import me.mike.jobs.model.JobState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
/**
* !!This bean is STATEFUL!!
*/
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class JobRunnerService {
@Autowired
private JobService js;
public void processJob(Job job) {
job.setState(JobState.WORKING_0);
js.update(job);
try {
doWork(job);
job.setState(JobState.COMPLETE);
} catch (Exception e) {
job.setState(JobState.FAILED);
}
System.out.println("I'm done working.");
js.update(job);
}
/**
* Be sure that any unchecked exception you throw gets added into the "rollbackFor" since it won't trigger
* a rollback if you don't...
*
* The @Transactional is optional - I assumed you would want the work performed in the job to be transactional.
*
* Note: Remember, when doing the work represented by these jobs, that your EntityManager (or SessionFactory) is
* configured with a TransactionManager and, as such, will throw exceptions when you attempt to do work within them
* without a Transaction. You will either need a separate EntityManager (SessionFactory) or something like a
* JdbcTemplate.
*
* Note: If the Job's work DOES need to be Transactional, this will probably not work. A very simple solution
* would to be to split up the work within the job into "steps" or "stages." The processJob(...) method above
* could then call each stage and, at the conclusion, update the Job's state appropriately. This, of course,
* would not work if each Job had N number of stages where N could vary an indeterminate amount.
*/
//@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = { IllegalArgumentException.class })
public void doWork(Job job) throws IllegalArgumentException {
// This method begins its own transaction, every single time its called. Period.
// Do some work...
job.setState(JobState.WORKING_10);
js.update(job);
// Do more work...
job.setState(JobState.WORKING_90);
js.update(job);
// At the conclusion, the transaction bound to this method is committed, unless a rollback was initiated.
}
}
서문 : 나는 SpringBatch 같은 것을 활용에보기 위하여 당신의 현명 할 것입니다 생각합니다. 더 많은 설정이 필요하지만 더 많은 지원을 제공합니다.
제대로 이해한다면 테이블에 "작업"을 저장하려고합니다 (RESTful 작성). 백그라운드에서 주기적으로 실행할 수있는 @Scheduled 작업이 필요하다면 각 작업이 나타내는 작업을 수행 할 수 있습니다. 또한 작업하기 전후에 해당 엔티티 각각의 상태 (ㅎ)를 변경하려고합니다. 초기 상태 변경은 필연적 인 끝 상태 변경과 마찬가지로 자체 트랜잭션 범위 내에서 발생해야한다는 경고가 있습니다.
스프링, JPA 및 하이버 네이트를 사용하는 MySQL 5.x DB에 대해이 코드를 실행했습니다. 필요한 경우 applicationContext 및 rest-servlet xml 파일을 제공 할 수 있습니다.
모델 :
이 내가 될 당신의 명시된 목표를 이해하고 무엇을 수행 할
import org.hibernate.validator.constraints.Length;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.UUID;
@Entity
public class Job {
@Id
private String id;
@Column
@NotNull
@Length(min = 3, max = 50)
private String name;
@Enumerated(EnumType.STRING)
@Column(length = 50, nullable = false)
private JobState state;
public UUID getId() {
return UUID.fromString(id);
}
public void setId(UUID id) {
this.id = id.toString();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public JobState getState() {
return state;
}
public void setState(JobState state) {
this.state = state;
}
}
리포지토리 :
import me.mike.jobs.model.Job;
import me.mike.jobs.model.JobState;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@Repository
public class JobDao {
@PersistenceContext
private EntityManager em;
@Transactional(propagation = Propagation.REQUIRED)
public void create(Job job) {
// ...
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public Set<Job> readAll() {
// ...
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public Job readById(UUID id) {
// ...
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public Set<Job> readByState(JobState state) {
// ...
}
@Transactional(propagation = Propagation.REQUIRED)
public void update(Job job) {
// ...
}
@Transactional(propagation = Propagation.REQUIRED)
public void delete(Job job) {
// ...
}
}
JobService (이 J의 RESTful 액션을 처리한다. 산부인과 개체)
import me.mike.jobs.dao.JobDao;
import me.mike.jobs.model.Job;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Set;
@Service
public class JobService {
@Autowired
private JobDao jd;
@Transactional(propagation = Propagation.REQUIRED)
public void create(Job job) {
// Business logic...
jd.create(job);
// More business logic...
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public Set<Job> read() {
// Business logic...
Set<Job> jobs = jd.readAll();
// More business logic...
return jobs;
}
@Transactional(propagation = Propagation.REQUIRED)
public void update(Job job) {
// Business logic...
jd.update(job);
// More business logic...
}
@Transactional(propagation = Propagation.REQUIRED)
public void delete(Job job) {
// Business logic...
jd.delete(job);
// More business logic...
}
}
MaintenanceService (이 사람은 당신의 @ScheduledTask 방법을 모두 보유 것)
import me.mike.jobs.dao.JobDao;
import me.mike.jobs.model.Job;
import me.mike.jobs.model.JobState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public class MaintenanceService {
@Autowired
private JobRunnerService jrs;
@Autowired
private JobDao jd;
@Scheduled(fixedDelay = 5000, initialDelay = 5000)
public void processQueuedJobs() {
// This may be somewhat dangerous depending on how many jobs could potentially be racked up during the 'downtime'
for (Job curJob : jd.readByState(JobState.QUEUED))
jrs.processJob(curJob);
}
// Any other timed service methods...
}
JobRunnerService이 실제로 작업
import me.mike.jobs.model.Job;
import me.mike.jobs.model.JobState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* !!This bean is STATEFUL!!
*/
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class JobRunnerService {
@Autowired
private JobService js;
public void processJob(Job job) {
job.setState(JobState.WORKING);
js.update(job);
try {
doWork(job);
job.setState(JobState.COMPLETE);
} catch (Exception e) {
job.setState(JobState.FAILED);
}
System.out.println("I'm done working.");
js.update(job);
}
/**
* Be sure that any unchecked exception you throw gets added into the "rollbackFor" since it won't trigger
* a rollback if you don't...
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = { IllegalArgumentException.class })
public void doWork(Job job) throws IllegalArgumentException {
// This method begins its own transaction, every single time its called. Period.
// Do your work here...
// At the conclusion, the transaction bound to this method is committed, unless a rollback was initiated.
}
}
를 실행하는 서비스입니다
감사 Mike. 내가 이해할 수없는 한 가지는 doWork가 큰 거래를해야하는 이유입니다. 내가하고 싶은 것은 doWork가 Job의 분야 (물론 작은 거래에서)에서 job의 진행 상황 (0 % .. 10 % etc.)을 정기적으로 설정할 수 있다는 것입니다. – RobAu
첫 번째 부분에 대답하려면 : 실제로 트랜잭션이 필요하지 않습니다. 나는 그저 거기에 놓았다. 두 번째 부분은 위의 코드를 약간 수정하지 않으면 수행 할 수 없습니다. 나는 아마도 "Job"객체를 "Update"하기 위해 @Async annotation을 가진 MaintenanceService 내부에 또 다른 메소드를 생성 할 것이고, 어떤 메소드가 작업을하고 있더라도 그것을 호출 할 것이다. – Mike
몇 분만 주시면 위의 답을 기꺼이 고려하여이 사실을 고려해 볼 것입니다. – Mike