我有调度员:
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
,并在该服务中注入SchedulerService
和process
。并在一次交易中调用bouth服务。
在第一种情况下,我在userService
中获得了新的依赖关系,这可能会违反此服务的职责范围。服务为什么要知道要拉其他服务
在第二种情况下,我需要创建其他服务(另外一个类)
能够对内部Schedulers方法logService
进行注释是很理想的。我知道可以使用 cglib 代替 proxy 来完成,但是我使用代理。
哪种方法更好?
答案 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
将“消除”显式调用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()));
}
});