开发者

Manual Transactional Service and DAO layer for JPA with Spring

I am using JPA with Spring. If I were to let Spring handle the transactions, then this is what my Service layer would look like assuming the EntityManager has been properly injected into the DAOs:

MyService {

   @Transactional
   public void myMethod() {
       myDaoA.doSomething();
       myDaoB.doSomething();
    }
}

However, if I were to do transactions manually, I have to make sure to pass that instance of EntityManager into each of the DAOs within a transaction. Any idea how can this be better refactored? I fee ugly doing new MyDaoA(em) or passing em into each DAO met开发者_Go百科hod like doSomething(em).

MyService {

   private EntityManagerFactory emf;

   public void myMethod() {
       EntityManager em = emf.createEntityManager();
       EntityTransaction tx = em.getTransaction();
       MyDaoA myDaoA = new MyDaoA(em);
       MyDaoB myDaoB = new MyDaoB(em);
       try {
           tx.begin();
           myDaoA.doSomething();
           myDaoB.doSomething();
           tx.commit();
       } catch(Exception e) {
           tx.rollback();
       }
    }
}


However, if I were to do transactions manually, I have to make sure to pass that instance of EntityManager into each of the DAOs within a transaction.

This is where you are wrong. From the Spring Reference, JPA section:

The main problem with such a DAO is that it always creates a new EntityManager through the factory. You can avoid this by requesting a transactional EntityManager (also called "shared EntityManager" because it is a shared, thread-safe proxy for the actual transactional EntityManager) to be injected instead of the factory:

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(); 
    }
}

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.


add this to your spring config

<bean p:entityManagerFactory-ref="emf" class='org.springframework.orm.jpa.support.SharedEntityManagerBean' />

now you can @Autowired EntityManager inside your dao

for the transaction management, since you already using spring, and @Transactional annotation, i assume you already have one transaction manager declared in your spring.xml

so using spring's transaction management

as

transactionStatus = platformTransactionManager.getTransaction(new DefaultTransactionDefinition());
// do your work here 
platformTransactionManager.commit(transactionStatus );


Shot in the dark a bit I guess, but do you know you can do:

TransactionInterceptor.currentTransactionStatus().setRollbackOnly();

That usually eliminates the majority of cases where you would want/need to use programmatic transactions in a system that otherwise has declarative transactions.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