Michał Ziobro
Michał Ziobro

Reputation: 11762

RESTEasy and Jackson provider cannot serialise entity with @XmlIDREF and @XmlID to JSON

I have such two entities:

@XmlRootElement(name = "provider")
@XmlAccessorType(XmlAccessType.PROPERTY) 
@Entity    
public class Provider {
  //...
  @XmlElementWrapper(name = "industries")
  @XmlElement(name = "industry")
  @XmlIDREF
  @ManyToMany(mappedBy = "providers", fetch = FetchType.EAGER)
  public Set<Industry> getIndustries() {
    return industries;
  }

}

And second entity:

@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
@Entity
public class Industry implements Serializable {

     //...
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Basic(optional = false)
     @Column(name = "industry_id", nullable = false /*, columnDefinition = "BIGINT UNSIGNED" */)
     public Long getIndustryId() {
          return industryId;
      }

     public void setIndustryId(Long industryId) {
          this.industryId = industryId;
     }

     //...

     @XmlID
     @Transient
     public String getSelfLink() {
          return getIndustryId().toString();
     }
}

And now using RESTEasy to generate both XML and JSON I'm getting expected result only in XML and in JSON there is instead of id return entire Industry entity + exception thrown.

Correct XML received:

      <provider>
            <userId>8</userId>
            //.... - deleted for simplicity
            <industries>
                <industry>1</industry>
            </industries>
        </provider>

But incorrect result in JSON:

{"provider":{ /* deleted for simplicity */ 
"industry":[{"industryId":1,"name":"Bran?a medyczna","description":null,"providers"}]}}

And "industry" : 1 should return only identifier!

Exception thrown:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: pl.salonea.entities.Industry.providers, could not initialize proxy - no Session (through reference chain: pl.salonea.jaxrs.utils.ResourceList["resources"]->java.util.ArrayList[0]->pl.salonea.entities.Provider["industry"]->org.hibernate.collection.internal.PersistentSet[0]->pl.salonea.entities.Industry["providers"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210)

And:

Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: pl.salonea.entities.Industry.providers, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:575)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:214)
    at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:155)
    at org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:160)
    at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:88)

Upvotes: 4

Views: 778

Answers (1)

Gavin King
Gavin King

Reputation: 4263

I experienced the same thing and I solved it by adding an @XmlAttribute annotation to the @XmlID. Here's what worked for me. (Note: my code is written in Ceylon, not Java.)

shared entity class Employee(name, manager) {

    xmlAttribute xmlID
    generatedValue id
    shared late Integer id;

    column { length = 50; }
    shared String name;

    shared variable Integer? year = null;

    xmlIDREF
    manyToOne
    shared Employee? manager;

    xmlTransient
    oneToMany { mappedBy = "manager"; }
    shared Set<Employee> employees = set {};

}

I have no clue why this is necessary, especially since AFAIK @XmlAttribute vs @XmlElement is otherwise not meaningful for JSON.

Upvotes: 2

Related Questions