Handling Jackson Parent-Child Serialization

hibernate, jackson, java, json, spring

Solution

Thanks people.

I was able to resolve this issue by using JsonIdentityInfo on my classes (both parent and child) along with a plugin called jackson-datatype-hibernate through which I enabled a feature called FORCE_LAZY_LOADING

If it helps anyone, below is the code for both the above configurations:

Create a class as below to enable jackson-datatype-hibernate

public class HibernateAwareObjectMapper extends ObjectMapper {

    public HibernateAwareObjectMapper() {
        Hibernate4Module hbm = new Hibernate4Module();
        hbm.enable(Hibernate4Module.Feature.FORCE_LAZY_LOADING);
        registerModule(hbm);
    }
}

Tell Spring to use the above ObjectMapper than the default provided by Jackson:

<mvc:annotation-driven>
        <mvc:message-converters>
            <!-- Use the HibernateAware mapper instead of the default -->
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="com.adwitiya.o2plus.utilities.HibernateAwareObjectMapper" />
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

As for the JsonIdentityInfo, below is the code. Notice the use of JsonIgnoreProperties (I've used this mainly for all the sets as it creates a recursive big tree and I wanted to avoid that)

@Entity
@Table(name = "o2_branch", catalog = "o2plus"
)
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id")
@JsonIgnoreProperties(value = {"active", "modifiedBy", "modifiedTimestamp", "members", "staff"})
public class Branch implements java.io.Serializable {

    private Long id;
    private Address address;
    private String name;
    private String contactNumber;
    private Integer capacity;
    private String manager;
    private boolean active;
    private String modifiedBy;
    private Date modifiedTimestamp;
    private Set<Member> members = new HashSet<Member>(0);
    private Set<Staff> staff = new HashSet<Staff>(0);

Hope it helps more people.

Problem

I'm using Spring MVC (am new to this), Hibernate and Jackson for my project and am using JSON for data exchange between the server and client. I have a couple of classes "Employee" and "Address" with a ManyToOne / OneToMany relationship. I faced the cyclic reference error, however, I was able to resolve that using @JsonManagedReference and @JsonBackReference. But the problem is, during serialization (I'm querying the DB to get all the employees), Jackson is completely ignoring the Address property and serializing just 3 fields (other fields have been ignored specifically which you can see in the code). Here's the JSON returned ``` [ { "id": 1, "name": "xxx", "age": 100 }, { "id": 2, "name": "yyy", "age": 100 } ] ``` The Employee Class: ``` @Entity @Table(name = "e_employee", catalog = "emploman") public class Employee implements java.io.Serializable { private int id; private String name; private int age; private Address address; private String modifiedBy; private Date modifiedTime; private transient int addressId; public Employee() { } public Employee(int id, String name, int age, Address address, String modifiedBy, Date modifiedTime) { this.id = id; this.name = name; this.age = age; this.address = address; this.modifiedBy = modifiedBy; this.modifiedTime = modifiedTime; } public Employee(String name, int age, Address address, String modifiedBy, Date modifiedTime) { this.name = name; this.age = age; this.address = address; this.modifiedBy = modifiedBy; this.modifiedTime = modifiedTime; } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, nullable = false) public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(name = "name", nullable = false, length = 100) public String getName() { return name; } public void setName(String name) { this.name = name; } @Column(name = "age", nullable = false) public int getAge() { return age; } public void setAge(int age) { this.age = age; } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "address", nullable = false) @JsonBackReference("employee-address") public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @Column(name = "modified_by", nullable = false, length = 50) @JsonIgnore public String getModifiedBy() { return modifiedBy; } @JsonIgnore public void setModifiedBy(String modifiedBy) { this.modifiedBy = modifiedBy; } @Temporal(TemporalType.TIMESTAMP) @Column(name = "modified_time", nullable = false, length = 19) @JsonIgnore public Date getModifiedTime() { return modifiedTime; } @JsonIgnore public void setModifiedTime(Date modifiedTime) { this.modifiedTime = modifiedTime; } @JsonIgnore @Transient public int getAddressId() { return addressId; } @JsonIgnore public void setAddressId(int addressId) { this.addressId = addressId; } } ``` The Address Class: ``` @Entity @Table(name = "e_address", catalog = "emploman") public class Address implements java.io.Serializable { private int id; private String country; private String state; private String city; private String streetAddress; private String pinCode; private String modifiedBy; private Date modifiedTime; private Set<Employee> employees; public Address() { } public Address(int id, String country, String state, String city, String streetAddress, String pinCode, String modifiedBy, Date modifiedTime, Set<Employee> employees) { this.id = id; this.country = country; this.state = state; this.city = city; this.streetAddress = streetAddress; this.pinCode = pinCode; this.modifiedBy = modifiedBy; this.modifiedTime = modifiedTime; this.employees = employees; } public Address(String country, String state, String city, String streetAddress, String pinCode, String modifiedBy, Date modifiedTime, Set<Employee> employees) { this.country = country; this.state = state; this.city = city; this.streetAddress = streetAddress; this.pinCode = pinCode; this.modifiedBy = modifiedBy; this.modifiedTime = modifiedTime; this.employees = employees; } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(name = "country", nullable = false, length = 100) public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } @Column(name = "state", nullable = false, length = 100) public String getState() { return state; } public void setState(String state) { this.state = state; } @Column(name = "city", nullable = false, length = 100) public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Column(name = "street_address", nullable = false, length = 500) public String getStreetAddress() { return streetAddress; } public void setStreetAddress(String streetAddress) { this.streetAddress = streetAddress; } @Column(name = "pincode", nullable = false, length = 15) public String getPinCode() { return pinCode; } public void setPinCode(String pinCode) { this.pinCode = pinCode; } @Column(name = "modified_by", nullable = false, length = 50) public String getModifiedBy() { return modifiedBy; } public void setModifiedBy(String modifiedBy) { this.modifiedBy = modifiedBy; } @Temporal(TemporalType.TIMESTAMP) @Column(name = "modified_time", nullable = false, length = 19) public Date getModifiedTime() { return modifiedTime; } public void setModifiedTime(Date modifiedTime) { this.modifiedTime = modifiedTime; } @OneToMany(mappedBy = "address", fetch = FetchType.LAZY) @JsonManagedReference(value = "employee-address") public Set<Employee> getEmployees() { return employees; } public void setEmployees(Set<Employee> employees) { this.employees = employees; } } ``` So, basically, the json response I'm expecting is something like below: ``` [ { "id": 1, "name": "xxx", "age": 100, "address": { "country": "xxx", "city": "abc" } } ] ``` Could anyone please help? Update 1 I've tried getting Addresses from the DB, and employees related to that address is successfully being fetched ``` [ { "id": 1, "country": "xxx", "state": "yyy", "city": "zzz", "streetAddress": "abc", "pinCode": "12345", "modifiedBy": "xxx", "modifiedTime": 1400930509000, "employees": [ { "id": 2, "name": "xxx", "age": 190 }, { "id": 1, "name": "xxx", "age": 200 } ] } ] ```

Original source