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

ztyzrc3y  于 12个月前  发布在  Spring
关注(0)|答案(3)|浏览(125)

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

@Embeddable
@Data
public class StudentTeacherKey implements Serializable {

    @Column(name = "teacher_id")
    private UUID teacher_id;

    @Column(name = "student_id")
    private Long student_id;
}

x

@Entity
@Table(name = "STUDENT_TEACHER")
@Data
public class StudentTeacherRelation {

    @JsonIgnore
    @EmbeddedId
    private StudentTeacherKey id;

    @ManyToOne(fetch = FetchType.EAGER)
    @MapsId("student_id")
    @JoinColumn(name = "student_id")
    private Student student;

    @JsonIgnore
    @ManyToOne(fetch = FetchType.EAGER)
    @MapsId("teacher_id")
    @JoinColumn(name = "teacher_id")
    private Teacher teacher;

    @Column(name = "extra_column")
    private String extraColumn;

}
@Data
@Entity
@Table(name = "TEACHERS")
public class Teacher {

    @Id
    @Column(name = "teacher_id")
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID teacher_id;

    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "teacher", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<StudentTeacherRelation> students = new HashSet<>();
}
@Data
@Entity
@Table(name = "STUDENTS")
public class Student {

    @Id
    @Column(name = "student_id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long student_id;

    @Column(name = "name")
    private String name;

    @JsonIgnore
    @OneToMany(mappedBy = "student", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<StudentTeacherRelation> teacher = new HashSet<>();
}

的数据

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

的字符串

  • 服务-
public interface TeacherService {
    public Set<Teacher> getAllTeachers();
}
@Service
public class TeacherServiceImpl implements TeacherService{

    private final TeacherRepo teacherRepo;

    @Autowired
    public TeacherServiceImpl(TeacherRepo teacherRepo) {
        this.teacherRepo = teacherRepo;
    }

    @Override
    public Set<Teacher> getAllTeachers() {
        return new HashSet<>(teacherRepo.findAll());
    }
}
  • 控制器-
@RestController
@RequestMapping(path = "/teacher")
public class TeacherController {

    private final TeacherService teacherService;

    @Autowired
    public TeacherController(TeacherService teacherService) {
        this.teacherService = teacherService;
    }

    @GetMapping()
    public Set<Teacher> getAllTeachers() {
        return teacherService.getAllTeachers();
    }
}


这是我得到的
[ {“教师_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

@Entity
@Table(name = "TEACHERS")
@Data
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    @Column(name = "teacher_id")
    private UUID id;

    @Column(name = "name")
    private String name;

    @ManyToMany
    @JoinTable(
        name = "STUDENT_TEACHER",
        joinColumns = @JoinColumn(name = "teacher_id"),
        inverseJoinColumns = @JoinColumn(name = "student_id")
    )
    private Set<Student> students = new HashSet<>();
}

字符串
Student.java

@Entity
@Table(name = "STUDENTS")
@Data
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "student_id")
    private Long id;

    @Column(name = "name")
    private String name;

    @ManyToMany(mappedBy = "students")
    private Set<Teacher> teachers = new HashSet<>();
}


StudentRepo.java

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


TeacherRepo.java

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


TeacherService.java

public interface TeacherService {
    public Set<Teacher> getAllTeachers();
}


TeacherServiceImpl.java

@Service
public class TeacherServiceImpl implements TeacherService {

    private final TeacherRepo teacherRepo;

    @Autowired
    public TeacherServiceImpl(TeacherRepo teacherRepo) {
        this.teacherRepo = teacherRepo;
    }

    @Override
    @Transactional(readOnly = true)
    public Set<Teacher> getAllTeachers() {
        return new HashSet<>(teacherRepo.findAll());
    }
}


TeacherController.java

@RestController
@RequestMapping(path = "/teacher")
public class TeacherController {

    private final TeacherService teacherService;

    @Autowired
    public TeacherController(TeacherService teacherService) {
        this.teacherService = teacherService;
    }

    @GetMapping()
    public Set<Teacher> getAllTeachers() {
        return teacherService.getAllTeachers();
    }
}


PS:@Data来自Lombok

pbpqsu0x

pbpqsu0x3#

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

相关问题