【问题标题】:Transaction propagation from CDI to EJB从 CDI 到 EJB 的事务传播
【发布时间】:2014-05-29 23:05:02
【问题描述】:

我想提交一个带有文件和其他简单字段的表单。因为发送文件并将其保存在文件系统中是 Web 层的典型做法,所以我在 CDI 托管 bean 中执行此操作。从这个 CDI 托管 bean 中,我还调用 EJB 将其他数据存储在数据库中。保存文件可能会失败,所以我用@Transactional (JEE7) 注释了我的CDI bean 的方法。我期望的是在保存文件失败时回滚 EJB 的事务,但它没有发生。这是正常行为吗?这种情况下如何回滚EJB的事务?

@Named
@javax.faces.view.ViewScoped
public class LectureCtrl
{
    @Inject LecturesFacade lecturesFacade;
    @Getter @Setter UploadedFile paper;
    @Getter @Setter Lecture lecture;

    @Transactional(rollbackOn={FileNotFoundException.class})
    public void create(Lecture _lecture) throws MessagingException, IOException
    {
        try{
            Long _lectureId = lecturesFacade.create( _lecture );
            //args: file, path, constant filename "abstract"
            WebUtils.handleFileUpload( this.paper, "conferences/"+lecture.getConference().getId()+"/lectures/"+_lectureId, "abstract" );
        }catch(Exception e){
            System.out.println(e);
            throw e;
        }
    }

    public String onSubmitNewLecture(){
        //JSF-specific code
        this.create(this.lecture);
        //JSF-specific code
    }

}


@Stateless
public class LecturesFacade {
    @PersistenceContext EntityManager em;
    @Inject EmailSubsystem emailSubsystem;

    public Long create( Lecture lecture ) throws MessagingException
    {
        em.persist(lecture);
        em.flush();
        em.refresh(lecture);
        Long id = lecture.getId();
        emailSubsystem.sendEmailOnNewLecture(lecture);
        return id;
    }
}

例外:

INFO:   java.io.FileNotFoundException: C:\konferencje\files\conferences\3\lectures\20\abstract (Access is denied)
    at java.io.FileOutputStream.open(Native Method)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:212)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:165)
    at BackingBeans.WebUtils.handleFileUpload(WebUtils.java:154)
    at BackingBeans.LectureCtrl.create(LectureCtrl.java:56)
    ...

WebUtils.handleFileUpload 失败时,我还想回滚lecturesFacade.create 所做的更改。

编辑

我已经用@TransactionAttribute(TransactionAttributeType.MANDATORY) 注释了我的LectureFacade.create 方法,它给了我一个javax.ejb.EJBTransactionRequiredException 所以看起来CDI 事务没有传播到EJB 事务

【问题讨论】:

  • 如何将交易标记为失败?带注释?一些代码可以帮助我们帮助您。
  • 没有,我只是看到抛出了java.io.FileNotFoundException,文件没有保存,但是数据库中的数据已经保存了。
  • 您可以通过解耦创建讲座和上传与讲座相关的文件来简化事情并避免处理此问题。因此工作流程将是 1)创建讲座并将 id 返回给客户端 2)客户端上传文件并将 id 作为参数传递。如果您在侧面 handleFileUpload 中有其他与模型相关的逻辑,这可能会被过度简化,但由于您没有粘贴我不知道的代码。
  • 另外,您的 emailSubsystem 商务逻辑是 TX 的一部分吗?

标签: jakarta-ee jta


【解决方案1】:

问题是您需要定义在哪些检查异常上回滚事务。 @Transactional 注释有一个名为 rollbackOn 的字段来设置它

所以尝试注释@Transactional(rollbackOn={FileNotFoundException.class}) - 我没有尝试过代码,所以可能有错字。之所以需要指定这个是因为 FileNotFoundException 是一个检查异常。

请阅读我链接的文档,因为这是使用容器管理事务时最常见的错误之一。

编辑

这很奇怪。您可以尝试将 IOException 添加到rollbackOn 吗?如果这不能解决问题,我会检查:

  1. 您的数据库是否支持事务? (例如,带有 myisam 的 mysql 不支持事务)
  2. 您的数据库连接是否正确配置为使用事务? (在某些驱动程序上,您可以指定对每个 sql 语句执行自动提交)
  3. 您的容器配置正确吗?不幸的是,您没有添加完整的堆栈跟踪,所以我看不到容器是否确实为 LectureCtrl 创建了事务代理
  4. 您的应用程序中是否有多个数据源?这仅在您正确配置 JTA 时才有效(在标签中提到)

【讨论】:

  • 请看评论——顺便说一句,我喜欢你的个人资料描述“java EE hater”......你不是唯一一个:)。
  • "Sometimes" JEE 真的很烦人 :) IOException 没有帮助,我使用的是 Glassfish4 和 Postgresql9.2,只有一个数据源 - 由 JTA 管理,但请查看我的上次编辑的原始帖子。
【解决方案2】:

我刚刚意识到我正在从同一类的非事务方法(onSubmitNewLecture)调用@Transactional方法(创建),所以@Transactional拦截器将被省略。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-03
    • 2014-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多