在JAX-RS中具有复合主键的JPA实体的正确REST URI是什么?

时间:2016-07-09 18:32:47

标签: java rest http netbeans jax-rs

我有以下设置。

使用的技术:

  • NetBeans IDE
  • MySQL DB
  • EclipseLink JPA
  • JAX-RS

我使用NetBeans工具“从数据库中获取RESTful Web服务来生成我的实体和休息服务。现在我在我的数据库中有一个视图,其中一行由两列组成的compits唯一标识(我知道它没有意义说到关于视图的主键,但是为了简单起见,让我们把它称为主键。我将视图建模为具有嵌入式复合主键的实体,如下所示:

@Entity
@Table(name = "UserAmountView")
@XmlRootElement
public class UserAmountView implements Serializable {

  private static final long serialVersionUID = 1L;
    @EmbeddedId
  protected UserAmountPK userAmountPK;

  // fields, constructors, getters, setters etc.

}

@Embeddable
public class UserAmountPK implements Serializable {

  @Basic(optional = false)
  @NotNull
  @Column(name = "UserID")
  private int userID;
  @Basic(optional = false)
  @NotNull
  @Column(name = "BalanceID")
  private Integer balanceID;

  public UserAmountPK() {
  }

  public UserAmountPK(int userID, Integer balanceID) {
    this.userID = userID;
    this.balanceID = balanceID;
  }

  // getters and setters etc...
}

现在我的问题是,如何最好地RESTful地解决这样一个实体的实例?对于具有复合主键的普通表,NetBeans使用矩阵参数(复合键的每个部分/列一个)生成一个过程,因此我将此方法用于我的视图。我希望,我可以在 ... / wgm.rest.useramountview; userID = 7; balanceID = 3 下获取由主键(7,3)组成的实体,但当然这不是因为矩阵参数与wgm.rest.useramountview在同一路径段中,所以请求将返回所有现有实体。接下来我尝试了 ... / wgm.rest.useramountview /; userID = 7; balanceID = 3 这给了我相同的结果(为什么?)。只有当我在'/'和';'之间插入一些内容时,它才会按预期工作并返回由(7,3)标识的实体,例如的 ... / wgm.rest.useramountview /富;用户ID = 7; balanceID = 3 即可。 显然,我想避免总是在URL中插入'foo'。有哪些替代方案?

我的服务类目前看起来如下:

@Stateless
@Path("wgm.rest.useramountview")
public class UserAmountViewFacadeREST extends AbstractFacade<UserAmountView> {

  @PersistenceContext(unitName = "WGManagerPU")
  private EntityManager em;

  private UserAmountPK getPrimaryKey(PathSegment pathSegment) {
    wgm.rest.UserAmountPK key = new wgm.rest.UserAmountPK();
    javax.ws.rs.core.MultivaluedMap<String, String> map = pathSegment.getMatrixParameters();
    java.util.List<String> userID = map.get("userID");
    if (userID != null && !userID.isEmpty()) {
      key.setUserID(new java.lang.Integer(userID.get(0)));
    }
    java.util.List<String> balanceID = map.get("balanceID");
    if (balanceID != null && !balanceID.isEmpty()) {
      key.setBalanceID(new java.lang.Integer(balanceID.get(0)));
    }
    return key;
  }

  @GET
  @Path("{id}")
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  public UserAmountView find(@PathParam("id") PathSegment id) {
    wgm.rest.UserAmountPK key = getPrimaryKey(id);
    return super.find(key);
  }

  @GET
  @Override
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  public List<UserAmountView> findAll() {
     return super.findAll();
  }

  // ...
}

更新

另一种可能性是将复合键的单列作为路径参数包括在内,如下所示:

  @GET
  @Path("{userID}/{balanceID}")
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  public UserAmountView find(
      @PathParam("userID") String userID,
      @PathParam("balanceID") String balanceID) {

    wgm.rest.UserAmountPK key = 
        new UserAmountPK(new Integer(userID), new Integer(balanceID));
    return super.find(key);
  }

但是,这仍然不能让我满意,因为那看起来像是一个子资源。实际上,两个路径参数都在同一层级上,用于识别一个资源。

解决方案:

我终于提出了罗曼沃特纳答案的改编版本(见下文):直接在基础资源路径上使用矩阵参数,并直接在方法内区分这两种情况。我必须将结果包装在Response对象中,因为返回类型不匹配(UserAmountView findList<UserAmountView> findAll匹配。结果如下:

@Stateless
@Path("wgm.rest.useramountview")
public class UserAmountViewFacadeREST extends AbstractFacade<UserAmountView> {

  @PersistenceContext(unitName = "WGManagerPU")
  private EntityManager em;
  @GET
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  public Response findAll(
      @MatrixParam("userID") String userID,
      @MatrixParam("balanceID") String balanceID) {
     if (userID != null && balanceID != null) {
       wgm.rest.UserAmountPK key = new UserAmountPK(Parser.asIntOrNull(userID), Parser.asIntOrNull(
      balanceID));
       return Response.ok(super.find(key)).build();
    } else {
      return Response.ok(super.findAll()).build();
    }
  }
}

2 个答案:

答案 0 :(得分:1)

您将复合键作为矩阵参数.../wgm.rest.useramountview;userID=7;balanceID=3传递,但您已将定义的路径结构设置为.../wgm.rest.useramountview.../wgm.rest.useramountview/{id},其中id也定义为路径参数。

由于矩阵参数属于它们所定义的资源,JAX-RS将使用类似findAll的URI调用最接近的可能资源.../wgm.rest.useramountview;userID=7;balanceID=3方法,尽管此处您不提取矩阵参数。这就是为什么返回所有条目而不是调用该URI的特定条目的原因。

第二次调用.../wgm.rest.useramountview/foo;userID=7;balanceID=3成功,因为您现在向服务提供ID foo;userID=7;balanceID=3,JAX-RS现在将解析为find(...)方法。在此处使用PathSegment对象时,您只需忽略实际的ID值,而只是收集在其上定义的矩阵参数。

要处理矩阵参数而不必使用其他一些当前未使用的ID路径参数,您应该将findfindAll方法重构为:

@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public UserAmountView find(@MatrixParam("userID") Integer userID,
                           @MatrixParam("balanceID") Integer balanceID) {
    if (null != userID && null != balanceID) {
        return findOne(userID, balanceID);
    } else {
        return findAll();
    }
} 

PathParamQueryParam一样,您也可以直接注入MatrixParam,如上例所示。你也可以直接在PathSegment方法上重复使用findAll并像你一样检索矩阵参数,但我想使用@MatrixParam注释会更直观一些。

答案 1 :(得分:0)

我认为这是 MatrixParam,PathSegment 值的副作用,第一个标记实际上是一个正常的路径参数,后跟; key1 = val1; key2 = val2 值。

您是否考虑过使用普通的&amp; key1 = val1&amp; key2 = val2 查询参数?