【问题标题】:How to retrieve entity relationships after save?保存后如何检索实体关系?
【发布时间】:2013-01-12 22:42:17
【问题描述】:

我正在开发一个以spring-data 作为其数据访问层的 RESTful Web 服务,由 JPA/Hibernate 提供支持。 域实体之间存在关系是很常见的。例如,想象一个实体Product一个Category实体。

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

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

entity = repository.save(entity);

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

然后我这样做了:

entity = repository.save(entity);
entity = repository.findOne(entity.getId());

也就是说,在持久化对象后检索对象,在同一事务/会话中

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

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

但是如何使用 spring 数据实现这一点?

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

【问题讨论】:

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


    【解决方案1】:

    tl;dr

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

    详情

    这样传递后端 id 并假设编组绑定做正确的事情是一种反模式。因此,客户端应该使用链接并将其交给服务器,以表明他们希望在已经存在的资源和即将创建的资源之间建立连接。

    因此,假设您有通过 /categories/4711 公开的现有 Category,您可以发布到您的服务器:

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

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

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

    所以在你的例子中归结为:

    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 的协议的特性。

    【讨论】:

    • 很好的答案!我们不是已经在单个资源 url 中传递了后端 id,例如/categories/4711
    • 另外,我们需要解析链接的Category URL 并获取它的id。这应该由一些 HATEOAS 帮助程序或库提供。不知道我怎样才能优雅地做到这一点。 HATEOAS 似乎比我想象的要广泛。 :)
    • 我使用 /categories/4711 作为可读示例,但 URI 可能是 /fe6325a27,这使得我猜需要额外的映射步骤更加明显。在Spring HATEOAS 中,我们已经有API 来构建指向控制器方法和JAX-RS 资源的Link 实例。不幸的是,您是唯一知道在 URI 中何处找到后端 id 的人(例如 /categories/4711 VS。/categories?id=4711。甚至可能需要一个完整的映射步骤,如上所示。因此很难为那个。
    • 我看到你是 spring-hateoas 的贡献者。你能告诉我它对 JAX-RS 的支持如何,特别是关于 ResourceAssemblers 吗? JAX-RS 有可用的示例吗?当 JaxRsLinkBuilder 尝试执行 new JaxRsLinkBuilder(ServletUriComponentsBuilder.fromCurrentServletMapping()) 时,我遇到了一些问题。我得到Could not find current request via RequestContextHolder。我过去曾尝试使用此库。它似乎很好地解决了 HATEOAS,但从未设法让它与 JAX-RS 一起使用。刚刚找到带有常规 Spring 控制器的示例存储库
    • 我没有使用最新的 spring-hateoas。我没有使用 EntityLinks。是否可以在 xml 中使用@EnableEntityLinks?我对如何在 Spring+JAX-RS 应用程序中使用编程配置有点迷茫。我应该继承/实现哪个“WebInitializer”?也许我应该创建一个新的 SO 问题。
    猜你喜欢
    • 2021-11-11
    • 1970-01-01
    • 2011-01-18
    • 2017-05-05
    • 2015-06-04
    • 2021-06-04
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多