我面对一个微笑的场景。让我们拥有经典的多对多关系:
public class Student {
private Long id;
private String name;
private List<StudentCourse> studentCourses = new ArrayList<>();
}
和
public class Course {
private Long id;
private String name;
private List<StudentCourse> studentCourses = new ArrayList<>();
}
课程订阅由以下方式管理:
public class StudentCourse {
private Long id;
private Student student;
private Course course;
}
通过API检索的Student
实体的表示如下所示:
{
"name": "Peter",
"_links": {
"self": {
"href": "myapiroot/api/students/1"
},
"studentCourses": {
"href": "myapiroot/api/students/1/studentCourses"
}
}
}
Course
实体的表示如下所示:
{
"name": "Engineering",
"_links": {
"self": {
"href": "myapiroot/api/courses/1"
},
"studentCourses": {
"href": "myapiroot/api/courses/1/studentCourses"
}
}
}
从API检索的StudentCourse
实体如下所示:
{
"_links": {
"self": {
"href": "myapiroot/api/studentCourses/1"
},
"student": {
"href": "myapiroot/api/studentCourses/1/student"
},
"course": {
"href": "myapiroot/api/studentCourses/1/course"
},
}
}
如果我已经有一个内存课程(带有self
URI),我希望能够从StudentCourse
表示确定课程,而无需导航到course
URI。我希望能够将course
表示中的StudentCourse
URI视为数据库中的外键 - 唯一标识符。由于每个表示都有self
URI,我希望该唯一标识符为。
我认为无法比较来自不同来源的两个实体之间的相等性。 StudentCourse
表示中学生和课程的URI 不等于self
和Student
表示中的Course
链接。
由于默认情况下不导出ID,如何有效确定从API中的不同路由获取的相同类型的两个不同实例是否相等?
我有一个来自Student
的{{1}}实体。
我有一个来自myapiroot/api/students/1
的{{1}}实体。
我想知道该学生是否订阅了该课程。为此,我必须执行以下步骤:
Course
以获取所有学生的课程订阅。myapiroot/api/courses/1
的每个学生的课程订阅提出额外请求以检索课程实例。myapiroot/api/students/1/studentCourses
URI进行比较。第二步非常低效,如果myapiroot/studentCourses/{id}/course
表示中的课程URI等于我已经知道的课程实体的self
链接,则可以完全避免。
我尝试通过实现这样的自定义StudentCourse
来自定义self
返回的资源:
StudentCourseRepository
现在,REST存储库返回的StudentCourseResourceProcessor
资源如下所示:
public class StudentCourseResourceProcessor implements ResourceProcessor<Resource<StudentCourse>> {
@Autowired
private EntityLinks entityLinks;
@Override
public Resource<StudentCourse> process(Resource<StudentCourse> resource) {
Student student = resource.getContent().getStudent();
Course course = resource.getContent().getCourse();
resource.add(entityLinks.linkToSingleResource(Student.class, student.getId()).withRel("student"));
resource.add(entityLinks.linkToSingleResource(Course.class, course.getId()).withRel("course"));
return resource;
}
}
StudentCourse
和{
"_links": {
"self": {
"href": "myapiroot/api/studentCourses/1"
},
"student": [
{"href": "myapiroot/api/students/1"},
{"href": "myapiroot/api/studentCourses/1/student"}
],
"course": [
{"href": "myapiroot/api/courses/1"},
{"href": "myapiroot/api/studentCourses/1/course"
],
}
}
条目已从对象更改为对象数组。这使得项目的表示不规则。我试图通过调用student
删除资源处理器中的旧链接,但这不起作用。就目前而言,无法删除自动生成的URI。
我已经深入研究了文档并尝试使用projections在course
实体表示中嵌入关系数据:
resource.removeLinks()
现在我可以向StudentCourse
发出GET请求。
@Projection(name="studentCourseDetails", types={StudentCourse.class})
public interface StudentCourseDetails {
Course getCourse();
}
表示现在看起来像这样:
myapiRoot/students/1/studentCourses?projection=studentCourseDetails
存在StudentCourse
信息,但课程{
"course": {
"name": "Engineering"
},
"_links": {
"self": {
"href": "myapiroot/api/studentCourses/1"
},
"student": {
"href": "myapiroot/api/studentCourses/1/student"
},
"course": {
"href": "myapiroot/api/studentCourses/1/course"
},
}
}
缺失。此外,我的course
设置已被忽略,只有_link
&amp;的关系链接。 ResourceProcessor
可用。
我还了解到完全替换这些关系URI是一个坏主意,因为您可以对它们执行操作。例如,HTTP course
到student
,正文为
PUT
会更改myapiroot/studentCourses/1/course
实体的{ "href": "myapiroot/courses/2" }
关联。所以这些关系URI确实有用!
course
URI添加到相对URI。这是一种可接受的处理方式吗?这有什么含义?答案 0 :(得分:2)
你有一个课程,你有一个学生。要了解学生是否注册了该课程,您只需查询API即可。您可以通过以下两种方式之一完成此操作:在StudentCoursesRepository中创建显式查询方法,或者让此存储库扩展QueryDslPredicateExecutor。
采用后一种方法非常强大,因为它允许您通过任意标准的任意组合查询您的存储库。
public interface StudentCoursesRepository extends JpaRepository<StudentCourse, Long>,
QueryDslPredicateExecutor<StudentCuurse>{
}
有了这个,您现在可以查询下面的API,它将返回零或1条记录:
http://localhost/myapiroot/api/studentCourses?
student=http://localhost/myapiroot/api/students/1
&course=http://localhost/myapiroot/api/courses/1
或者,如果您已经公开了ID字段,而是愿意处理这些:
http://localhost/myapiroot/api/studentCourses?student.id=1&course.id=1
答案 1 :(得分:1)
根本问题在于你没有宣称你所寻求的是什么。
我认为没有办法比较来自不同来源的两个实体之间的相等性。
即使/ api / courses / 1和/ api / student / 2 / courses / 3都指向相同的课程,这两个URI代表不同的东西。第一个是对课程的规范性引用,没有语义覆盖,而第二个代表与某个学生相关的特定课程。
这类似于获取COURSES表的PRIMARY KEY并查找一行数据,然后将其与STUDENTS_COURSES表中的FOREIGN KEY进行比较,并想知道“为什么这两个值不相同?”
可能缺少的另一个金块是你似乎已经为所有三个表定义了聚合根,尽管你可能只需要两个最多,一个用于学生,一个用于课程。
StudentCourse表示中学生和课程的URI不等于学生和课程表示中的自我链接。
不是不是,因为这实际上是多对多表中的FOREIGN KEY,将它们链接在一起。在您导航到/ api / students或/ api / courses之前,您可以收集“自我”链接并对该资源进行绝对引用。
我想知道该学生是否订阅了该课程
现在我们到了某个地方。这是你要解决的问题。乍一看,你的域模型似乎并没有描绘出多对多的关系。这是JPA吗?如果是这样,这样的事情可能会更好地暗示如何做到这一点:http://uaihebert.com/jpa-manytomany-unidirectional-and-bidirectional/
通过正确的建模,您可以编写导航属性的查询。最终,你应该能够带一个学生并写一个发现者来获得一个课程列表,反之亦然。从那里开始,你谈论的是检测链接的存在。
基本上,一旦你通过JPA @ManyToMany关系让学生和课程互相指向,你应该能够写
interface StudentRepository extends CrudRepository<Student,Long> {
List<Student> findByCoursesName(@Param("q") String name);
}
和
interface CourseRepository extends CrudRepository<Course, Long>{
List<Student> findByStudentsName(@Param("q") String name);
}
查看此套牌了解更多详情:https://speakerdeck.com/olivergierke/ddd-and-rest-domain-driven-apis-for-the-web-4