我想将一个对象( 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 范围的。如果您只为实体管理器执行此操作,请记住:有一些方法可以使用 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/