java Spring服务保存方法和启动线程

pbgvytdp  于 2023-09-29  发布在  Java
关注(0)|答案(1)|浏览(87)

我想在Rest端点上保存一个实体,并立即返回DTO。在那之后,我想添加一些需要一些时间的数据到实体中并保存它。
这就是我的问题真正归结为,在Sping Boot 项目中有没有一种首选的方法来做到这一点?
仅供参考,我想出了两种方法,这两种方法都没有完全发挥作用:

方式1 -异步

@Service
public class EntityServiceImpl implements EntityService {

    @Override
    @Transactional
    public Entity save(Entity entity) {
        Entity saved = repository.save(entity);
        asyncCalculation(entity.getId());
        return saved;
    }

    @Async
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private void asyncCalculation(Long id) {
        Entity entity = repository.findById(id).orElseThrow( () ... );
        entity.setData(longCalculation(entity));
        repository.save(entity);
    }

}

我在我的一个@Configuration类上有@EnableAsync,并且在所述配置类中还有一个Bean:

@Configuration
@ComponentScan
@EnableAsync
@EnableJpaRepositories(basePackages = "org.repository")
@EnableTransactionManagement
public class SpringConfiguration{

    @Bean
    public ThreadPoolTaskExecutor asyncExecutor() {
        // setting a breakpoint here gets this called, so this should mean the configuration
        // gets triggered?
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); 
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("AsyncThread-");
        executor.initialize();
        return executor;
    }

然而,这并不运行pklc,它只是顺序运行。是否缺少某些配置?另外,我不太确定这里的transactions,因为save()方法中的commit直到返回之后才出现,所以我不完全理解为什么这会起作用,因为我会在实体真正保存到数据库之前访问它。

方法2 -线程和自己的事务管理

与之前相同的服务类,只是:

@Autowired
        private PlatformTransactionManager transactionManager;

        @Override
        // no transactional
        public Entity save(Entity entity) {
            TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);

            Entity savedEntity = transactionTemplate.execute(status -> {
                return repository.save(entity)
            });
            // now it is commited
            
            new Thread(() -> {
                asyncCalculation(savedEntity.getId());
            }).start();

            return savedEntity ;
        }

        // no @Async
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        private void asyncCalculation(Long id) {
            Entity entity = repository.findById(id).orElseThrow( () ... );
            entity.setData(longCalculation(entity));
            // longCalculation triggers LazyInitializations
            repository.save(entity);
        }

longCalculation()触发实体的初始化,但是尽管我用@Transactional(propagation = Propagation.REQUIRES_NEW)注解了这个方法,我还是得到了一个LazyInitializationException:无法初始化代理[org.project.domain.SubEntity#767204] -无会话。SubEntity通过@ManyToOne(fetch = FetchType.LAZY)连接到Entity

carvr3hs

carvr3hs1#

基于代理的AOP(这是Spring的默认设置,用于@Async@Transactional)不能处理内部方法调用。仅仅是因为你在物体内部。
你可以用几种方法来解决这个问题:
1.将此方法移动到另一个类并调用它
1.通过进行自调用,这意味着将服务的示例注入到自身中并调用其上的方法(导致外部调用)。
1.对于这种特殊情况:创建一个监听事件的@TransactionalEventListener,并将其绑定到AFTER_COMMIT阶段(数据以这种方式呈现)。

public record MyEvent(long id) { }
public class MyEventListener {

  @Async
  @TransactionalEventListener(phase = AFTER_COMMIT)
  public void calculation(MyEvent evt) {
    Entity entity = repository.findById(id).orElseThrow( () ... );
    entity.setData(longCalculation(entity));
    // longCalculation triggers LazyInitializations
    repository.save(entity);
  }
}
@Service
public class EntityServiceImpl implements EntityService {

    private final ApplicationEventPublisher publisher;

    public EntityServiceImpl(ApplicationEventPublisher publisher) {
      this.publisher = publisher;
    }

    @Override
    @Transactional
    public Entity save(Entity entity) {
        Entity saved = repository.save(entity);
        publisher.publishEvent(new MyEvent(entity.getId());
        return saved;
    }
}

相关问题