保存后如何检索实体关系?

时间:2013-01-29 11:14:03

标签: java rest jpa spring-data-jpa hypermedia

我正在开发一个RESTful webservice,其spring-data作为其数据访问层,由JPA / Hibernate支持。 在域实体之间建立关系是很常见的。例如,假设一个实体Product 拥有 一个Category实体。

现在,当客户端POST表示JAX-RS方法的Product表示时。该方法使用@Transactional进行注释,以在事务中包装每个存储库操作。当然,客户端只发送已存在的id的{​​{1}},而不是整个表示,只发送一个引用(外键)。

在那种方法中,如果我这样做:

Category

变量entity = repository.save(entity); 现在只有entity字段集Category。这并没有让我感到惊讶。我没想到要保存(SQL插入)来检索相关对象的信息。但我需要整个id对象和相关实体才能返回给用户。

然后我这样做了:

Product

即,在持久化后检索对象,在同一个事务/会话中

令我惊讶的是,变量entity = repository.save(entity); entity = repository.findOne(entity.getId()); 没有改变任何东西。实际上,数据库甚至没有获得单个选择查询。 这与Hibernate的缓存有关。出于某种原因,当在同一事务中时,如果该对象先前已被持久化,则查找不会检索整个对象图。

使用Hibernate,解决方案似乎是使用entity(请参阅thisthis)。有道理。

但是如何通过弹簧数据实现这一目标?

我想避免创建重复的自定义存储库。我认为这个功能应该是Spring数据的一部分(有些人已经在Spring数据论坛中报告了这一点:thread1thread2)。

1 个答案:

答案 0 :(得分:1)

<强> TL;博士

Web层中的实体之间的引用需要使用链接显式化,不应隐藏在半填充对象实例后面。持久层中的引用由对象引用表示。所以应该有一个专门的步骤将一个(链接)转换为另一个(完全填充的对象引用)。​​

<强>详情

这样一种反模式可以绕过后端ID,并假设编组绑定做正确的事情。因此,客户端应该使用链接并将其传递给服务器,以指示他们想要在已存在的资源和即将创建的资源之间建立连接。

假设您通过Category展示了现有的/categories/4711,则可以发布到您的服务器:

POST /products
{ links : [ { rel : "category", href : "/categories/4711" } ],
  // further product data
}

服务器将实例化一个新的Product实例,用其他数据填充它,并最终按如下方式填充关联:

  1. 通过查找链接关系类型(例如此处的category属性)来确定要填充的属性。
  2. 从给定的URI
  3. 中提取后端标识符
  4. 使用相应的存储库查找相关的实体实例
  5. 在根实体上设置
  6. 所以在你的例子中沸腾为:

    Product product = new Product();
    // populate primitive properties
    product.setCategory(categoryRepository.findOne(4711));
    productRepository.save(product);
    

    只需将类似内容发布到服务器:

    POST /products
    { category : {
        id : 1, … },
      … 
    }
    
    由于很多原因,

    不是最理想的:

    1. 您希望持久性提供程序隐式持久保存Product实例,同时“识别”所引用的Category实例(实际上只包含一个id)并不意味着要保留但更新了现有Category的数据?我认为这有点神奇。
    2. 您基本上将用于POST的数据结构强加给服务器到持久层,期望它透明地处理您决定执行POST的方式。这不是持久层的责任,而是Web层的责任。 Web层的整个目的是使用表示和后端服务的链接来缓解基于HTTP的协议的特性。