如何实现容器管理事务(CMT)? [英] How to implement container managed transaction (CMT)?

查看:104
本文介绍了如何实现容器管理事务(CMT)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  javax.persistence.TransactionRequiredException:执行此操作需要事务(使用事务或扩展持久化上下文)

以下是一些代码:



实体

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

// fields(@Column等)
// setters / getters方法
// toString,hashCode,equals方法
}

自定义注释允许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;

公共类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)
公共类ValidateReportAction扩展ReportAction实现Serializable {

private static final long serialVersionUID = -2456544897212149335L;

@Inject @MyEm
private EntityManager em;

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public synchronized String createReport(){
ReportBean report = new Report();
//设置报告属性
// em.createNamedQuery(queryName)。getResultList(); ----作品
em.persist(报告)
}
}

Q :执行em.persist时, createReport()方法中出现错误。我认为交易是由容器管理的( CMT ),但现在我觉得我错了。我哪里弄错了?实施CMT的正确方法是什么?



这也是我的 persistence.xml 配置:



< pre class =lang-xml prettyprint-override> <?xml version =1.0encoding =UTF-8?>
< persistence version =2.0
xmlns =http://java.sun.com/xml/ns/persistencexmlns: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 =MyPersistenceUnittransaction-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配置文件 - >
< property name =hibernate.connection.driver_classvalue =org.postgresql.Driver/>
< property name =hibernate.connection.passwordvalue =password/>
< property name =hibernate.connection.urlvalue =jdbc:postgresql://192.168.2.125:5432 / t_report/>
< property name =hibernate.connection.usernamevalue =username/>

<! - 指定数据库驱动程序,提供hibernate cfg查询
并提供事务管理器配置 - >
< property name =hibernate.dialectvalue =org.hibernate.dialect.PostgreSQLDialect/>
< property name =hibernate.transaction.factory_classvalue =org.hibernate.transaction.JTATransactionFactory/>
< property name =hibernate.transaction.manager_lookup_class
value =org.hibernate.transaction.JBossTransactionManagerLookup/>
< property name =hibernate.archive.autodetectionvalue =class/>

<! - 开发期间的有用配置 - 开发人员可以看到结构化SQL查询 - >
< property name =hibernate.show_sqlvalue =true/>
< property name =hibernate.format_sqlvalue =false/>

< / properties>
< / persistence-unit>
< / persistence>

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

解决方案


我在哪里弄错了?


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



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



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



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


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


正确的方法是使用已经从容器中获取CMT的组件模型,如无状态会话bean:

  @Stateless 
公共类ValidateReportAction扩展ReportAction {

@PersistenceContext(unitName =MyPersistenceUnit)
private EntityManager em;

public String createReport(){
ReportBean report = new Report();
//设置报告属性
em.persist(报告)
}
}

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



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




  • 创建实体经理非常便宜

  • 它不一定是线程安全的(官方它不是,但在某些实现中它是)

  • 无状态bean通常是池化的,因此需要自己缓存EM http session moot。



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


I wanted to persist an object(ReportBean) to the database, but I got error message:

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

Here is a bit of a code:

entity

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

    // fields (@Column, etc.)
    // setters/getters methods
    // toString , hashCode, equals methods
}

custom annotation for allowing EntityManager injection (with @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 provider

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 class - has a method to persist report to the database.
I am trying to stick with the most important imports.
If I want to use the EntityManager to create a query (or NamedQuery as in the example) everything works.

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: Here in the createReport() method when the em.persist executes is where the error appear. I thought that the transaction is managed by the container (CMT), but now I think I am wrong. Where have I made a mistake? What is the right way to implement CMT?

Here is also my persistence.xml configuration:

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

Please let me know if something in my question is not clear.

解决方案

Where have I made a mistake?

You seem to think that @TransactionManagement(TransactionManagementType.CONTAINER) enables container managed transactions and that @TransactionAttribute(TransactionAttributeType.REQUIRED) then enables a transaction on a method, for a non EJB bean.

This is however not (yet) possible in Java EE.

The @TransactionManagement annotation is only used to switch an EJB bean that already gets CMT from the container into BMT (Bean Managed Transactions). The CONTAINER constant is more for completeness, it's what you get when you omit the annotation altogether.

Likewise, the @TransactionAttribute will not enable transactions for a method on a non-EJB bean. The annotation itself exists to switch the transaction into another type (like REQUIRES_NEW). For an EJB it would not even be normally needed, since this too is the default and it too mainly exists for completeness, but can also be used to switch a single method back to REQUIRES if transactions are changed on the class level.

What is the right way to implement CMT?

The right way is to use a component model that already gets CMT from the container, like a stateless 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)
    }
}

Then inject this bean (using @EJB or @Inject) into your named beans and use it. Alternatively this bean can be named too using @Named so it can be used directly in EL, but this is not always recommended.

The @Stateless bean does not allow scoping (it's basically 'invocation-scoped'), but the @Stateful model can be session scoped as your original bean was. However, with the given functionality it doesn't need to be session scoped. If you only did this for the entity manager, then remember:

  • The entity manager is very cheap to create
  • It is not necessarily thread-safe (officially it's not, but in some implementations it is)
  • Stateless beans are typically pooled, making the need to cache the EM yourself in an http session moot.

There are ways to implement something that looks a bit like CMT using CDI and JTA, but if you want true CMT then for the moment this is the only way. There are plans to break the fixed component models like stateless, stateful, singleton and message driven up into individual (CDI) annotations (see http://java.net/jira/browse/EJB_SPEC, and specifically for your question Decoupling the @TransactionAttribute annotation from the EJB component model), but this hasn't happened yet.

这篇关于如何实现容器管理事务(CMT)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