我有以下设置:
@Component
public class Scheduler {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
BatchService batchService;
@Scheduled(cron = "0 */1 * ? * *")
void tick() {
logger.info("Beginning of a batch tick");
batchService.refundNotAssignedVisits();
logger.info("End of the batch tick");
}
}
其中BatchService
包含以下内容:
@Service
public class BatchServiceImpl implements BatchService {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
VisitService visitService;
@Override
@Transactional
public void refundNotAssignedVisits() {
logger.info("Start automatic refund of past visits being assigned");
Set<Visit> visits = visitService.findRefundableVisits();
if(visits != null && visits.size() != 0) {
logger.info("Found " + visits.size() + " visits to refund with IDs: " + visits.stream().map(x -> x.getId().toString()).collect(Collectors.joining(", ")));
visits.forEach(x -> {
logger.info("Refunding visit with ID: " + x.getId());
try {
visitService.cancel(x);
logger.info("Visit successfully refunded!");
}
catch(Exception e) {
logger.error("Error while refunding visit...", e);
}
});
}
else {
logger.info("Found no visit to refund.");
}
logger.info("End of automatic refund");
}
}
和cancel
方法定义如下:
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Visit cancel(Visit visit) throws Exception {
// Some business logic
}
出于业务目的,我需要cancel
方法每个呼叫进行一个事务,目前,refundNotAssignedVisits
为@Transactional
,以便启用Hibernate会话,因此我能够在cancel
方法中对相关实体使用延迟加载。
这会导致诸如重复提交之类的问题,我想知道什么是实现我想要的最佳模式:有一个@Scheduled
方法可以启用Hibernate会话,以便通过一个事务多次调用另一个方法每次通话。
答案 0 :(得分:1)
@Transactional
的{{1}}将创建另一个新的Hibernate会话,因此REQUIRES_NEW
内部的会话将不同于用于加载实体的会话,这对我来说似乎很尴尬。通常,我们使用同一会话来加载和管理事务中的同一实体。
我将代码重构为以下内容:
cancel()
:
VisitService
BatchService:
//Cannel by visitorId and load the Visitor by Id in a new transaction
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Visit cancel(Integer visitorId) throws Exception {
Visit visit= session.get(Visit.class , visitorId);
cancel(visit);
}
@Override
public Visit cancel(Visit visit) throws Exception {
// Some business logic
}
//Add method to return the IDs only
@Transactional(readOnly=true)
public Set<Integer> findRefundableVisitId(){
}
通过这种方式,每笔退款都将在其自己的交易中执行,并且用于加载退款访问者的交易无需等待所有退款完成就可以提交并且不再需要“重复提交”。