Search code examples
hibernaterestjakarta-eelazy-loading3-tier

Hibernate lazy loading within 3-tier java ee application


I've built a Java EE application with hibernate and wildly 10. I didn't use any servlets but a REST API built with Java API for RESTful WebService. Because my models have become bigger and bigger I set my collections from FetchType.EAGER to FetchType.LAZY. Now I am getting a LazyInitializationException with the message could not initialize proxy - no Session. I think I do understand why this is happening but I don't understand when hibernate is trying to access the proxy.

Entity:

@Entity
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Version
    private Timestamp lastChanged;

    @NotNull
    private Date creationDate;

    @ManyToMany
    private Set<Entity> lazyEntity;

    public User() { }

    // getters and setters
}

DAO:

public interface UserDAO {
    public List<User> getAll();
}

Bean:

@Stateless
@Remote(UserDAO.class)
public class UserBean<User> implements UserDAO {

    @PersistenceContext
    protected EntityManager em;

    @Override
    public List<User> getAll() {
        return this.em.createQuery("SELECT u FROM User u ORDER BY u.creationDate", User.class)
                      .getResultList();
    }
}

Endpoint:

@Path("/users")
@Stateless
public class UsersEndpoint {

    @EJB
    private UserDAO userDAO;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getAll() {
        List<User> users = userDAO.getAll();
        if (users.size() == 0)
            return Response.status(Response.Status.NOT_FOUND)
                           .build();
        return Response.ok(users).build();
    }
}

My question is why isn't the lazyEntity just returned as null but an error is thrown? In which part is hibernate trying to access the lazyEntity, because as you can see in the code above I don't try to access within the API endpoint. Am I able to catch the Exception?

What can I do to receive the object as below without getting the exception? I've already seen that I can disable it by changing the property hibernate.enable_lazy_load_no_trans. But I also read that this isn't highly recommended.

User {
    id: 1,
    lastChanged: 0981283812738,
    creationDate: 2178371283098,
    lazyEntity: null
}

Solution

  • I think the problem is in the serialization to JSON of the getAll() method results in UsersEndpoint (triggered by the annotation on the method).

    I assume you use Jersey+Jackson (which is a common Java<->JSON converter).

    When the getAll() method complete, Jackson take the results of the method and try to convert it to JSON by getting all the data of the User class. To do that, Jackson call getLazyEntity() in the User class and, as it is lazy loaded, trigger a database access (and fail because the session is close).

    If you want to have the data of the "lazyEntity" in your JSON response, you have to fetch the data before the session close: inside the DAO with a fetch join in the HQL query.

    If you don't want the lazyEntity in the JSON, you have to tell Jackson to not serialize the lazyEntity, you can do that with a Jackson view. See How can a @JsonView working with jersey and http://wiki.fasterxml.com/JacksonJsonViews.