我正在构建一个使用 Spring Data 和 Hibernate 的简单 Tomcat webapp。有一个端点做了很多工作,所以我想将工作卸载到后台线程,这样 Web 请求就不会在工作完成时挂起 10 多分钟。所以我在组件扫描包中编写了一个新服务:
@Service
public class BackgroundJobService {
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
public void startJob(Runnable runnable) {
threadPoolTaskExecutor.execute(runnable);
}
}
然后在 Spring 中配置 ThreadPoolTaskExecutor
:
<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/