我们通过JPA和Spring使用Hibernate来管理Web应用程序中的对象持久性.我们使用open-session-in-view模式为响应http请求的线程创建会话.我们还使用了一些不产生视图的线程 – 它们只是不时醒来完成工作.这会产生问题,因为默认情况下它们没有打开会话,所以它们会产生异常
org.hibernate.SessionException: Session is closed!
要么
could not initialize proxy - no Session
我们发现,如果每个后台线程在使用@Transactional注释的方法中调用其逻辑,则没有这种类型的异常,因为@Transactional确保线程在事务内部时具有会话.
它解决了一段时间的问题,但我不认为这是一个很好的解决方案 – 使长时间运行的方法事务导致问题,因为在提交事务之前,其他线程无法看到数据库中所做的更改.
我创建了一个java-pseudocode示例来更好地说明我的问题:
public class FirstThread {
...
@Transactional
public void processQueue() {
for(element : queue){
if(elementCanBeProcessed(element)){
elementDao.saveIntoDatabase(element);
secondThread.addToQueue(element.getId());
}
}
}
private boolean elementCanBeProcessed(element){
//code that gets a few objects from database and processes them
}
}
如果我用@Transactional注释整个processQueue方法所做的更改
elementDao.saveIntoDatabase(element);
在事务提交之前不会在secondThread中看到(因此直到整个队列被处理).如果我不这样做,那么线程将不会在elementCanBeProcessed中有会话,并且它将无法访问数据库.我也无法注释elementCanBeProcessed,因为它是这个类中的私有方法,我必须将它移动到另一个类,以便Spring代理可以工作.
最佳答案
我不知道任何针对此的Spring-ready解决方案.所以,我认为你需要实现一个类似于OpenEntityManagerInViewInterceptor类.
原文链接:https://www.f2er.com/spring/431746.html基本上,当你的线程启动时,你需要使用TransactionSynchronizationManager来绑定资源()一个EntityManagerHolder的实例,当线程完成时你需要使用unbindResource().
OpenEntityManagerInViewInterceptor的核心部分是:
if (TransactionSynchronizationManager.hasResource(getEntityManagerFactory())) {
...
}
else {
logger.debug("opening JPA EntityManager in OpenEntityManagerInViewInterceptor");
try {
EntityManager em = createEntityManager();
EntityManagerHolder emHolder = new EntityManagerHolder(em);
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(),emHolder);
...
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager",ex);
}
}
如果您实施了,请在此处发布代码作为答案.