【问题标题】:Eliminate circular JSON in Java Spring Many to Many Relationship消除Java Spring中的循环JSON多对多关系
【发布时间】:2017-05-15 10:40:05
【问题描述】:

我有一个 Spring 应用程序,其中包含 2 个具有多对多关系的实体。有学生和小组。一个学生可以是多个小组的成员,一个小组可以有多个学生。

学生模型

@Entity
@Table(name = "STUDENTS")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "id")

public class Student extends AbstractUser {

    //Fields

    @ManyToMany(fetch = FetchType.LAZY, targetEntity = Group.class)
    @JoinTable(name = "GROUPS_STUDENTS",
            joinColumns = { @JoinColumn(name = "student_id") },
            inverseJoinColumns = { @JoinColumn(name = "group_id") })
    private List<Group> groups = new ArrayList<Group>();

    //Constructors

    public Student(String password, String firstName, String lastName, String email){
        super(password, firstName, lastName, email);
    }

    public Student(){
    }

    //Setters and getters

    public List<Group> getGroups() {
        return groups;
    }

    public void setGroups(List<Group> groups) {
        this.groups = groups;
    }

}

组模型

@Entity
@Table(name = "GROUPS")
@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "id")
public class Group implements Item, Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", nullable = false, unique = true)
    private String name;

    @Column(name = "year", nullable = false, length = 1)
    private int year;

    @ManyToMany(mappedBy = "groups", targetEntity = Student.class)
    private List<Student> students;

    public Group(String name, int yearOfStudy) {
        this.setName(name);
        this.setYear(yearOfStudy);
    }
...
}

问题是当我请求显示所有学生时,如果 2 个学生在同一个组中,他们会出现在一个层次结构中,而不是一个接一个。我的意思是 JSON 太深了。我不知道如何准确解释,但我会举个例子。

它的外观

[
  {
    "id": 2,
    "password": "$2a$10$bxieGA7kWuEYUUMbYNiYo.SbGpo7X5oh8ulUsqCcrIR2cFN2HiQP2",
    "firstName": "First",
    "lastName": "Last",
    "email": "name@mail.com",
    "groups": [
      {
        "id": 1,
        "name": "G1",
        "year": 0,
        "users": [
          2,
          {
            "id": 1,
            "password": "$2a$10$9pfTdci7PeHtzxuAxjcOsOjPSswrU35/JOOeWPpgVhJI4tD2YpZdG",
            "firstName": "John",
            "lastName": "Smith",
            "email": "john.smith@mail.com",
            "groups": [
              1
            ]
          }
        ]
      }
    ]
  },
  1
]

应该如何显示

  {
    "id": 2,
    "password": "$2a$10$bxieGA7kWuEYUUMbYNiYo.SbGpo7X5oh8ulUsqCcrIR2cFN2HiQP2",
    "firstName": "First",
    "lastName": "Last",
    "email": "name@mail.com",
    "groups": [
      {
        "id": 1,
        "name": "G1",
        "year": 0,
      }
    ]
  },
  {
    "id": 1,
    "password": "$2a$10$9pfTdci7PeHtzxuAxjcOsOjPSswrU35/JOOeWPpgVhJI4tD2YpZdG",
    "firstName": "John",
    "lastName": "Smith",
    "email": "john.smith@mail.com",
    "groups": [
      {
        "id": 1,
        "name": "G1",
        "year": 0
      }]
  }
]

你知道如何解决这个问题吗?我不知道如何准确描述我的问题,这就是为什么我还没有找到解决方案。任何帮助将不胜感激。谢谢。

