在一次事务中从调度程序调用2种服务方法的最佳方法

时间:2019-03-12 08:47:35

标签: java spring design-patterns transactions scheduled-tasks

我有调度员:

HTTP

在UserService中,我保存了新用户并抛出异常:

@Component
public class MyScheduler {

    private static final long INIT_DELAY = 1L;
    private static final long DELAY = 10L;

    private final UserService userService;

    public MyScheduler(UserService userService) {
        this.userService = userService;
    }

    @EventListener(ApplicationReadyEvent.class)
    public void schedule() {
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleWithFixedDelay(this::process, INIT_DELAY, DELAY, TimeUnit.SECONDS);
    }

    private void process() {
        userService.process(new User("Bill", 20));
    }
}

结果,尽管有例外,仍然保存了用户。要解决此问题,我可以采用几种方法:

1)在@Slf4j @Service public class UserServiceImpl implements UserService { private final UserRepository userRepository; public UserServiceImpl(UserRepository userRepository) { this.userRepository = userRepository; } @Override public void process(User user) { log.info("Start process..."); userRepository.save(user); methodWithException(); log.info("End process..."); } private void methodWithException() { throw new RuntimeException(); } } 上方添加@Transactional并将此方法更改为private void process()

2)在public的{​​{1}}方法上方添加@Transactional

在第一种情况下,这无济于事,因为public void process(User user)来自同一类的调用。

在第二种情况下有帮助。

但是如果我添加新服务,例如LogService:

UserService

并将调度程序更改为:

process() witn @Transactional

问题

@Service public class LogServiceImpl implements LogService { private final LogRepository logRepository; public LogServiceImpl(LogRepository logRepository) { this.logRepository = logRepository; } @Transactional @Override public Log save(Log log) { return logRepository.save(log); } } 调用一个事务,而@Component public class MyScheduler { private static final long INIT_DELAY = 1L; private static final long DELAY = 10L; private final UserService userService; private final LogService logService; public MyScheduler(UserService userService, LogService logService) { this.userService = userService; this.logService = logService; } @EventListener(ApplicationReadyEvent.class) public void schedule() { ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); scheduledExecutorService.scheduleWithFixedDelay(this::process, INIT_DELAY, DELAY, TimeUnit.SECONDS); } private void process() { User user = userService.process(new User("Bill", 20)); logService.save(new Log(user.getId(), new Date())); } } 调用另一个事务。我需要在一笔交易中提供电话通话服务。

我看到两种方式:

1)向userService.process注入logService.save并以logService方法调用userService

2)使用方法logService.save创建一个新服务,例如userService.process,并在该服务中注入SchedulerServiceprocess。并在一次交易中调用bouth服务。

在第一种情况下,我在userService中获得了新的依赖关系,这可能会违反此服务的职责范围。服务为什么要知道要拉其他服务

在第二种情况下,我需要创建其他服务(另外一个类)

能够对内部Schedulers方法logService进行注释是很理想的。我知道可以使用 cglib 代替 proxy 来完成,但是我使用代理。

哪种方法更好?

1 个答案:

答案 0 :(得分:0)

天哪,无论有没有PlatformTransactionManager,这都是TransactionTemplate的好用例。
为此,我要使用一种纯粹的PlatformTransactionManager解决方案。

如果您使用的是Spring Boot,则默认情况下会将其作为Bean。

@Component
class MyScheduler {
    private static final long INIT_DELAY = 1L;
    private static final long DELAY = 10L;

    private final PlatformTransactionManager txManager;
    private final ConcurrencyService userService;
    private final LogService logService;

    MyScheduler(
            final PlatformTransactionManager txManager,
            final ConcurrencyService userService,
            final LogService logService) {
        this.txManager = txManager;
        this.userService = userService;
        this.logService = logService;
    }

    @EventListener(ApplicationReadyEvent.class)
    public void schedule() {
        final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleWithFixedDelay(this::process, INIT_DELAY, DELAY, TimeUnit.SECONDS);
    }

    private void process() {
       final DefaultTransactionDefinition definition = new DefaultTransactionDefinition(PROPAGATION_REQUIRES_NEW);
       final TransactionStatus tx = txManager.getTransaction(definition);

       try {
          final User user = userService.process(new User("Bill", 20));
          logService.save(new Log(user.getId(), new Date()));
          txManager.commit(tx);
       } catch (final YourException e) {
          txManager.rollback(tx);
       }
    }
}

使用TransactionTemplate将“消除”显式调用commitrollback的需要。

您可以将TransactionTemplate作为Bean,也可以像我在这里所做的那样从PlatformTransactionManager手动构造它。

final TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    @Override
    protected void doInTransactionWithoutResult(final TransactionStatus status) {
       final User user = userService.process(new User("Bill", 20));
       logService.save(new Log(user.getId(), new Date()));
    }
});