Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get around Jackson's inability to handle direct self-references in JPA beans

So I am working on a RESTful data API using Java, Hibernate, JPA annotations, JAX-RS annotations, JAXB annotations, Jersey, and the Jackson JSON parser.

After trying various configurations of the MAPPING and NATURAL JSON notations that Jersey provides, I finally decided to use the Jackson JSON parser instead. Jackson would be perfect except for this one issue...

The issue I have come across is that Jackson does not work with the JAXB annotations, "@XmlID" and "@XmlIDREF", which I use to signify my entity relationships, and although the "@JsonBackReference" and "@JsonManagedReference" help with this. The combination seems to break down when dealing with direct self-referencing properties.

This seems like it would be a fairly common problem. How have any of you circumvented this limitation with Jackson?

With my POJO as...

@XmlRootElement
public class Employee implements Serializable {
    private Date lastUpdatedOn;
    private Employee lastUpdatedBy;
    private Integer empId;

    @JoinColumn(nullable=false)
    @OneToOne
    @XmlIDREF
    public Employee getLastUpdatedBy() {
        return createdBy;
    }
    public void setLastUpdatedBy(Employee lastUpdatedBy) {
        this.lastUpdatedBy = lastUpdatedBy;
    }

    @Temporal(TemporalType.TIMESTAMP)
    public Date getLastUpdatedOn() {
        return createdOn;
    }
    public void setLastUpdatedOn(Date lastUpdatedOn) {
        this.lastUpdatedOn = lastUpdatedOn;
    }
    @XmlID
    @XmlJavaTypeAdapter(IntegerAdapter.class)
    public Integer getEmpId() {
        return empId;
    }
    public void setEmpId(Integer empId) {
        this.empId = empId;
    }
}

... and the following EmployeeResource...

@Path("/Employees")
public class EmployeeResource {
  private List<Employee> employees;

  public List<Employee> getEmployees() {
    return employees;
  }
  public void setEmployees(List<Employee> employees) {
    this.employees = employees;
  }

  @GET
  @Path("/{empId}")
  public Response getEmployees(
    @Context UriInfo ui
    , @PathParam("id") Integer empId
  ) {
    this.employees = HibernateUtil.pagedQuery(
      Employee.class
      , new ArrayList() {
        Restrictions.eq("empId",empId)
      }
    );
    return Response.ok(this).build();
  }
}

My JAX-RS resource will produce the following error

org.codehaus.jackson.map.JsonMappingException: Direct self-reference leading to cycle (through reference chain: resources.EmployeeResource["employees"]->java.util.ArrayList[0]->entities.Employee["lastUpdatedBy"])

... but I want it to produce...

{
  "employees" : [ {
    "lastUpdatedOn" : 1331149770737,
    "lastUpdatedBy" : 10150,
    "empId" : 10150,
  } ],
}

Thanks in advance, everyone!

NOTES:

  1. I use the IntegerAdapter.class to convert it to a string so that it will work with the @XmlID annotation.
  2. The Employee and EmployeeResource classes described above are merely abbreviated versions of my actual implementation, but they represent the portion of my implementation that is relevant to this direct self-referencing issue.

EDIT #1 2012.03.10 I'm sorry, in my first version of this question I had gotten confused between the version I have using Jersey's natural notation vs the version I have running with Jackson. I have revised my question to more accurately reflect the direct self-referencing issue I have with Jackson.

like image 887
hypno7oad Avatar asked Oct 08 '22 10:10

hypno7oad


1 Answers

Jackson 1.x does not have specific support for resolving cyclic references, but there is support for handling parent/child style dependencies: this blog entry has more info.

Jackson 2.0 will have support for arbitrary Object Id / reference handling, using new @JsonIdentityInfo annotation, so perhaps that could be used to solve the problem. Official 2.0 release is not out yet, but release candidates (latest being RC2) are, in case you wanted to have a look. Not sure if it would handle your problem, but maybe it would help.

EDIT: Actually, Jackson JAXB annotation module will have support for @XmlID / @XmlIDREF for 2.0.0 -- this was just implemented, see here.

like image 140
StaxMan Avatar answered Oct 13 '22 12:10

StaxMan