Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/395.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在一个事务中从调度程序调用2个服务方法的最佳方法_Java_Spring_Design Patterns_Transactions_Scheduled Tasks - Fatal编程技术网

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明确使用可以用于方面的东西总是让我感到害怕。你对我的选择怎么看?(为调度程序创建一个单独的服务,这两个服务都称为服务)。现在我有了这样一个实现——一个服务注入另一个服务。并在其方法(在事务中)的末尾调用此服务的方法。我想重写它,以便为调度程序提供一个单独的服务,并在其中将两个服务的调用封装在一个单独的事务中。于是问了一个问题。