我有User和Post实体具有单向关系。当我尝试从特定用户获取所有帖子时,我收到org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:
异常。
根据这些SO answers,处理此问题的最佳方法是对服务方法/类使用@Transactional
注释。放置注释对我不起作用。我正在使用Wildfly服务器,Hibernate,MySQL和Java EE MVC Web框架。
如何让它发挥作用,即从用户那里获取帖子?我设法通过急切加载来实现,但出于性能原因不推荐这样做。
@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$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)
答案 0 :(得分:1)
您正在从会话边界返回未初始化的集合代理(在这种情况下由事务划分)。
您可以通过调用Hibernate.initialize(posts)
或在返回之前调用posts.size()
来初始化代理(第一种方法更清楚地描述了意图imo)。
我最常使用的另一种方法是利用DTO,这样我就不必处理分离的对象,而且还因为能够定制表示对象(以及一般的Web服务响应)。使用它们的客户的确切需求。同样,域模型(Hibernate实体)与表示逻辑分离,允许两者独立发展。
其他替代方案包括Open Session in View(反?)模式和hibernate.enable_lazy_load_no_trans
属性,但它们不够灵活,各有利弊。