如何使用事务范围的持久化上下文进行非事务性读取查询?

时间:2012-05-14 03:08:40

标签: spring jpa scope transactional

我阅读了Spring文档,并说:

  

@PersistenceContext注释具有可选的属性类型,默认为   PersistenceContextType.TRANSACTION。此默认值是您收到共享所需的内容   EntityManager代理。

  1. 这是否意味着我必须让EntityManager在事务中工作?
  2. 如何处理非事务性方法(读取查询),例如下面代码中的loadProductsByCategory?
  3. “共享”是什么意思?如何使用EntityManager与他人共享?
  4. 我是否需要将@Transactional添加到方法loadProductsByCategory才能将EntityManager绑定到线程?因为ProductDaoImpl类是单例并且在多线程中工作,但是entityManager不是线程安全的。

    @Service
    public class ProductDaoImpl implements ProductDao {
        @PersistenceContext
        private EntityManager em;
        public Collection loadProductsByCategory(String category) {
            Query query = em.createQuery("from Product as p where p.category = :category");
            query.setParameter("category", category);
            return query.getResultList();
        }
        @Transactional
        public void loadProductsByCategory(Product product) {
            em.persist(product);
        }
    }
    

1 个答案:

答案 0 :(得分:7)

在以下博客链接中对此行为进行了详细分析。 http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/以下是我的摘要。

EntityManager是一个java接口,它允许spring提供它自己的接口实现。 spring注入的实现使用动态代理来处理对实体管理器的调用。动态代理的行为方式如下。

如果@Transactional中没有loadProductsByCategory注释弹簧将在调用EntityManager时创建em.createQuery em的实例,spring将不会返回创建的Query对象通过JPA,但它会返回EntityManager的Spring代理,这个春天代理将所有调用转发到Query的实际执行,并等到getResultgetSingleResult或{{1调用它会立即关闭实体管理器。

因此,当没有executeUpdate时,Spring将确保实体管理器尽快关闭,即在实体管理器上的每个方法调用之后或在提取结果集之后。在上面的示例中,如果您注释掉query.getResultList(),您将最终泄漏一个未关闭的实体管理器实例

@Transactional

当存在@Transactional属性时,spring事务管理器将在调用事务方法之前创建事务上下文。当事务方法调用实体管理器上的任何方法时,Spring将创建一个全新的EntityManager实例,并将其与当前的跨国实例相关联,如果事务方法调用另一个方法,该方法调用另一个方法,所有这些方法使用实体管理器,则实体管理器为在所有这些电话中共享。这是一个例子。

 public Collection loadProductsByCategory(String category) {
        Query query = em.createQuery("from Product as p where p.category = :category");
        query.setParameter("category", category);
        return null;
        // a leak of an entity manager will happen because getResultList() was never called, so 
        // spring had no chance to close the entity manager it created when em.creaueQuery was 
        // invoked.
        // return query.getResultList(); 
 }

回答你的上一个问题。

  

我是否需要将@Transactional添加到方法loadProductsByCategory才能将EntityManager绑定到线程?因为ProductDaoImpl类是单例并且在多线程中工作,但是entityManager不是线程安全的。

@Transactional导致spring将spring tx绑定到当前线程,然后实体管理器绑定到spring tx,spring tx通过本地线程的本地线程绑定。

Pro JPA 2书在第6章中对这些内容有一个不错的解释,它有点密集,它在Java EE的上下文中进行了解释,但春天的步骤是相同的​​。