0

I'd like to know how to handle serialization with Spring REST with bidirectional relationships. I'm currently using Spring Boot the 1.3.0.BUILD-SNAPSHOT

Currently I'm receiving and internal Server Error stating an infinite recursion on a GET request on the primary class's repository.

It works with two classes where one of which is a Repository. In the example A being the one which has a repository:

@Entity
public class A implements Serializable {
    private static final long serialVersionUID = 1L;

    @OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
    @JsonManagedReference
    private List<B> b;

    public A() {

    }
    public List<B> getB() {
        return b;
    }
    public void setCategory(List<B> b) {
        this.b = b;
        for (B oneB : this.category) {
            oneB.setA(this);
        }
    }
}

@Entity
public class B implements Serializable {
    private static final long serialVersionUID = 1L;

    @ManyToOne
    @JoinColumn(name = "b_column")
    @JsonBackReference
    @RestResource(rel = "BParent")
    private A a;

    public B() {

    }
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
}

But if I add another class that is inside of B it won't work anymore and I'll get the infinite recursion if I send a GET request to A's repository (C having the same annotations as B on its b property):

@Entity
public class B implements Serializable {
    private static final long serialVersionUID = 1L;

    @ManyToOne
    @JoinColumn(name = "b_column")
    @JsonBackReference
    @RestResource(rel = "BParent")
    private A a;

    @OneToMany(mappedBy = "c", cascade = CascadeType.ALL)
    @JsonManagedReference
    private List<C> c;

    public B() {

    }
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
}

So from what I have observed I can omit the Jackson annotation in class C on the backreference to B without getting any change in behavior. Is this known behavior or am I missing something?

Update 1

I tried to get rid of the bidirectional relationships between the B and the C object by modifying the B class and removed the back reference on C:

@Entity
public class B implements Serializable {
    private static final long serialVersionUID = 1L;

    @ManyToOne
    @JoinColumn(name = "b_column")
    @JsonBackReference
    @RestResource(rel = "BParent")
    private A a;

    //This is now a unidirectional relationship to C
    @NotNull
    @OneToMany
    @JoinColumn(name="b_id", referencedColumnName="id")
    private List<C> c;

    public B() {

    }
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }

    public List<C> getC() {
    return c;
    }

    public void setC(List<C> c) {
       this.c= c;
    }
}

While this gets rid of the infinite recursion upon a GET request on A's repository (Hooray!) it also eliminates the possibility store C objects when sending a POST to A's repository (boooh!). This will throw this error message:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing

Update 2

I tried to add a repository for the B class. This resulted in another infinite recursion. But this time the application and the IDE just crash because the error is not handled properly.

Baiteman
  • 535
  • 1
  • 4
  • 14

1 Answers1

0

So basically what sort of works if I define Object B as a repository and annotate the a property on class A with

@RestResource(exported = false)
@OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
@JsonManagedReference
private List<B> b;

If it is annotated this way Object C will get properly serialized without any infinite recursion nonsense.

If anyone has a good explanation or solution I'll happily mark it as the answer.


Fun Fact about Spring Rest

Spring Rest has some issues with variable/class names that have multiple consecutive capital letters. For example a repository for a class named

public class ACos

will not work properly and a variable named

private String aCos;

will get serialized to

{"acos":"whatever"}

Baiteman
  • 535
  • 1
  • 4
  • 14