編集
私がよく理解していないことの 1 つは、なぜ doWork が 1 つの大きなトランザクションを必要とするのかということです。
そうである必要はありません。どちらの方向にも注意事項があります。doWork(...) メソッドの上の修正されたクラス ブロー (JobRunnerService) で、これらのいくつかに注目しました。それらのメモは価値があります...注目に値します。
私が達成したいのは、doWork が定期的にジョブの進行状況を設定できることです。
これは、doWork(...) をトランザクションにバインドするかどうか、および各ジョブを同じ方法で分割できるかどうか (つまり、更新は常に静的に発生するかどうか) に応じて、達成するのが難しい場合とそうでない場合があります。コード内の場所)。私はあなたのすべての要件を知っているわけではないので、この質問に答えることができません. ただし、Spring Batch を検討する際のアドバイスを繰り返します。
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 タスクが必要で、これらの各ジョブが表す作業を実行します。さらに、作業の前後に、これらの各エンティティの状態 (heh) を変更したいと考えています。初期状態の変更は、必然的な終了状態の変更と同様に、独自のトランザクション境界内で発生する必要があることに注意してください。
Spring、JPA、および Hibernate を使用して、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 (これは Job エンティティで 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.
}
}