How to handle RESTful response of Entity with self

2019-05-26 18:04发布

问题:

I have a class Foo which have reference to itself. This is rough example of situation I have been in.

@Entity
public class Foo() {
  @Column(name = "id", unique = true, nullable = false)
  private Long id;

  @JsonIgnore
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "id_Foo")
  private Foo parent;
}

When i pass Foo object to JSON response generator, all required getters are initiated and all required data retrieved from DB by JPA.

Now the problem is that if i remove @JsonIgnore from self-reference field I'm getting parent info, and parent parent info and etc.

What I want is that I could limit deep of retrieved parent info. For example.:

  • Deep 0 - I will get only Foo data
  • Deep 1 - I will get data of Foo and Foo parent
  • Deep 2 - I will get data of Foo and Foo parent and Foo parent parent etc.

Maybe there is a simple solution with annotations or smth, because i couldn't find anything.

EDIT:

Solved this problem with custom serializer and some recursion magic.

private void serializeParents(JsonGenerator gen, Foo value, int currentDepth) {
    if (currentDepth < PARENTS_DEPTH) {
      if (value.getMom() != null) {
        serializeParentData(gen, value.getMom(), currentDepth);
      }

      if (value.getDad() != null) {
        serializeParentData(gen, value.getDad(), currentDepth);
      }
    }
}

private void serializeParentData(JsonGenerator gen, Foo value, int currentDepth) {
    gen.writeObjectFieldStart(Gender.M.equals(value.getGender()) ? "dad" : "mom");
    // other parameters ...
    serializeParents(gen, value, currentDepth + 1);
    gen.writeEndObject();
}

回答1:

I think the best approach would be to create a custom DTO object to gather all your Foo properties as well as its parent, so you won't get a recursion relationship and avoid lazy loading exceptions.

So you will keep your entity as it is:

@Entity
public class Foo() {
   @Column(name = "id", unique = true, nullable = false)
   private Long id;

   @JsonIgnore
   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "id_Foo")
   private Foo parent;
}

And create a custom DTO object like this:

public class FooDto() {

   private Long id;
   private Foo parent;

   //getters and setters here
}

Then you can use this DTO to store JSOn results in your requests.

You can take a look at What is the point of using DTO (Data Transfer Objects)? for further details.

@JsonIgnore documentation:

According to the Jackson @JsonIgnore documentation there's no such option(property) to specify a depth/level for the annotation, as there's only one possible property value to define if this anotation is active or not.