【问题标题】:Unable to resolve circular reference issue with JPA and REST Controller in Spring无法解决 Spring 中 JPA 和 REST 控制器的循环引用问题
【发布时间】:2021-11-03 16:43:01
【问题描述】:

我正在开发一个 @RestController 类,它应该返回一个 JSON 数据,其中包含 Teacher 和 1 个或多个 Sections 的数组。

我目前在 teacher_section 表中只有 2 条不同的记录

我相信这种关系是:

  • 1 Teacher 可以处理 MANY Section(s)

  • 1 Section 可以由 MANY 不同的Teacher(s) 处理

这让我认为要使用的正确关系注释是@ManyToMany

数据库表

CREATE TABLE `teacher_section` (
  `teacher_id` int(11) NOT NULL,
  `section_id` int(11) NOT NULL,
  KEY `teacher_id` (`teacher_id`),
  KEY `section_id` (`section_id`),
  CONSTRAINT `teacher_section_ibfk_2` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`id`),
  CONSTRAINT `teacher_section_ibfk_3` FOREIGN KEY (`section_id`) REFERENCES `section` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='This is a join table';

CREATE TABLE `teacher` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `employee_id` int(11) NOT NULL,
  `first_name` varchar(45) NOT NULL,
  `middle_name` varchar(45) NOT NULL,
  `last_name` varchar(45) NOT NULL,
  `is_adviser` bit(1) NOT NULL DEFAULT b'0',
  `is_active` bit(1) NOT NULL DEFAULT b'1',
  `date_created` timestamp NOT NULL DEFAULT current_timestamp(),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

CREATE TABLE `section` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `section_name` varchar(45) NOT NULL,
  `is_active` bit(1) NOT NULL DEFAULT b'1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

Java 类如下。

抽象实体

@MappedSuperclass 
public class AbstractEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name="is_active")
    private boolean isActive;

//getters and setters....
}

老师

@Entity
@Table(name="teacher")
public class Teacher extends AbstractEntity{
    
    @Column(name="employee_id")
    private int employeeId;
    
    @Column(name="first_name")
    private String firstName;
    
    @Column(name="middle_name")
    private String middleName;
    
    @Column(name="last_name")
    private String lastName;
    
    @Column(name="is_adviser")
    private boolean isAdviser;
    
    @Column(name="date_created")
    private Timestamp dateCreated;
    
    @ManyToMany(cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
        })
    @JoinTable(
              name = "teacher_section", 
              joinColumns = @JoinColumn(name = "teacher_id"), 
              inverseJoinColumns = @JoinColumn(name = "section_id"))
    Set<Section> sections;

    //getters and setters.....
}

部分

@Entity
@Table(name="section")
public class Section extends AbstractEntity{
    
    @Column(name = "section_name")
    private String sectionName;
    
    @ManyToMany (mappedBy = "sections")
    private Set<Teacher> teachers;

    //getters and setters...
}

TeacherRestController

@RestController
@RequestMapping("/teachers")
@CrossOrigin(origins = "http://localhost:4200")
public class TeacherRestController {

    @Autowired
    TeacherRepository teacherRepository;
    
    
    @GetMapping
    public List<Teacher> getTeachers(){
        //GET localhost:8080/grading/teachers
        return teacherRepository.findAll();
    }
}

TeacherRepository

public interface TeacherRepository extends JpaRepository<Teacher, Long>{

}

问题

端点GETlocalhost:8080/grading/teachers返回的json数据在Teacher内嵌套Section多次。

Postman 中的响应如下所示:

[
    {
        "id": 1,
        "employeeId": 20210001,
        "firstName": "John",
        "middleName": "Someone",
        "lastName": "Doe",
        "dateCreated": "2021-08-29T13:13:29.000+00:00",
        "adviser": false,
        "section": [
            {
                "id": 1,
                "sectionName": "Unity",
                "teacher": [
                    {
                        "id": 1,
                        "employeeId": 20210001,
                        "firstName": "John",
                        "middleName": "Someone",
                        "lastName": "Doe",
                        "dateCreated": "2021-08-29T13:13:29.000+00:00",
                        "adviser": false,
                        "section": [
                            {
                                "id": 1,
                                "sectionName": "Unity",
                                "teacher": [
                                    {
                                        "id": 1,
                                        "employeeId": 20210001,
                                        "firstName": "John",
                                        "middleName": "Someone",
                                        "lastName": "Doe",
                                        "dateCreated": "2021-08-29T13:13:29.000+00:00",
                                        "adviser": false, .....

我不知道出了什么问题。我什至尝试按照以下 2 个网站中有关如何正确实施 @ManyToMany 的在线指南进行操作,但仍然无法解决问题。

https://www.baeldung.com/jpa-many-to-many

https://vladmihalcea.com/the-best-way-to-use-the-manytomany-annotation-with-jpa-and-hibernate/

我使用 Spring Tool Suite IDE 和 MySQL 作为数据库。

提前致谢。

【问题讨论】:

  • 您必须决定要序列化哪种关系。请阅读baeldung.com/…
  • 您需要@JsonIdentityInfo,或者更好的是,单独的 DTO 表示。
  • @SimonMartinelli 感谢您分享参考资料。它有很多我还不知道的信息。
  • @chrylis-cautiouslyoptimistic-@JsonIdentityInfo 解决了我的问题!非常感谢!!我将详细了解与 Json 和实验替代方案相关的注释。
  • 不要在控制器中使用实体对象,这是非常糟糕的做法。创建单独的 dto 对象和地图实体。

标签: java spring spring-boot jackson jackson-databind


【解决方案1】:

您必须使用@JsonManagedReference@JsonBackReference 注释。第一个在所属根对象上,第二个在子对象上。

在老师:

@JsonManagedReference
Set<Section> sections;

在部分:

@JsonBackReference
private Set<Teacher> teachers;

替代方法是将@JsonIgnore 添加到Section 中的teachers 字段。

【讨论】:

    猜你喜欢
    • 2016-01-12
    • 2021-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    相关资源
    最近更新 更多