Java 使用Spring数据REST处理子实体集合中的资源链接
我正在评估SpringDataREST作为基于AngularJS的应用程序的后端。我很快将我们的领域建模为一组聚合根,并遇到了以下设计障碍:Java 使用Spring数据REST处理子实体集合中的资源链接,java,spring-data-rest,hateoas,spring-hateoas,Java,Spring Data Rest,Hateoas,Spring Hateoas,我正在评估SpringDataREST作为基于AngularJS的应用程序的后端。我很快将我们的领域建模为一组聚合根,并遇到了以下设计障碍: 模型资源 多任务实体 引用多属性资源 我期望属性的HAL_链接放置在每个TaskJSON对象中,但遗憾的是,这些属性仅作为JSON构造根的链接可见 我明白了: { “版本”:0, “名称”:“myModel”, “任务”:[ { “名称”:“任务1” }, { “名称”:“任务2” } ], “_链接”:{ “自我”:{ “href”:”http://
- 模型资源
- 多任务实体
- 引用多属性资源
{
“版本”:0,
“名称”:“myModel”,
“任务”:[
{
“名称”:“任务1”
},
{
“名称”:“任务2”
}
],
“_链接”:{
“自我”:{
“href”:”http://localhost:8080/models/1"
},
“属性”:{
“href”:”http://localhost:8080/models/1/attributes"
}
}
}
而不是像我想象的那样:
{
“版本”:0,
“名称”:“myModel”,
“任务”:[
{
“名称”:“任务1”,
“_链接”:{
“属性”:{
“href”:”http://localhost:8080/models/1/tasks/1/attributes"
}
}
},
{
“名称”:“任务2”,
“_链接”:{
“属性”:{
“href”:”http://localhost:8080/models/1/tasks/2/attributes"
}
}
],
“_链接”:{
“自我”:{
“href”:”http://localhost:8080/models/1"
},
“属性”:{
“href”:”http://localhost:8080/models/1/attributes"
}
}
}
顺便提一下,在第一个示例中,属性链接以404结尾
我在HAL规范和Spring Data REST文档中都没有看到处理这种情况的任何内容。显然,我可以将任务定义为解决问题的资源,但是我的模型不需要这样做。我觉得这是一个合法的用例
我创建了一个简单的Spring Boot应用程序,它再现了这个问题。模型:
@实体
公共类模型{
@Id@GeneratedValue公共长Id;
@版本公共长版本;
公共字符串名称;
@OneToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE})
公开列出任务;
}
@实体
公开课任务{
@Id@GeneratedValue公共长Id;
公共字符串名称;
@许多
公共集合属性;
}
@实体
公共类属性{
@Id@GeneratedValue公共长Id;
@版本公共长版本;
公共字符串名称;
}
和存储库:
@RepositoryRestResource
公共接口模型存储库扩展了分页和排序存储库{
}
@存储资源
公共接口属性存储库扩展了分页和排序存储库{
}
在这里,我可能错过了一些东西,因为这似乎是一个非常简单的用例,但找不到任何有类似问题的人。此外,这可能是我的模型中的一个基本缺陷,如果是这样,我准备听听你的论点:-)你没有任务存储库-在spring data rest中,如果你没有我认为如果一个任务只包含一个属性,那么你会得到一个链接,但是你有一个集合,所以对属性的访问将是任务资源的一个子资源 因此,您的方案不起作用。我将尝试导出TaskRepository并删除属性repository 然后,模型资源将包含指向其任务的链接,任务资源将嵌入属性
如果您仍然希望将任务内联到您的模型资源中,您可以使用投影。因为Spring Data REST不以本机方式处理问题中描述的用例,所以第一步是停用任务属性的管理,并确保它们在默认情况下不会序列化ce(exported=false)确保不会为“attributes”rel自动生成(非工作)链接,
@JsonIgnore
确保默认情况下不会呈现属性
@实体
公开课任务{
@身份证
@生成值
公共长id;
公共字符串名称;
@许多
@RestResource(导出=假)
@杰索尼奥雷
公共列表属性;
}
接下来,\u links
属性仅在资源的根目录下可用,因此我选择实现一个名为“taskAttributes”的新rel,将有多个值,每个任务一个。为了将这些链接添加到资源,我构建了一个自定义ResourceProcessor
,为了实现实际端点,还构建了一个自定义ModelController
:
@组件
公共类ModelResourceProcessor实现ResourceProcessor{
@凌驾
公共资源流程(资源模型资源){
Model Model=modelResource.getContent();
对于(int i=0;i=model.tasks.size()){
返回新的ResponseEntity(未找到HttpStatus.NOT_);
}
列表属性=model.tasks.get(taskIndex.attributes);
列表资源=attributes.stream()
.map(t->assembler.toResource(t))
.collect(toList());
{
"name": "myModel",
"tasks": [
{
"name": "task1"
},
{
"name": "task2"
}
],
"_links": {
"self": {
"href": "http://localhost:8080/models/1"
},
"model": {
"href": "http://localhost:8080/models/1{?projection}",
"templated": true
},
"taskAttributes": [
{
"href": "http://localhost:8080/models/1/task/0/attributes"
},
{
"href": "http://localhost:8080/models/1/task/1/attributes"
}
]
}
}
{
"name": "myModel",
"tasks": [
{
"name": "task1",
"attributes": [
{
"name": "attrForTask1",
"_links": {
"self": {
"href": "http://localhost:8080/attributes/1{?projection}",
"templated": true
}
}
}
]
},
{
"name": "task2",
"attributes": [
{
"name": "attrForTask2",
"_links": {
"self": {
"href": "http://localhost:8080/attributes/2{?projection}",
"templated": true
}
}
},
{
"name": "anotherAttrForTask2",
"_links": {
"self": {
"href": "http://localhost:8080/attributes/3{?projection}",
"templated": true
}
}
},
...
]
}
],
"_links": {
"self": {
"href": "http://localhost:8080/models/1"
},
"model": {
"href": "http://localhost:8080/models/1{?projection}",
"templated": true
}
}
}