Wildfly - 如何启用事务以启用延迟加载

Wildfly - how to enable transactions to enable lazy loading

我有 User 和 Post 具有单向关系的实体。当我尝试从特定用户获取所有帖子时,出现 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: 异常。

根据这些 SO answers,处理此问题的最佳方法是对服务 method/class 使用 @Transactional 注释。放置注释对我不起作用。我正在使用 Wildfly 服务器、Hibernate、MySQL 和 Java EE MVC 网络框架。

如何让它发挥作用,即从用户那里获取帖子?我设法通过预先加载来做到这一点,但出于性能原因不推荐这样做。

@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;
    }
}

这是我的服务方法。

@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);
    }
}

这是我的控制器。

<?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>

这是我的持久化单元。

错误信息:

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(SynchronousDispatcher.java:231)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess[=13=](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.handleRequest(ServletChain.java:67)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)

您正在返回会话边界之外的未初始化集合代理(在本例中由事务划分)。

您可以通过调用 Hibernate.initialize(posts) 或在返回之前调用 posts.size() 来初始化代理(第一种方法更清楚地描述了我的意图)。

我最常使用的替代方法是利用 DTO,这样我就不必处理分离的对象,而且还因为能够根据使用它们的客户的确切需求。同样,域模型(Hibernate 实体)与表示逻辑分离,允许两者独立发展。

其他替代方案包括 Open Session in View(反?)模式和 hibernate.enable_lazy_load_no_trans 属性,但它们不太灵活并且各有利弊。