Spring Data JPA-针对不同查询的不同联接

约翰尼·洛克特芬格斯

使用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-通过添加@OneToManyinCourse@JsonBackReference在第二@ManyToOnein中添加a-我的结果PerformanceReport将不会得到任何Student数据。这也将防止Course数据流向Student查询。如果删除@JsonBackReference注释,则会出现无限递归和StackOverflow错误。

我尝试创建单独的实体来解决这些情况。我从中删除了该联接Student,并将其放在extends类中Student然后,我对Course执行相同的操作PerformanceReport这不仅导致新的错误,而且非常混乱。它还需要我创建单独的存储库来处理这些扩展类。

肯定有更好的办法。

我能正确处理吗?Spring Data JPA是完成此类任务的最佳方法吗?如果我要查询StudentCourse根本不使用任何联接怎么办?

当然,对于每种可能的情况我都不需要新的实体。如何为不同的查询自定义联接表的方式?

皮尔霍

您的问题与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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章