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

标签 jakarta-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)(使用 @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> 

如果我的问题中的某些内容不清楚,请告诉我。

最佳答案

Where have I made a mistake?



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

然而,这在 Java EE 中(还)是不可能的。
@TransactionManagement注解仅用于将已经从容器中获取 CMT 的 EJB bean 切换为 BMT(Bean Managed Transactions)。 CONTAINER constant 更多的是为了完整性,这是您完全省略注释时得到的结果。

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

What is the right way to implement CMT?



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

然后将此 bean (使用 @EJB@Inject )注入(inject)您命名的 bean 并使用它。或者,这个 bean 也可以使用 @Named 命名。所以它可以直接在 EL 中使用,但并不总是建议这样做。
@Stateless bean 不允许作用域(它基本上是“调用作用域”),但是 @Stateful模型可以像您的原始 bean 一样具有 session 范围。但是,对于给定的功能,它不需要是 session 范围的。如果您只为实体管理器执行此操作,请记住:
  • 创建实体管理器很便宜
  • 它不一定是线程安全的(官方不是,但在某些实现中它是)
  • 无状态 bean 通常是池化的,因此需要自己在 http session 中缓存 EM。

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

    关于jakarta-ee - 如何实现容器管理事务(CMT)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8772175/

    相关文章:

    sql - 如何在 ADO.NET 中设置 XACT_ABORT

    java - CDI 是否为 RequestScoped 重用代理?

    jakarta-ee - 在类库中使用@Inject 注入(inject)EJB

    java - 如何在同一 Java EE 应用程序中同时配置 BASIC 和 FORM 身份验证方法

    apache - 如何在 apache tomcat 中自动生成堆转储?

    java - 如何在类的 Timer 方法中可见 HTML 标记组件

    google-app-engine - CDI/Weld 无法执行 RESTEasy 资源的构造函数注入(inject)

    java - 无法导入 javax.ejb.* 包

    java - Grails 1.2.0 - 强制服务回滚

    sql - Sqlite事务-储备锁何时生效