How to implement container managed transaction (CMT)?

后端 未结 3 895
余生分开走
余生分开走 2020-12-16 22:06

I wanted to persist an object(ReportBean) to the database, but I got error message:

javax.persistence.TransactionRequiredException: Transactio         


        
相关标签:
3条回答
  • 2020-12-16 22:27

    update to java EE 7 (CDI 1.1), you can now use @Transactional to enable the CMT in CDI beans, no need to use EJB anymore.

    reference:JEE7: Do EJB and CDI beans support container-managed transactions?

    0 讨论(0)
  • 2020-12-16 22:28

    Where have I made a mistake?

    You seem to think that @TransactionManagement(TransactionManagementType.CONTAINER) enables container managed transactions and that @TransactionAttribute(TransactionAttributeType.REQUIRED) then enables a transaction on a method, for a non EJB bean.

    This is however not (yet) possible in Java EE.

    The @TransactionManagement annotation is only used to switch an EJB bean that already gets CMT from the container into BMT (Bean Managed Transactions). The CONTAINER constant is more for completeness, it's what you get when you omit the annotation altogether.

    Likewise, the @TransactionAttribute will not enable transactions for a method on a non-EJB bean. The annotation itself exists to switch the transaction into another type (like REQUIRES_NEW). For an EJB it would not even be normally needed, since this too is the default and it too mainly exists for completeness, but can also be used to switch a single method back to REQUIRES if transactions are changed on the class level.

    What is the right way to implement CMT?

    The right way is to use a component model that already gets CMT from the container, like a stateless session bean:

    @Stateless
    public class ValidateReportAction extends ReportAction {
    
        @PersistenceContext(unitName = "MyPersistenceUnit")
        private EntityManager em;
    
        public String createReport() {
            ReportBean report = new Report();
            // set report properties        
            em.persist(report)
        }
    }
    

    Then inject this bean (using @EJB or @Inject) into your named beans and use it. Alternatively this bean can be named too using @Named so it can be used directly in EL, but this is not always recommended.

    The @Stateless bean does not allow scoping (it's basically 'invocation-scoped'), but the @Stateful model can be session scoped as your original bean was. However, with the given functionality it doesn't need to be session scoped. If you only did this for the entity manager, then remember:

    • The entity manager is very cheap to create
    • It is not necessarily thread-safe (officially it's not, but in some implementations it is)
    • Stateless beans are typically pooled, making the need to cache the EM yourself in an http session moot.

    There are ways to implement something that looks a bit like CMT using CDI and JTA, but if you want true CMT then for the moment this is the only way. There are plans to break the fixed component models like stateless, stateful, singleton and message driven up into individual (CDI) annotations (see http://java.net/jira/browse/EJB_SPEC, and specifically for your question Decoupling the @TransactionAttribute annotation from the EJB component model), but this hasn't happened yet.

    0 讨论(0)
  • 2020-12-16 22:46

    You are right, but in your solution you create a nested transaction, that runs insolated from the calling context. Unfortunately, i was not able to find a solution to pass a transctions context like this

    @Stateless
    public class ValidateReportAction extends ReportAction {
    ...
    
      @TransactionAttribute(TransactionAttributeType.REQUIRED)
      public synchronized String createReport() {
    
    0 讨论(0)
提交回复
热议问题