Spring MVC 3.1.2 + Jackson 2: LazyInitializationEx

2019-05-14 04:31发布

I'm getting crazy with an error that I'm having using Spring MVC 3.1.2 and Jackson 2.

I have the following model Class:

@Entity
@Table(name = "USER")
@JsonIgnoreProperties(ignoreUnknown=true)
public class User implements Serializable
{
    @Id
    @SequenceGenerator(name = "USER_ID", sequenceName = "USER_ID_SEQ", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_ID")
    private Long id;

    @Column(length = 50, nullable = false)
    private String firstName;

    @Column(length = 50, nullable = false)
    private String lastName;

    @ManyToMany
    @JoinTable(name = "FRIENDS",
        joinColumns = @JoinColumn(name = "personId"),
        inverseJoinColumns = @JoinColumn(name = "friendId")
    )
    @JsonManagedReference
    private List<User> friends;

    @ManyToMany
    @JoinTable(name="FRIENDS",
        joinColumns=@JoinColumn(name="friendId"),
        inverseJoinColumns=@JoinColumn(name="personId")
    )
    @JsonIgnore
    private List<User> friendOf;

    // Other attributes and methods... 
}

When I get an single instance of User it is correctly serialized by Jackson. But when I try to get an instance of User that contains friends, the following exception is thrown:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.frooid.model.User.friends, no session or session was closed

I'm getting this instance using a single HQL:

select u from User u left join fetch u.friends f where u.id = :id

Thanks to everybody!

3条回答
仙女界的扛把子
2楼-- · 2019-05-14 04:39

ToMany associations are lazy-loaded by default. This means the the friens of your users will only be loaded from the database when invoking a method of the friends list.

This lazy-loading can only occur while the session used to load the user is open. So if you return the user from your transactional method without the friends list loaded, the session will be closed, and trying to load the freinds list will lead to the exception you're getting.

So, if the client needs the freinds list to be loaded, ither fetch the friends using HQL, or force the initialization of the list, inside the service method, by calling a method on the list or by calling Hibernate.initialize(user.getFriends()).

EDIT: since you have a fetch in your HQL, it should work. Another problem is that the bidirectional association is mapped twice: once on the friends field, and once on the friendOf field. One of those association must be marked as the inverse of the other using the mappedBy attribute:

@ManyToMany(mappedBy = "friends")
@JsonIgnore
private List<User> friendOf;
查看更多
时光不老,我们不散
3楼-- · 2019-05-14 04:40

I had Similar issue. The approach was to create a new class(view) and adding this class in @JsonView({NewClass1.class,NewClass2.class}) on each property, in the Entity where your properties are defined. You can choose the variables which you want to be loaded as part of your response and include the class in @JsonView. Just as simple as that!

查看更多
Melony?
4楼-- · 2019-05-14 04:41

Try using the OpenSessionInViewFilter while this is meant for accessing lazy initialized fields in your view it may keep the session open for Jackson to be able to access the collection.

The OpenSessionInViewFilter binds a Hibernate Session to the thread for the entire processing of the request. Intended for the "Open Session in View" pattern, i.e. to allow for lazy loading in web views despite the original transactions already being completed.

In Web.xml

<filter>
    <filter-name>OpenSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>OpenSessionInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

API Documentation

查看更多
登录 后发表回答