I have scheduller:
@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));
}
}
在用户服务中,我保存新用户并抛出异常:
@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();
}
}
因此,尽管有异常,用户仍会保存。要解决此问题,我可以应用几种方法:
1)在private void process()
上方添加@Transactional
并将此方法更改为public
2)在UserService
中添加@Transactional
public void process(User user)
方法
上方在第一种情况下,它没有帮助,因为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()));
}
}
问题:
userService.process
在一个事务中调用,在另一个事务中调用logService.save
cal。我需要在一个事务中调用 bouth 服务。
我看到两种方式:
1)将logService
注入userService
并在userService.process
方法中调用logService.save
2) 创建新服务,例如使用方法process
SchedulerService
,并在此服务中注入userService
和logService
。并在一笔交易中调用bouth服务。
在第一种情况下,我在userService
中获得新的依赖关系,这可能会违反此服务中的责任范围。为什么服务应该知道拉取另一个服务
在第二种情况下,我需要创建额外的服务(另一个类)
能够注释内部调度程序方法@Transactional
注释是理想的。我知道这可以使用cglib而不是代理来完成,但我使用代理。
哪种方法会更好?
恕我直言,这是一个很好的用例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
将"消除"显式调用commit
和rollback
的需要。
您可以将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()));
}
});