加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

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

发布时间:2020-12-15 07:38:25 所属栏目:Java 来源:网络整理
导读:我正在构建一个使用 Spring Data和Hibernate的简单Tomcat webapp.有一个终点可以完成很多工作,因此我想将工作卸载到后台线程,以便在完成工作时Web请求不会挂起10分钟.所以我在一个组件扫描包中写了一个新服务: @Servicepublic class BackgroundJobService {
我正在构建一个使用 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.在我的runnable中,查询只有一半工作.我可以:

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新手)新线程在这些自制线程上没有会话,但Spring Data会自动为HTTP Request线程创建新会话.

>有没有办法从会话中手动启动新会话?
>或者告诉线程池为我做这件事的方法?
>做这种工作的标准做法是什么?

我已经能够通过在@Transactional方法中做一些事情来解决它,但我很快就知道这不是一个非常好的解决方案,因为这不会让我使用适用于Web请求的方法.

谢谢.

解决方法

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

你的懒惰异常的问题出现在你没有会话的新线程中.为避免此问题,您的异步方法应该处理完整的工作.不要提供以前加载的实体作为参数.该服务可以使用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
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读