如何实现容器管理事务(CMT)?

时间:2012-01-07 18:55:43

标签: java-ee transactions cdi

我想将一个对象(ReportBean)保留到数据库中,但是收到了错误消息:

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

这是一些代码:

实体

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

    // fields (@Column, etc.)
    // setters/getters methods
    // toString , hashCode, equals methods
}
允许EntityManager注入的

自定义注释(使用@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提供程序

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类 - 有一种方法可以将报告持久存储到数据库中。
我正在努力坚持最重要的进口 如果我想使用EntityManager创建查询(或示例中的NamedQuery),一切正常。

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:执行em.persist时createReport()方法中出现错误。我认为事务是由容器(CMT)管理的,但现在我觉得我错了。我哪里弄错了?实施CMT的正确方法是什么?

这也是我的persistence.xml配置:

<?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> 

如果我的问题不清楚,请告诉我。

3 个答案:

答案 0 :(得分:18)

  

我在哪里弄错了?

您似乎认为@TransactionManagement(TransactionManagementType.CONTAINER)启用容器管理的事务,然后@TransactionAttribute(TransactionAttributeType.REQUIRED)启用方法上的事务,对于非EJB bean。

然而,这在Java EE中是不可能的。

@TransactionManagement注释仅用于将已经从容器获取CMT的EJB bean切换到BMT(Bean管理事务)。 CONTAINER常量更完整,这是你完全省略注释时得到的结果。

同样,@TransactionAttribute不会为非EJB bean上的方法启用事务。注释本身存在以将事务切换到另一种类型(如REQUIRES_NEW)。对于EJB,它通常不需要,因为这也是默认值,它也主要用于完整性,但如果在类级别更改事务,也可以用于将单个方法切换回REQUIRES。

  

实施CMT的正确方法是什么?

正确的方法是使用已经从容器中获取CMT的组件模型,如无状态会话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)
    }
}

然后将此bean(使用@EJB@Inject)注入您的命名bean并使用它。或者,也可以使用@Named命名此bean,因此可以直接在EL中使用,但并不总是建议这样做。

@Stateless bean不允许作用域(它基本上是'调用作用域'),但@Stateful模型可以作为原始bean的会话作用域。但是,使用给定的功能,它不需要是会话作用域。如果您只为实体经理执行此操作,请记住:

  • 实体经理创建
  • 非常便宜
  • 它不一定是线程安全的(官方它不是,但在某些实现中它是)
  • 无状态bean通常是池化的,因此需要在http会话中自行缓存EM。

有一些方法可以使用CDI和JTA实现看起来有点像CMT的东西,但如果你想要真正的CMT,那么目前这是唯一的方法。有计划打破固定组件模型,如无状态,有状态,单一和消息驱动到个人(CDI)注释(请参阅http://java.net/jira/browse/EJB_SPEC,特别是您的问题Decoupling the @TransactionAttribute annotation from the EJB component model),但这没有发生了。

答案 1 :(得分:4)

更新到Java EE 7(CDI 1.1),您现在可以使用@Transactional在CDI bean中启用CMT,不再需要使用EJB。

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

答案 2 :(得分:1)

你是对的,但在你的解决方案中你创建了一个嵌套的 事务,从调用上下文运行。 不幸的是,我无法找到通过的解决方案 像这样的转换上下文

@Stateless
public class ValidateReportAction extends ReportAction {
...

  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public synchronized String createReport() {