本文介绍了如何将事务范围的持久性上下文用于非事务性读取查询?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我阅读了Spring文档,上面写着:

I read the Spring doc and it says:

  1. 这是否意味着我必须使EntityManager在事务中工作?
  2. 它如何用于非事务方法(读取查询),例如下面的代码中的loadProductsByCategory?
  3. 共享"是什么意思?如何与他人共享EntityManager?
  4. 是否需要将@Transactional添加到方法loadProductsByCategory以便将EntityManager绑定到线程?因为类ProductDaoImpl是单例且可在多线程中工作,所以EntityManager不是线程安全的.

  1. Does this mean I have to make EntityManager work in a transaction?
  2. How does it work for non-transactional method (reading query), such as the loadProductsByCategory in the below code?
  3. What does the "shared" mean? How can it use EntityManager sharing with others?
  4. 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.

@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/这是我的总结.

There is a detailed analysis of this behavior at the following blog link. http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/ Here is my summary of it.

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

EntityManager is a java interface which allows spring to provide it's own implementation of the interface. The implementation injected by spring use a dynamic proxy to handle the calls to the entity manager. The dynamic proxy behaves the following way.

如果没有@Transactional注释(如在loadProductsByCategory中一样),则在调用em.createQuery时spring将创建EntityManager em的实例,spring将不会返回JPA创建的Query对象,但会返回Spring Proxy EntityManager的此春季代理将所有调用转发到Query的实际实现,并等待直到调用getResultgetSingleResultexecuteUpdate并立即关闭实体管理器.

if there is no @Transactional annotation as in loadProductsByCategory spring will create an instance of the EntityManager em when em.createQuery is called, spring will not return the Query object created by JPA but it will return Spring Proxy of EntityManager this spring proxy forwards all calls to the real implementation of Query and it waits until the getResult or getSingleResult or executeUpdate are called and it immediately closes the Entity Manager.

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

So when there is no @Transactional Spring will make sure that the entity manager is closed as soon as possible, i.e. after each method call on the entity manager or after a result set is extracted. In your above example if you comment out the query.getResultList() you will end up leaking an entity manager instance that does not get closed

 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实例并将其与当前的跨国公司相关联,如果事务性方法调用了另一个方法,该方法又调用了另一个方法,并且所有这些方法都使用了一个实体管理器,那么该实体管理器就是在所有这些通话中共享.这是一个例子.

When there is @Transactional attribute the spring transaction manager will create a transactional context before the transactional method is called. When transactional method calls any method on the entity manager Spring will create a brand new EntityManager instance and associate it with current transnational, if the transactional method, calls another method, which calls another and all those methods use an entity manager then the entity manager is shared across all those calls. here is an example.

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.
     }
}

回答您的最后一个问题.

To answer your last last question.

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

The @Transactional causes spring to bind a spring tx to the current thread, and then the entity manager is bound to the spring tx which is bound via thread local to the current thread.

Pro JPA 2本书在第6章对此内容进行了不错的解释,虽然有点密集,但在Java EE的上下文中进行了解释,但步骤与Spring相同.

Pro JPA 2 book has a decent explanation of this stuff in chapter 6, it is bit dense and it is explained in the context of Java EE but the steps are the same for spring.

这篇关于如何将事务范围的持久性上下文用于非事务性读取查询?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 04:57