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.
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
@TransactionManagementannotation is only used to switch an EJB bean that already gets CMT from the container into BMT (Bean Managed Transactions). TheCONTAINERconstant is more for completeness, it’s what you get when you omit the annotation altogether.Likewise, the
@TransactionAttributewill 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.The right way is to use a component model that already gets CMT from the container, like a stateless session bean:
Then inject this bean (using
@EJBor@Inject) into your named beans and use it. Alternatively this bean can be named too using@Namedso it can be used directly in EL, but this is not always recommended.The
@Statelessbean does not allow scoping (it’s basically ‘invocation-scoped’), but the@Statefulmodel 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: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.