我想将一个对象(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>
如果我的问题不清楚,请告诉我。
答案 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的会话作用域。但是,使用给定的功能,它不需要是会话作用域。如果您只为实体经理执行此操作,请记住:
有一些方法可以使用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() {