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

标签 spring jpa scope transactional

我读了 Spring 文档,它说:

The @PersistenceContext annotation has an optional attribute type, which defaults to PersistenceContextType.TRANSACTION. This default is what you need to receive a shared EntityManager proxy.

  1. 这是否意味着我必须让 EntityManager 在事务中工作?
  2. 对于非事务性方法(读取查询)(例如下面代码中的 loadProductsByCategory)如何工作?
  3. “共享”是什么意思?如何使用EntityManager与其他人共享?
  4. 我是否需要将 @Transactional 添加到方法 loadProductsByCategory 才能将 EntityManager 绑定(bind)到线程?因为类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);
        }
    }
    

最佳答案

以下博客链接对此行为进行了详分割析。 http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/这是我的总结。

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

如果没有@Transactional注释,如loadProductsByCategory中,spring将在em.createQuery时创建EntityManager em的实例被调用时,spring不会返回JPA创建的Query对象,而是返回EntityManager的Spring Proxy,这个spring代理将所有调用转发给Query的真正实现> 它会等待,直到调用 getResultgetSingleResultexecuteUpdate 并立即关闭实体管理器。

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

 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属性时,Spring事务管理器将在调用事务方法之前创建事务上下文。当事务方法调用实体管理器上的任何方法时,Spring将创建一个全新的EntityManager实例并将其与当前跨国相关联,如果事务方法调用另一个方法,该方法又调用另一个方法,并且所有这些方法都使用实体管理器,则实体管理器是在所有这些通话中共享。这是一个例子。

main(..)
{
    Foo foo = call spring to get foo instance 
    foo.doFoo();
} 

public class Foo {
     @PersistenceContext 
     EntityManager em; 

     @Autowired
     Bar bar;

     @Transactional 
     public doFoo(){
         // before this method is called spring starts a spring transaction
         em.createQuery(....) // here spring will create an instance of the Entity manager 
          // and assoicated with the current tx 
         bar.doBar(); // call bar transactional method 
     }
}

public calss Bar {

      @PersistenceContext 
     EntityManager em; 


     @Transactional 
     public doBar(){
         // no tx is started here because one was already started in doFoo
         em.createQuery(....) // spring looks under the current tx and finds that it has 
            // an entity manager, was created in the doFoo() method so this entity manager
            // is used, This is what is meant by sharing of the entity manager.
     }
}

回答你最后一个问题。

Do I need to add @Transactional to the method loadProductsByCategory in order to bind the EntityManager to the thread? Because the class ProductDaoImpl is singleton and works in multi-thread, but entityManager is not thread-safe.

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

Pro JPA 2 书在第 6 章对这些内容进行了很好的解释,它有点密集,并且是在 Java EE 的上下文中进行解释的,但步骤对于 spring 是相同的。

关于spring - 如何使用事务范围的持久化上下文进行非事务性读取查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10577110/

相关文章:

java - Spring 注释 @Entry.base 不支持 SpEL

java - 组织.apache.tomcat.dbcp.dbcp.SQLNestedException : Cannot create JDBC driver of class ''

javascript - javascript 和 Node.js 中的全局变量作用域

java - 了解 Hibernate 获取

jquery - $(document).ready() 内的范围?

javascript - "const"与 "console.log"一起使用时 undefined variable

java - Spring:如何使服务以 PlatformTransactionManager bean 的可用性为条件?

java - 关联表未映射 - Hibernate + netbeans

java - Spring + Quartz 重新调度或更新触发器?

java - JPA 实体关系简单示例中出现错误