spring Sping Boot Jpa jakrant无法获取ManyToMany数据

ztyzrc3y  于 2024-01-05  发布在  Spring
关注(0)|答案(3)|浏览(174)

我有一个问题,我试图隔离,我仍然不明白什么是错的。我有2个类(实体)->教师和学生和表的多对多关系(学生教师关系)与@EmbeddableId。
问题是,当我从数据库中检索教师的集合时,它不会检索与该教师链接的学生数据(多对多关系)。回到javax.persistence,它可以工作,但现在我使用jakax.persistence,它不能以这种方式工作,我想知道为什么。
至于依赖关系,我有Spring Web,Spring Data JPA,Lombok和PostgreSQL Driver
代码:

  1. @Embeddable
  2. @Data
  3. public class StudentTeacherKey implements Serializable {
  4. @Column(name = "teacher_id")
  5. private UUID teacher_id;
  6. @Column(name = "student_id")
  7. private Long student_id;
  8. }

x

  1. @Entity
  2. @Table(name = "STUDENT_TEACHER")
  3. @Data
  4. public class StudentTeacherRelation {
  5. @JsonIgnore
  6. @EmbeddedId
  7. private StudentTeacherKey id;
  8. @ManyToOne(fetch = FetchType.EAGER)
  9. @MapsId("student_id")
  10. @JoinColumn(name = "student_id")
  11. private Student student;
  12. @JsonIgnore
  13. @ManyToOne(fetch = FetchType.EAGER)
  14. @MapsId("teacher_id")
  15. @JoinColumn(name = "teacher_id")
  16. private Teacher teacher;
  17. @Column(name = "extra_column")
  18. private String extraColumn;
  19. }
  1. @Data
  2. @Entity
  3. @Table(name = "TEACHERS")
  4. public class Teacher {
  5. @Id
  6. @Column(name = "teacher_id")
  7. @GeneratedValue(strategy = GenerationType.UUID)
  8. private UUID teacher_id;
  9. @Column(name = "name")
  10. private String name;
  11. @OneToMany(mappedBy = "teacher", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
  12. private Set<StudentTeacherRelation> students = new HashSet<>();
  13. }
  1. @Data
  2. @Entity
  3. @Table(name = "STUDENTS")
  4. public class Student {
  5. @Id
  6. @Column(name = "student_id")
  7. @GeneratedValue(strategy = GenerationType.AUTO)
  8. private Long student_id;
  9. @Column(name = "name")
  10. private String name;
  11. @JsonIgnore
  12. @OneToMany(mappedBy = "student", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
  13. private Set<StudentTeacherRelation> teacher = new HashSet<>();
  14. }

的数据

  1. public interface TeacherRepo extends JpaRepository<Teacher, UUID> {
  2. }
  1. public interface StudentRepo extends JpaRepository<Student, Long> {
  2. }

的字符串

  • 服务-
  1. public interface TeacherService {
  2. public Set<Teacher> getAllTeachers();
  3. }
  1. @Service
  2. public class TeacherServiceImpl implements TeacherService{
  3. private final TeacherRepo teacherRepo;
  4. @Autowired
  5. public TeacherServiceImpl(TeacherRepo teacherRepo) {
  6. this.teacherRepo = teacherRepo;
  7. }
  8. @Override
  9. public Set<Teacher> getAllTeachers() {
  10. return new HashSet<>(teacherRepo.findAll());
  11. }
  12. }
  • 控制器-
  1. @RestController
  2. @RequestMapping(path = "/teacher")
  3. public class TeacherController {
  4. private final TeacherService teacherService;
  5. @Autowired
  6. public TeacherController(TeacherService teacherService) {
  7. this.teacherService = teacherService;
  8. }
  9. @GetMapping()
  10. public Set<Teacher> getAllTeachers() {
  11. return teacherService.getAllTeachers();
  12. }
  13. }


这是我得到的
[ {“教师_id”:“467 f4868 - 928 e-11 ee-b 9d 1 - 0242 ac 120002”,“姓名”:“教师”,“学生”:[] } ]
我应该把每个老师的学生也联系起来。
我更新了这个例子,以更清楚地说明为什么我需要这种方法->在连接表中有额外的列。

esbemjvw

esbemjvw1#

您所描述的方法是在Java中使用Spring和JPA处理多对多关系的常用方法。然而,有一些潜在的问题和改进需要考虑:
1.**延迟加载与急切加载:**在TeacherStudent实体的@OneToMany关系中使用FetchType.LAZY是提高性能的标准做法。但是,这也意味着相关实体只有在代码中显式访问时才会加载。这可能是为什么在获取教师时不会检索到学生数据的原因。如果需要立即加载相关实体,可以切换到FetchType.EAGER。但是,要小心急切加载,因为它可能会导致性能问题,特别是当您有大型数据集或复杂实体图时。
1.**Jackson序列化和延迟加载:**在典型的Sping Boot 应用程序中,Jackson用于将实体序列化为JSON。使用延迟加载,Jackson在序列化这些实体时可能会遇到问题,因为延迟加载的集合在序列化时没有初始化。要解决此问题,您可以在序列化之前手动初始化这些集合,或者使用Hibernate的@JsonIgnoreProperties注解忽略它们。但是,更好的方法可能是使用数据传输对象(DTO)。DTO允许您通过网络选择性地发送所需的数据,避免与延迟加载相关的问题,并在内部数据模型和向用户公开的数据之间添加额外的抽象层。
1.**使用DTO:**直接公开实体可能会导致几个问题,包括前面提到的延迟加载问题。DTO是一种模式,您可以专门为数据传输目的创建单独的类。这些类可以根据API使用者的确切数据要求进行定制,从而避免发送不必要的数据,并有助于将API层与数据库模型解耦。通过使用DTO,您可以手动将必要的数据加载到DTO中,从而有效地绕过与延迟加载和Jackson序列化相关的问题。

总结

总而言之,虽然延迟加载对性能有益,但它可能会使API中的序列化和数据检索复杂化。使用DTO提供了一种规避这些问题的方法,可以对通过API公开的数据进行更多控制,并可能提高应用程序的性能和可维护性。

nsc4cvqm

nsc4cvqm2#

您的代码

在原始代码中,TeacherStudent之间的多对多关系是通过中间实体StudentTeacherRelation管理的。这种方法涉及一个复合键(StudentTeacherKey)来处理关系。每个TeacherStudent实体都有一个StudentTeacherRelationSet来表示关联。

代码问题

***复杂性:**使用带有复合键的中间实体增加了关系管理的复杂性。
***延迟加载:**关系中的LAZY获取类型意味着除非显式访问,否则不会加载相关实体,导致数据检索问题。
***数据检索:**似乎没有从数据库中检索到与教师关联的学生,可能是由于配置错误或延迟加载问题。

我的代码(在本文末尾)

重构后的代码在TeacherStudent实体中使用了直接的@ManyToMany注解,从而消除了对中间StudentTeacherRelation实体的需要。

我所做的更改

***直接多对多Map:**x1m15 n1x和x1m16 n1x两个实体都使用x1m13 n1x沿着x1m14 n1x,直接Map两个实体,简化关系。
***删除中间实体:**删除了StudentTeacherRelation实体和StudentTeacherKey,降低了复杂性和潜在的错误来源。
***transmitting Annotation:**在service方法中增加了@Transactional(readOnly = true),保证transactions的正确处理,这对于实体的延迟加载尤为重要。

为什么会有这些变化?

***简化:**在JPA中,直接的多对多Map是处理此类关系的一种更直接和传统的方式。它降低了复杂性并与标准实践保持一致。
***提高可读性和维护性:**删除中间实体和复合键,使代码更容易理解和维护。
***延迟加载问题的潜在解决方案:**通过重新构建关系并确保适当的事务边界,与延迟加载和数据检索相关的问题可能会得到解决。

摘要

重构后的代码旨在提供一种更干净、更易于维护、功能更强大的方法来管理Sping Boot 应用程序中的多对多关系。它简化了关系Map,并与常见的JPA实践保持一致,可能解决初始复杂设置中遇到的问题。
Teacher.java

  1. @Entity
  2. @Table(name = "TEACHERS")
  3. @Data
  4. public class Teacher {
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.UUID)
  7. @Column(name = "teacher_id")
  8. private UUID id;
  9. @Column(name = "name")
  10. private String name;
  11. @ManyToMany
  12. @JoinTable(
  13. name = "STUDENT_TEACHER",
  14. joinColumns = @JoinColumn(name = "teacher_id"),
  15. inverseJoinColumns = @JoinColumn(name = "student_id")
  16. )
  17. private Set<Student> students = new HashSet<>();
  18. }

字符串
Student.java

  1. @Entity
  2. @Table(name = "STUDENTS")
  3. @Data
  4. public class Student {
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.AUTO)
  7. @Column(name = "student_id")
  8. private Long id;
  9. @Column(name = "name")
  10. private String name;
  11. @ManyToMany(mappedBy = "students")
  12. private Set<Teacher> teachers = new HashSet<>();
  13. }


