简体   繁体   中英

Wildfly - how to enable transactions to enable lazy loading

I have User and Post entities with a unidirectional relationship. I am getting org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: exception when I try to get all posts from a specific user.

According to these SO answers , the optimal way to handle this is to use @Transactional annotation for the service method/class. Placing annotations does not work for me. I am using Wildfly server, Hibernate, MySQL, and Java EE MVC web framework.

How do I make it work, ie get the posts from a user? I managed to do it via eager loading, but this is not recommended for performance reasons.

@Transactional
public class UserService {

    private List<User> users;
    private Set<Post> posts;


    @PersistenceContext(unitName = "my-pu")
    private EntityManager em;

    public List<User> getUsers() {

        String qlQuery = "SELECT u FROM User u";
        Query query = em.createQuery(qlQuery);
        users = query.getResultList();

        return users;
    }

    @Transactional
    public Set<Post> getUserPosts(Long userId) {

            String qlQuery = "SELECT u FROM User u WHERE u.id = :userId";
            Query query = em.createQuery(qlQuery);
            query.setParameter("userId", userId);
            User user = (User) query.getSingleResult();

            posts = user.getPosts();

        return posts;
    }
}

This is my Service method.

@Path("users")
@Controller
public class UserController {

    @Inject
    private Models models;

    @Inject
    private UserService service;

    @GET
    @Produces("text/html")
    @View("showUsers.ftl")
    public void users() {

        List<User> users = service.getUsers();

        models.put("users", users);
    }

    @GET
    @Path("{id}")
    @Produces("text/html")
    @View("showUserPosts.ftl")    
    public void getPosts(@PathParam("id") Long userId) {

        System.out.println("here am i");

        Set<Post> posts = service.getUserPosts(userId);

        models.put("posts", posts);
    }
}

This is my controller.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
            <persistence-unit name="my-pu" transaction-type="JPA">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/testdb?useSSL=false"/>
            <property name="javax.persistence.jdbc.user" value="testuser"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.password" value="test623"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
            <property name="hibernate.dialect.storage_engine" value="innodb"/>

            <property name="javax.persistence.schema-generation.database.action"
                      value="drop-and-create"/>      
            <property name="javax.persistence.sql-load-script-source"
                      value="META-INF/sql/data.sql" />        
        </properties>
    </persistence-unit>
</persistence>

An this is my persistence unit.

Error message:

org.jboss.resteasy.spi.UnhandledException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.zetcode.model.User.posts, could not initialize proxy - no Session
    at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:257)
    at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:195)
    at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:539)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:461)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:231)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:137)
    at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:361)
    at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:140)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:217)
    at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:227)
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:67)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)

You are returning an uninitialized collection proxy out of session boundaries (demarcated by the transaction in this case).

You can initialize the proxy by calling Hibernate.initialize(posts) or by just calling posts.size() before returning it (the first approach more clearly describes the intent imo).

An alternative that I use most often is to utilize DTOs, so that I don't have to deal with detached objects, but also because of the ability to tailor presentation objects (and web service responses in general) to the exact needs of clients using them. Also that way the domain model (Hibernate entities) is decoupled from the presentation logic, allowing the two to evolve independently.

Other alternatives include Open Session in View (anti-?) pattern and hibernate.enable_lazy_load_no_trans property, but they are less flexible and have their own pros and cons.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM