【问题标题】:How to implement container managed transaction (CMT)?如何实现容器管理事务(CMT)?
【发布时间】:2012-02-05 00:41:40
【问题描述】:

我想将一个对象(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:在createReport() 方法中,当em.persist 执行时出现错误。我以为事务是由容器管理的(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> 

如果我的问题中有什么不清楚的地方,请告诉我。

【问题讨论】:

    标签: jakarta-ee transactions cdi


    【解决方案1】:

    我在哪里做错了?

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

    然而,这在 Java EE 中(还)不可能。

    @TransactionManagement 注解仅用于将已经从容器中获取 CMT 的 EJB bean 切换为 BMT(Bean Managed Transactions)。 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 中并使用它。或者,这个 bean 也可以使用 @Named 命名,因此可以直接在 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),但这还没有发生。

    【讨论】:

    • @Arjan Tijms,这个答案还有效吗?
    • @Ced 好吧,“@Transactional”现在可以作为单独的注释使用,所以“还没有发生”现在已经发生了;)
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      @Stateless
      public class ValidateReportAction extends ReportAction {
      ...
      
        @TransactionAttribute(TransactionAttributeType.REQUIRED)
        public synchronized String createReport() {
      

      【讨论】:

        猜你喜欢
        • 2015-11-26
        • 2012-09-26
        • 1970-01-01
        • 2011-07-20
        • 1970-01-01
        • 1970-01-01
        • 2013-03-27
        • 1970-01-01
        • 2017-06-23
        相关资源
        最近更新 更多