我正在开发一个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
(请参阅this和this)。有道理。
但是如何通过弹簧数据实现这一目标?
我想避免创建重复的自定义存储库。我认为这个功能应该是Spring数据的一部分(有些人已经在Spring数据论坛中报告了这一点:thread1,thread2)。
答案 0 :(得分:1)
<强> TL;博士强>
Web层中的实体之间的引用需要使用链接显式化,不应隐藏在半填充对象实例后面。持久层中的引用由对象引用表示。所以应该有一个专门的步骤将一个(链接)转换为另一个(完全填充的对象引用)。
<强>详情
这样一种反模式可以绕过后端ID,并假设编组绑定做正确的事情。因此,客户端应该使用链接并将其传递给服务器,以指示他们想要在已存在的资源和即将创建的资源之间建立连接。
假设您通过Category
展示了现有的/categories/4711
,则可以发布到您的服务器:
POST /products
{ links : [ { rel : "category", href : "/categories/4711" } ],
// further product data
}
服务器将实例化一个新的Product
实例,用其他数据填充它,并最终按如下方式填充关联:
category
属性)来确定要填充的属性。所以在你的例子中沸腾为:
Product product = new Product();
// populate primitive properties
product.setCategory(categoryRepository.findOne(4711));
productRepository.save(product);
只需将类似内容发布到服务器:
POST /products
{ category : {
id : 1, … },
…
}
由于很多原因,不是最理想的:
Product
实例,同时“识别”所引用的Category
实例(实际上只包含一个id)并不意味着要保留但更新了现有Category
的数据?我认为这有点神奇。