Java 在一个事务中从调度程序调用2个服务方法的最佳方法
我有个调度员:Java 在一个事务中从调度程序调用2个服务方法的最佳方法,java,spring,design-patterns,transactions,scheduled-tasks,Java,Spring,Design Patterns,Transactions,Scheduled Tasks,我有个调度员: @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.userS
@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));
}
}
在UserService中,我保存新用户并引发异常:
@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) 将@Transactional
添加到private void process()上方,并将此方法更改为public
2) 在UserService
在第一种情况下,它没有帮助,因为process()witn@Transactional
调用来自同一个类
在第二种情况下,它有帮助
但如果我添加新服务,例如LogService:
@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。处理一个事务中的调用和logService。保存另一个事务中的CAL。我需要在一次交易中呼叫bouth服务
我认为有两种方式:
1) 将logService
注入userService
并在userService.process中调用logService.save
2) 使用方法process
创建新服务,例如SchedulerService
,并在此服务中注入userService
和logService
。并在一个事务中调用bouth服务
在第一种情况下,我在userService
中获得了新的依赖项,这可能违反此服务中的责任范围。为什么服务应该知道拉另一个服务
在第二种情况下,我需要创建附加服务(另一个类)
最好能够对内部调度器方法@Transactional
注释进行注释。我知道这可以用cglib代替proxy来完成,但我使用proxy
哪种方法更好?我想,这是PlatformTransactionManager
的一个很好的用例,有或没有TransactionTemplate
为此,我将使用纯平台TransactionManager
解决方案
如果您使用的是springboot
,那么默认情况下它将作为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()));
}
});
cglib也是一个代理。区别一个是基于接口的,另一个是基于类的,但两者都是代理。使用process
方法中的TransactionTemplate
将两种方法包装在一个事务中。或者将过程
方法移动到另一个类,只需将该方法标记为@Transactional
@M.Deinum”或者将过程方法移动到另一个类,只需将该方法标记为@Transactional”-这只是我的第二个案例-“2)使用方法process创建新服务,例如SchedulerService,并在此服务中注入userService和logService。并在一个事务中调用bouth服务。”“不要。而是使用TransactionTemplate
将这两种方法包装到事务中。节省了手动执行所有提交/回滚等操作的开销。@M.Deinum我完全不同意。我更喜欢在提交或回滚时明确地看到。但我想这是品味的问题。这就是为什么我写了带/不带。我们可以同意不同意,但Spring团队也建议在考虑PlatformTransactionManager
@M.Deinum之前使用TransactionTemplate
。@M.Deinum我将添加一个例子。@M.Deinum明确使用可以用于方面的东西总是让我感到害怕。你对我的选择怎么看?(为调度程序创建一个单独的服务,这两个服务都称为服务)。现在我有了这样一个实现——一个服务注入另一个服务。并在其方法(在事务中)的末尾调用此服务的方法。我想重写它,以便为调度程序提供一个单独的服务,并在其中将两个服务的调用封装在一个单独的事务中。于是问了一个问题。