如何使用JAX-RS,Spring和JPA管理事务

时间:2012-03-07 12:50:22

标签: spring jpa transactions jax-rs

我正在使用JAX-RS提供基于HTTP的界面来管理数据模型。数据模型存储在数据库中,并通过JPA进行交互。

这允许我修改数据模型的接口以适应REST客户端,并且大多数似乎工作得很好。但是,我不确定如何处理由JAX-RS资源提供的方法需要事务的场景,这会影响JPA get,update,commit-on-tx-end模式,因为只有事务包装get操作,因此永远不会提交更新。如果单个REST操作需要多个JPA操作,我可以看到同样的问题。

由于我正在使用Spring的事务支持,显而易见的事情是将@Transactional应用于JAX-RS资源中的这些方法。但是,为了实现这一点,Spring需要管理JAX-RS资源的生命周期,而我所知道的使用示例是在需要时通过“new”创建资源,这让我有点紧张。

我可以想到以下解决方案:

  1. 更新我的JPA方法,以原子方式从我的REST接口提供我想要做的所有事情的事务管理版本。应该工作,将事务保持在JAX-RS层之外,但是阻止了get,update,commit-on-tx-end模式,这意味着我需要创建一个非常精细的JPA接口。
  2. 注入资源对象;但它们通常是有状态的,至少持有与
  3. 交互的对象的ID
  4. 抛弃资源层次结构,在根目录下注入大的无状态超级资源,从而根据该根管理整个层次结构;没有凝聚力,大服务
  5. 具有注入的,无状态的,支持事务的帮助程序对象的层次结构,它们“遮蔽”实际资源;资源被实例化并保持状态,但是将方法调用委托给辅助对象
  6. 有人有什么建议吗?我很可能错过了某个关键点。


    更新 - 为了解决get,update,commit-on-tx-close流程周围缺少事务的问题,我可以公开EntityManager merge(object)方法并手动调用它。虽然不整洁,但并没有解决更大的问题。


    更新2 @skaffman 代码示例: 在JPA服务层,注入,注释工作

    public class MyEntityJPAService {
    ...
    @Transactional(readOnly=true) // do in transaction
    public MyEntity getMyEntity(final String id) {
        return em.find(MyEntity.class, id);
    }
    

    在JAX-RS资源中,由new创建,无交易

    public class MyEntityResource {
    ...
    private MyEntityJPAService jpa;
    ...
    @Transactional // not injected so not effective
    public void updateMyEntity(final String id, final MyEntityRepresentation rep) {
        MyEntity entity = jpa.getMyEntity(id);
        MyEntity.setSomeField(rep.getSomeField());
        // no transaction commit, change not saved...
    }
    

1 个答案:

答案 0 :(得分:4)

我有一些建议

  1. 在JPA和JAX-RS图层之间引入一个图层。该层由Spring管理的@Transactional bean组成,并将从其组件JPA调用组成各种业务级操作。这有点类似于你的(1),但保持JPA层简单。

  2. 用Spring-MVC取代JAX-RS,它提供相同(或类似)的功能,包括@PathVariable@ResponseBody等。

  3. 使用TransactionProxyFactorybean以编程方式将JAX-RS对象包装在事务代理中。这会删除您的@Transactional注释并生成一个尊重它们的代理。

  4. 使用@Configurable和AspectJ LTW即使您使用`new创建对象,也允许Spring尊重@Transactional。请参阅8.8.1 Using AspectJ to dependency inject domain objects with Spring