StudentRepo.java

  1. public interface StudentRepo extends JpaRepository<Student, Long> {}


TeacherRepo.java

  1. public interface TeacherRepo extends JpaRepository<Teacher, UUID> {}


TeacherService.java

  1. public interface TeacherService {
  2. public Set<Teacher> getAllTeachers();
  3. }


TeacherServiceImpl.java

  1. @Service
  2. public class TeacherServiceImpl implements TeacherService {
  3. private final TeacherRepo teacherRepo;
  4. @Autowired
  5. public TeacherServiceImpl(TeacherRepo teacherRepo) {
  6. this.teacherRepo = teacherRepo;
  7. }
  8. @Override
  9. @Transactional(readOnly = true)
  10. public Set<Teacher> getAllTeachers() {
  11. return new HashSet<>(teacherRepo.findAll());
  12. }
  13. }


TeacherController.java

  1. @RestController
  2. @RequestMapping(path = "/teacher")
  3. public class TeacherController {
  4. private final TeacherService teacherService;
  5. @Autowired
  6. public TeacherController(TeacherService teacherService) {
  7. this.teacherService = teacherService;
  8. }
  9. @GetMapping()
  10. public Set<Teacher> getAllTeachers() {
  11. return teacherService.getAllTeachers();
  12. }
  13. }


PS:@Data来自Lombok

展开查看全部
pbpqsu0x

pbpqsu0x3#

你是自己创建关系表,并将它们设置为ManyToManyMap关系。整体Map是错误的-请参考-https://www.baeldung.com/jpa-many-to-many。按照示例,应该会有帮助。

相关问题