【问题讨论】:

    标签: java json spring hibernate


    【解决方案1】:

    要解决杰克逊无限递归,您可以使用@JsonManagedReference@JsonBackReference

    @JsonManagedReference 是引用的前向部分——即 正常序列化。

    @JsonBackReference 是后面的部分 参考 - 它将从序列化中省略。

    在此处查找更多详细信息:http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

    public class Student extends AbstractUser {
        @ManyToMany(fetch = FetchType.LAZY, targetEntity = Group.class)
        @JoinTable(name = "GROUPS_STUDENTS",
                joinColumns = { @JoinColumn(name = "student_id") },
                inverseJoinColumns = { @JoinColumn(name = "group_id") })
        @JsonManagedReference
        private List<Group> groups = new ArrayList<Group>();
    }
    
    public class Group implements Item, Serializable {
        @ManyToMany(mappedBy = "groups", targetEntity = Student.class)
        @JsonBackReference
        private List<Student> students;
    }
    

    【讨论】:

    • 我试过了,但这只会使它成为一个方向。我已将 @BackReference 添加到 Student 并且组字段消失了。但如果我切换注释,什么都不会发生。
    • 如果您不希望学生分组,您也可以使用@JsonIgnore 注释来简单地忽略关系的一侧,从而破坏链。
    【解决方案2】:

    如果您希望能够动态定义必须序列化的内容,您可以尝试我根据此处的问题开发的 jackson 插件:

    https://stackoverflow.com/a/28245875/869225

    这也帮助我处理反向引用。

    【讨论】:

      【解决方案3】:

      我已经解决了。我制作了一个自定义序列化程序。因此,在小组中,我将通过设置自定义注释 @JsonSerialize(using = CustomStudentSerializer.class)

      对学生进行序列化

      CustomStudentSerializer

      public class CustomStudentSerializer extends StdSerializer<List<Student>> {
      
          public CustomStudentSerializer() {
              this(null);
          }
      
          public CustomStudentSerializer(Class<List<Student>> t) {
              super(t);
          }
      
          @Override
          public void serialize(
                  List<Student> students,
                  JsonGenerator generator,
                  SerializerProvider provider)
                  throws IOException, JsonProcessingException {
      
              List<Student> studs = new ArrayList<>();
              for (Student s : students) {
                  s.setGroups(null);
                  studs.add(s);
              }
              generator.writeObject(studs);
          }
      }
      

      对组做同样的事情。当关系已经嵌套时,我刚刚删除了学生/组组件。现在它工作得很好。

      我花了一些时间才弄清楚这一点,但我在这里发帖是因为它也可能对其他人有所帮助。

      【讨论】:

        【解决方案4】:

        或者您可以使用 DTO(数据传输对象)类。这些是纯代码类,您可以在发送数据时根据需要进行设置。例如,对于您的情况,您可以:

        UserDTO.java:

        import java.util.List;
        
        public class UserDTO {
        
            private int id;
        
            private String password;
        
            private String firstName;
        
            private String lastName;
        
            private String email;
        
            private List<GroupDTO> groups;
        
            public int getId() {
                return id;
            }
        
            public void setId(int id) {
                this.id = id;
            }
        
            public String getPassword() {
                return password;
            }
        
            public void setPassword(String password) {
                this.password = password;
            }
        
            public String getFirstName() {
                return firstName;
            }
        
            public void setFirstName(String firstName) {
                this.firstName = firstName;
            }
        
            public String getLastName() {
                return lastName;
            }
        
            public void setLastName(String lastName) {
                this.lastName = lastName;
            }
        
            public String getEmail() {
                return email;
            }
        
            public void setEmail(String email) {
                this.email = email;
            }
        
            public List<GroupDTO> getGroups() {
                return groups;
            }
        
            public void setGroups(List<GroupDTO> groups) {
                this.groups = groups;
            }
        
        }
        

        GroupDTO.java

        package temp;
        
        public class GroupDTO {
        
            private int id;
        
            private String name;
        
            private int year;
        
            public int getId() {
                return id;
            }
        
            public void setId(int id) {
                this.id = id;
            }
        
            public String getName() {
                return name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        
            public int getYear() {
                return year;
            }
        
            public void setYear(int year) {
                this.year = year;
            }
        
        }
        

        这样您就可以自定义将在 json 文件中发送的信息。

        假设您有一个 User 类并且您想发送所有用户信息:

            List<StudentDTO> response = new ArrayList<StudentDTO>(); //List to be send
        
            List<Student> students = bussinessDelegate.findAllStudents(); //Or whatever you do to get all students
        
            for (int i = 0; i < students.size(); i++) {
        
                Student student = students.get(i);          
        
                StudentDTO studentDTO = new StudentDTO(); //Empty constructor
        
                studentDTO.setEmail(student.getEmail());
                studentDTO.setFirstName(student.getFirstName());
                studentDTO.setLastName(student.getLastName());
                studentDTO.setId(student.getId());
                studentDTO.setPassword(student.getPassword());
        
                List<Group> studentGroups = student.getGroups();
        
                List<GroupDTO> studentGroupsDTO = new ArrayList<GroupDTO>();
        
                for (int j = 0; j < studentGroups.size(); j++) {
        
                    Group group = studentGroups.get(j);
        
                    GroupDTO groupDTO = new GroupDTO();
        
                    groupDTO.setId(group.getId());
                    groupDTO.setName(group.getName());
                    groupDTO.setYear(group.getYear());
        
                    studentGroupsDTO.add(groupDTO);
        
                }
        
                studentDTO.setGroups(studentGroupsDTO);
                response.add(studentDTO);
            }
        
            //Here you have your response list of students ready to send`
        

        【讨论】:

        • 您需要一个映射器才能使其正常工作,例如:link**Jackson**
        【解决方案5】:

        使用@JsonIgnoreProperties 注释是另一种选择:

        @Entity
        public class Student extends AbstractUser {
            @ManyToMany(fetch = FetchType.LAZY, targetEntity = Group.class)
            @JoinTable(name = "GROUPS_STUDENTS",
                    joinColumns = { @JoinColumn(name = "student_id") },
                    inverseJoinColumns = { @JoinColumn(name = "group_id") })
            @JsonIgnoreProperties("students")
            private List<Group> groups = new ArrayList<Group>();
        }
        
        @Entity
        public class Group implements Item, Serializable {
            @ManyToMany(mappedBy = "groups", targetEntity = Student.class)
            @JsonIgnoreProperties("groups")
            private List<Student> students;
        }
        

        @JsonManagedReference+@JsonBackReference@JsonIdentityInfo@JsonIgnoreProperties 之间进行比较:http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-07-08
          • 1970-01-01
          • 1970-01-01
          • 2023-04-10
          • 2012-11-17
          • 2018-07-04
          • 2011-11-09
          相关资源
          最近更新 更多