使用Java 11,Spring Boot和Spring Data JPA
我在MySQL数据库中有3个联接表,我想使用Spring Data JPA访问这些表。为了简单起见,我们将它们称为“学生”,“课程”和“ performance_report”。
这是我的数据类:
@Entity
@Table(name = "student")
@Data
public class Student {
@Id
@Column(name = "student_id")
private Long studentId;
@Column(name = "student_name")
private String studentName;
@OneToMany(mappedBy = "student", fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
private List<PerformanceReport> performanceReports;
}
@Entity
@Table(name = "course")
@Data
public class Course {
@Id
@Column(name = "course_id")
private Long courseId;
@Column(name = "course_name")
private String courseName;
}
@Entity
@Table(name = "performance_report")
@Data
public class PerformanceReport {
@Id
@Column(name = "performance_report_id")
private Long performanceReportId;
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "student_id", nullable = false)
// JsonBackReference needed to prevent infinite recursion.
@JsonBackReference
private Student student;
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "course_id", nullable = false)
private Course course;
@Column(name = "grade")
private String grade;
@Column(name = "attendance")
private String attendance;
}
这是我的学生资料库:
public interface StudentRepository extends JpaRepository<Student, Long> {
Optional<Student> findById(Long studentId);
}
调用StudentRepository.findById会生成如下对象:
{
"studentId": 1,
"studentName": "Spongebob Squarepants",
"performanceReports": [
{
"performanceReportId": 5473,
"course": {
"courseId": 643,
"courseName": "Boating 101"
},
"grade": "F",
"attendance": "100%"
},
{
"performanceReportId": 4723,
"course": {
"courseId": 346,
"courseName": "Grilling 101"
},
"grade": "A+",
"attendance": "100%"
}
]
}
我还想执行此操作的逆操作,因此我可以查询Course并获取如下对象:
{
"courseId": 346,
"courseName": "Grilling 101",
"performanceReports": [
{
"performanceReportId": 4723,
"student": {
"studentId": 1,
"studentName": "Spongebob Squarepants"
},
"grade": "A+",
"attendance": "100%"
},
{
"performanceReportId": 4774,
"student": {
"studentId": 4,
"studentName": "Squidward Tentacles"
},
"grade": "C-",
"attendance": "72%"
}
]
}
我目前的实体结构无法做到这一点。
如果我Course
以与我相同的方式设置联接Student
-通过添加@OneToMany
inCourse
并@JsonBackReference
在第二@ManyToOne
in中添加a-我的结果PerformanceReport
将不会得到任何Student
数据。这也将防止Course
数据流向Student
查询。如果删除@JsonBackReference
注释,则会出现无限递归和StackOverflow错误。
我尝试创建单独的实体来解决这些情况。我从中删除了该联接Student
,并将其放在extends类中Student
。然后,我对Course
和执行相同的操作PerformanceReport
。这不仅导致新的错误,而且非常混乱。它还需要我创建单独的存储库来处理这些扩展类。
肯定有更好的办法。
我能正确处理吗?Spring Data JPA是完成此类任务的最佳方法吗?如果我要查询Student
或Course
根本不使用任何联接怎么办?
当然,对于每种可能的情况我都不需要新的实体。如何为不同的查询自定义联接表的方式?
您的问题与JPA无关,可以用更紧凑的方式表示。我介绍和说明一个解决方案,使用-而不是@JsonBackrefence
-一个@JsonView
注解。
假设我们有两个类:
家长:
@Getter @Setter
public class Parent {
public static class ManagedReferenceView {};
// Just to have something to show
private String name = "" + hashCode();
@JsonView(ManagedReferenceView.class)
private List<Child> children;
}
小孩:
@Getter @Setter
@RequiredArgsConstructor
public class Child {
public static class BackReferenceView {
};
// Just to have something to show
private String name = "" + hashCode();
@JsonView(BackReferenceView.class)
private final Parent parent;
}
如您所见,视图类不需要代码,它们只是要使用的“名称”。关键是要告诉我们什么时候要序列化。这就是它们的使用方式(例如):
构造一些父母:
Parent p = new Parent();
p.setChildren(Arrays.asList(new Child(p), new Child(p)));
序列化Parent
:
objectMapper.writerWithView(Parent.ManagedReferenceView.class).writeValueAsString(p);
序列化Child
:
objectMapper.writerWithView(Child.BackReferenceView.class).writeValueAsString(p);
对于问题的JPA优化部分,您可以添加fetch = FetchType.LAZY
到联接中,这可以让Hibernate决定从db获取引用是否明智。在最佳情况下,直到objectMapper
序列化时需要调用getter以供参考时,才会获取引用。也许您还需要一个不会触发吸气剂的视图。
您也可以实现JPA预测等,但这是另一回事了。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句