Infinite Recursion with Jackson JSON and Hibernate JPA issue

前端 未结 25 3142
你的背包
你的背包 2020-11-21 07:31

When trying to convert a JPA object that has a bi-directional association into JSON, I keep getting

org.codehaus.jackson.map.JsonMappingException: Infinite          


        
25条回答
  •  别跟我提以往
    2020-11-21 07:32

    JsonIgnoreProperties [2017 Update]:

    You can now use JsonIgnoreProperties to suppress serialization of properties (during serialization), or ignore processing of JSON properties read (during deserialization). If this is not what you're looking for, please keep reading below.

    (Thanks to As Zammel AlaaEddine for pointing this out).


    JsonManagedReference and JsonBackReference

    Since Jackson 1.6 you can use two annotations to solve the infinite recursion problem without ignoring the getters/setters during serialization: @JsonManagedReference and @JsonBackReference.

    Explanation

    For Jackson to work well, one of the two sides of the relationship should not be serialized, in order to avoid the infite loop that causes your stackoverflow error.

    So, Jackson takes the forward part of the reference (your Set bodyStats in Trainee class), and converts it in a json-like storage format; this is the so-called marshalling process. Then, Jackson looks for the back part of the reference (i.e. Trainee trainee in BodyStat class) and leaves it as it is, not serializing it. This part of the relationship will be re-constructed during the deserialization (unmarshalling) of the forward reference.

    You can change your code like this (I skip the useless parts):

    Business Object 1:

    @Entity
    @Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
    public class Trainee extends BusinessObject {
    
        @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
        @Column(nullable = true)
        @JsonManagedReference
        private Set bodyStats;
    

    Business Object 2:

    @Entity
    @Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
    public class BodyStat extends BusinessObject {
    
        @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
        @JoinColumn(name="trainee_fk")
        @JsonBackReference
        private Trainee trainee;
    

    Now it all should work properly.

    If you want more informations, I wrote an article about Json and Jackson Stackoverflow issues on Keenformatics, my blog.

    EDIT:

    Another useful annotation you could check is @JsonIdentityInfo: using it, everytime Jackson serializes your object, it will add an ID (or another attribute of your choose) to it, so that it won't entirely "scan" it again everytime. This can be useful when you've got a chain loop between more interrelated objects (for example: Order -> OrderLine -> User -> Order and over again).

    In this case you've got to be careful, since you could need to read your object's attributes more than once (for example in a products list with more products that share the same seller), and this annotation prevents you to do so. I suggest to always take a look at firebug logs to check the Json response and see what's going on in your code.

    Sources:

    • Keenformatics - How To Solve JSON infinite recursion Stackoverflow (my blog)
    • Jackson References
    • Personal experience

提交回复
热议问题