Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement container managed transaction (CMT)?

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

javax.persistence.TransactionRequiredException: Transaction is required to perform this operation (either use a transaction or extended persistence context)  

Here is a bit of a code:

entity

@Entity
@Table(name="t_report")
@Access(AccessType.FIELD)
public class ReportBean implements Serializable {

    // fields (@Column, etc.)
    // setters/getters methods
    // toString , hashCode, equals methods
}

custom annotation for allowing EntityManager injection (with @Inject)

import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.*;
import java.lang.annotation.Target;

@Qualifier
@Target({TYPE, METHOD, FIELD, PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyEm {
}

EntityManager provider

import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;

public class EntityManagerProvider {

    private final String PERSISTENCE_UNIT = "MyPersistenceUnit";

    @SuppressWarnings("unused")
    @Produces
    @MyEm 
    @PersistenceContext(unitName=PERSISTENCE_UNIT, type=PersistenceContextType.TRANSACTION)
    private EntityManager em;

}

ValidateReportAction class - has a method to persist report to the database.
I am trying to stick with the most important imports.
If I want to use the EntityManager to create a query (or NamedQuery as in the example) everything works.

import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.enterprise.context.SessionScoped;


@Named("validateReportAction")
@SessionScoped
@TransactionManagement(TransactionManagementType.CONTAINER)
public class ValidateReportAction extends ReportAction implements Serializable {

    private static final long serialVersionUID = -2456544897212149335L;

    @Inject @MyEm
    private EntityManager em;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public synchronized String createReport() {
        ReportBean report = new Report();
        // set report properties
        // em.createNamedQuery("queryName").getResultList(); ---- works
        em.persist(report)
    }
}

Q: Here in the createReport() method when the em.persist executes is where the error appear. I thought that the transaction is managed by the container (CMT), but now I think I am wrong. Where have I made a mistake? What is the right way to implement CMT?

Here is also my persistence.xml configuration:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="MyPersistenceUnit" transaction-type="JTA">

        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:jboss/TimeReportDS</jta-data-source>
        <mapping-file>META-INF/orm.xml</mapping-file> 

        <class>....</class>
        <class>....</class>
        <class>....</class>

        <properties>

            <property name="jboss.entity.manager.factory.jndi.name"
                value="java:/modelEntityManagerFactory" />

            <!-- PostgreSQL Configuration File -->
            <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
            <property name="hibernate.connection.password" value="password" />
            <property name="hibernate.connection.url" value="jdbc:postgresql://192.168.2.125:5432/t_report" />
            <property name="hibernate.connection.username" value="username" />

            <!-- Specifying DB Driver, providing hibernate cfg lookup
                 and providing transaction manager configuration -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
            <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
            <property name="hibernate.transaction.manager_lookup_class"
                value="org.hibernate.transaction.JBossTransactionManagerLookup" />
            <property name="hibernate.archive.autodetection" value="class" />

            <!-- Useful configuration during development - developer can see structured SQL queries -->
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="false" />

        </properties>
    </persistence-unit>
</persistence> 

Please let me know if something in my question is not clear.

like image 587
nyxz Avatar asked Jan 07 '12 18:01

nyxz


People also ask

What is CMT transaction?

CMT is a model that performs transaction management in the EJB container. The CMT target is the Session Beans, Entity Beans, and the Message-driven Beans. In CMT, you specify the transaction attributes for each method of Bean.

What is a container managed transaction?

It commits the transaction just before the method exits. Each method can be associated with a single transaction. Nested or multiple transactions are not allowed within a method. Container-managed transactions do not require all methods to be associated with transactions.

What is Java CMT?

In JavaEE environments Hibernate can use the CMT (Container Managed Transaction) strategy which will bind hibernate transactions with the underlying JTA transaction eliminating the need to manually begin, commit and rollback transactions.

What is Bean managed transaction?

In a bean-managed transaction, the code in the session or message-driven bean explicitly marks the boundaries of the transaction. An entity bean cannot have bean-managed transactions; it must use container-managed transactions instead.


3 Answers

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.

like image 60
Arjan Tijms Avatar answered Oct 08 '22 00:10

Arjan Tijms


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?

like image 45
neptune Avatar answered Oct 08 '22 01:10

neptune


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() {
like image 36
GroovieMan Avatar answered Oct 07 '22 23:10

GroovieMan