0

Getting the following error because of a circular reference from the many to many relationship.

org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)

I've tried using @JsonIdentityInfo, @com.fasterxml.jackson.annotation.JsonIgnore, and @JsonManagedReference, but none of these are working for me. I'm using lombok, hence the @getter/setter.

User Table

@Entity
@Table(name = "user")
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="user_id", scope=User.class)
public class User implements Serializable{

    @Getter
    @Setter
    @Id
    @GeneratedValue(strategy = IDENTITY, generator = "increment")
    @GenericGenerator(name = "increment", strategy = "increment")
    public int user_id;

    @JsonManagedReference
    @Getter
    @Setter
    @com.fasterxml.jackson.annotation.JsonIgnore
    @ElementCollection(targetClass=UserInterest.class)
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.user", cascade=CascadeType.ALL)
    public Set<UserInterest> userInterestSet = new HashSet<UserInterest>();
}

Interest table

@Entity
@Table(name = "interest")
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="interest_id", scope=Interest.class)
public class Interest implements Serializable{

    @Getter
    @Setter
    @Id
    @GeneratedValue(strategy = IDENTITY, generator = "increment")
    @GenericGenerator(name = "increment", strategy = "increment")
    public int interest_id;

    @Getter
    @Setter
    @Column(name = "interest_name", nullable = false, length=100)
    public String interest_name;

    @Getter
    @Setter
    @com.fasterxml.jackson.annotation.JsonIgnore
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.interest", cascade=CascadeType.ALL)
    public Set<UserInterest> userInterestSet = new HashSet<UserInterest>();
}

UserInterest join table

@Entity
@Table(name="user_interest")
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="pk", scope=UserInterest.class)
@AssociationOverrides({
        @AssociationOverride(name="pk.user", joinColumns=@JoinColumn(name="user_id")),
        @AssociationOverride(name="pk.interest", joinColumns=@JoinColumn(name="interest_id"))
})
public class UserInterest implements Serializable {
    @Getter
    @Setter
    @com.fasterxml.jackson.annotation.JsonIgnore
    @GeneratedValue(generator = "increment")
    @GenericGenerator(name = "increment", strategy = "increment")
    public int user_interest_id;

    @EmbeddedId
    @Getter
    @Setter
    @com.fasterxml.jackson.annotation.JsonIgnore
    public UserInterestId pk;

    @Transient
    @Getter
    @Setter
    @com.fasterxml.jackson.annotation.JsonIgnore
    public User user;

    @Transient
    @Getter
    @Setter
    @com.fasterxml.jackson.annotation.JsonIgnore
    public Interest interest;
}

UserInterestId class

@Embeddable
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=UserInterestId.class)
public class UserInterestId implements Serializable{
    @Getter
    @Setter
    @org.codehaus.jackson.annotate.JsonIgnore

    @ManyToOne(fetch = FetchType.LAZY)
    @com.fasterxml.jackson.annotation.JsonIgnore
    @JsonBackReference
    public User user;

    @Getter
    @Setter
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="interest_id")
    @ManyToOne(fetch = FetchType.LAZY)
    @com.fasterxml.jackson.annotation.JsonIgnore
    @JsonBackReference
    public Interest interest;
}

When I run the following on my joined User objects I get the error:

ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
ow.writeValueAsString(userSet);

I'm pretty new to Hibernate, so would appreciate any pointers. I've read 5-10 SO articles that suggested the solutions I mentioned above, but I still haven't been able to stop the circular reference. Thanks a ton!

Mark
  • 85
  • 1
  • 11

2 Answers2

0

You should unproxy your objects. An example from here

@SuppressWarnings("unchecked")
protected T unproxy(T entity){
    if (entity == null) {
        return null;
    }

    if (entity instanceof HibernateProxy) {
        try {
            Hibernate.initialize(entity);
        } catch (ObjectNotFoundException e) {
            return null;
        }
        entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
    }
    return entity;
}

Or this way (from here)

public <P> List<P> unproxy(Collection<P> objects) {
    Session session = sessionFactory.getCurrentSession();
    List<P> result = new ArrayList<P>();
    for (P t : objects) {
        Object unproxied = ((SessionImplementor)session).getPersistenceContext().unproxy(t);
        result.add((P) unproxied);
    }
    return result;
}
Community
  • 1
  • 1
StanislavL
  • 56,971
  • 9
  • 68
  • 98
  • Hmm. Thanks. The first method doesn't work because it isn't an instanceof HibernateProxy. The second one executes okay, but doesn't change the objects. It still leads to the infinite loop. – Mark Dec 16 '15 at 06:57
  • What do you pass to the method(s)? – StanislavL Dec 16 '15 at 07:02
  • For the second one, basically this: **session = sessionFactory.openSession().beginTransaction()** .... **session.createQuery(hql) **.... **users = unproxy(users, session);** // I added session as an argument that took the place of sessionFactory.getCurrentSession(). – Mark Dec 17 '15 at 01:33
  • it never seems to be an instance of HibernateProxy. I also switched it to FetchType.LAZY but still no instance of HibernateProxy. – Mark Dec 17 '15 at 01:58
  • List of users is not proxy object. You need to unproxy every user instance in the list – StanislavL Dec 17 '15 at 05:41
  • I mean for the second method you mentioned. It's signature is unproxy(Collection

    objects, Session session). For the first one, I used a for loop and tried: for (User user : users) {if (user instanceof HibernateProxy) { // never is an instance of hibernateproxy. @StanislavL

    – Mark Dec 17 '15 at 05:44
0

Found a solution. For my ObjectWriter/ObjectMapper, I switched to com.fasterxml.jackson.databind.ObjectWriter / Mapper instead of codehaus. It seems that jsonIgnore and JsonIdentityInfo just weren't being acknowledged by codehaus.jackson.map, or at least not with my version.

Mark
  • 85
  • 1
  • 11