每个预定方法如何具有一个Hibernate会话

时间:2018-12-31 11:01:43

标签: java spring hibernate spring-scheduled

我有以下设置:

@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会话,以便通过一个事务多次调用另一个方法每次通话。

1 个答案:

答案 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(){

}

通过这种方式,每笔退款都将在其自己的交易中执行,并且用于加载退款访问者的交易无需等待所有退款完成就可以提交并且不再需要“重复提交”。