java - 使用 Spring Data 和 Hibernate 时如何正确执行后台线程?

标签 java multithreading spring hibernate spring-data

我正在构建一个使用 Spring Data 和 Hibernate 的简单 Tomcat webapp。有一个端点做了很多工作,所以我想将工作卸载到后台线程,这样 Web 请求就不会在工作完成时挂起 10 多分钟。所以我在组件扫描包中编写了一个新服务:

@Service
public class BackgroundJobService {
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    public void startJob(Runnable runnable) {
         threadPoolTaskExecutor.execute(runnable);
    }
}

然后在 Spring 中配置 ThreadPoolTask​​Executor:

<bean id="threadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="5" />
    <property name="maxPoolSize" value="10" />
    <property name="queueCapacity" value="25" />
</bean>

这一切都很好。但是,问题来自Hibernate。在我的可运行文件中,查询只工作了一半。我能做到:

MyObject myObject = myObjectRepository.findOne()
myObject.setSomething("something");
myObjectRepository.save(myObject);

但是如果我有延迟加载的字段,它会失败:

MyObject myObject = myObjectRepository.findOne()
List<Lazy> lazies = myObject.getLazies();
for(Lazy lazy : lazies) { // Exception
    ...
}

我收到以下错误:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.stackoverflow.MyObject.lazies, could not initialize proxy - no Session

所以在我(Hibernate 新手)看来,新线程在这些自制线程上没有 session ,但 Spring Data 会自动为 HTTP 请求线程创建新 session 。

  • 有没有办法在 session 中手动启动新 session ?
  • 或者告诉线程池为我做这件事的方法?
  • 进行此类工作的标准做法是什么?

我已经能够通过在 @Transactional 方法中执行所有操作来解决它,但我很快就知道这不是一个很好的解决方案,因为它不会让我使用适合 Web 请求的方法。

谢谢。

最佳答案

使用 Spring,您不需要自己的执行程序。一个简单的注释 @Async 将为您完成工作。只需在您的服务中注释您的 heavyMethod 并返回 void 或 Future 对象,您将获得一个后台线程。我会避免在 Controller 级别使用异步注释,因为这将在请求池执行器中创建一个异步线程,并且您可能会用完“请求接受器”。

您的惰性异常问题来自您怀疑没有 session 的新线程。为避免此问题,您的异步方法应处理完整的工作。不要提供以前加载的实体作为参数。该服务可以使用 EntityManager,也可以是事务性的。

我自己不合并 @Async@Transactional 所以我可以以任何一种方式运行服务。我只是在服务周围创建异步包装器,并在需要时使用这个包装器。 (例如,这简化了测试)

@Service
public class AsyncService {

    @Autowired
    private Service service;

    @Async
    public void doAsync(int entityId) {
        service.doHeavy(entityId);
    }
}

@Service
public class Service {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void doHeavy(int entityId) {
        // some long running work
    }
}

关于java - 使用 Spring Data 和 Hibernate 时如何正确执行后台线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24916104/

相关文章:

java - 如何在多个 mongo 存储库上使用 QueryDSL?

java - ReactiveCrudRepository 与 R2dbcRepository

java - 为什么我会收到 bean 'com.mypackage.service.blog.BlogService' 的 NoSuchBeanDefinitionException

java - 不正确的延迟初始化

java - 从java中的https-URL读取

java - 我如何将 Google Maps V2 显示到自定义对话框 android

java - 这段代码有多少应该位于 UI 线程内?

java - SwingWorker 和 Executor 的区别

java - 如何使用 JPA 和 Hibernate 在多对多关系中保存具有唯一字段的数据

Java 线程与 Swing