Glassfish到wildfly迁移(从Eclipse链接到Hibernate):LazyInitializationException

时间:2014-06-25 13:03:05

标签: hibernate jpa eclipselink

我有一个在Glassfish 4上正常运行的网络应用程序,我正在将它迁移到Wildfly。我明白了:

15:19:28,802 ERROR [io.undertow.request] (default task-33) UT005023: Exception handling request to /dvd_store/movie.xhtml: javax.servlet.ServletException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: dvd_store.entities.Movie.categories, could not initialize proxy - no Session
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:659) [jboss-jsf-api_2.2_spec-2.2.6.jar:2.2.6]
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:113) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:56) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:240) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:177) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:727) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_25]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_25]
    at java.lang.Thread.run(Thread.java:724) [rt.jar:1.7.0_25]
Caused by: javax.faces.el.EvaluationException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: dvd_store.entities.Movie.categories, could not initialize proxy - no Session
    at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:101) [jboss-jsf-api_2.2_spec-2.2.6.jar:2.2.6]
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102) [jsf-impl-2.2.6-jbossorg-4.jar:]
    at javax.faces.component.UIViewAction.broadcast(UIViewAction.java:562) [jboss-jsf-api_2.2_spec-2.2.6.jar:2.2.6]
    at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790) [jboss-jsf-api_2.2_spec-2.2.6.jar:2.2.6]
    at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282) [jboss-jsf-api_2.2_spec-2.2.6.jar:2.2.6]
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81) [jsf-impl-2.2.6-jbossorg-4.jar:]
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.2.6-jbossorg-4.jar:]
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198) [jsf-impl-2.2.6-jbossorg-4.jar:]
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646) [jboss-jsf-api_2.2_spec-2.2.6.jar:2.2.6]
    ... 26 more
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: dvd_store.entities.Movie.categories, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:575) [hibernate-core-4.3.5.Final.jar:4.3.5.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:214) [hibernate-core-4.3.5.Final.jar:4.3.5.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:155) [hibernate-core-4.3.5.Final.jar:4.3.5.Final]
    at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:278) [hibernate-core-4.3.5.Final.jar:4.3.5.Final]
    at dvd_store.controllers.MovieDisplayController.init(MovieDisplayController.java:49) [classes:]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.7.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) [rt.jar:1.7.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.7.0_25]
    at java.lang.reflect.Method.invoke(Method.java:606) [rt.jar:1.7.0_25]
    at com.sun.el.parser.AstValue.invoke(AstValue.java:275) [javax.el-3.0.0.jar:]
    at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:304) [javax.el-3.0.0.jar:]
    at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
    at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
    at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
    at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50) [weld-core-impl-2.1.2.Final.jar:2014-01-09 09:23]
    at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105) [jsf-impl-2.2.6-jbossorg-4.jar:]
    at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87) [jboss-jsf-api_2.2_spec-2.2.6.jar:2.2.6]
    ... 34 more

movie.getCategories().size();下面的<{1}}行抛出异常

@ManagedBean
@ViewScoped
public class MovieDisplayController implements Serializable {

    @EJB
    private MovieService ms;
    private Movie movie;
    private int id;
    @ManagedProperty(value = "#{cartController}")
    private CartController cartController;

    // @PostConstruct // init() called in a f:viewParam
    public void init() {
        if (getId() == 0) {
            String message = "Bad request. Please use a link from within the "
                + "system.";
            msgError(message);
            return;
        }
        movie = ms.find(getId());
        if (movie == null) {
            String message = "Bad request. Unknown movie.";
            msgError(message);
            return;
        }
        // hack to load the categories TODO
        // see: https://community.oracle.com/thread/173733
        movie.getCategories().size(); // MovieDisplayController.java:49
        movie.getMoviesHasCrews().size();
        sessionPut("movie", movie);
    }
    //...
}

其中:

@Entity
@Table(name = "movies")
public class Movie implements Serializable {

    // bi-directional many-to-many association to Category
    @ManyToMany
    @JoinTable(name = "movies_has_categories", joinColumns = { @JoinColumn(
            name = "movies_idmovie") }, inverseJoinColumns = { @JoinColumn(
            name = "categories_idcategory") })
    @NotEmpty(message = "Please enter categories for the movie")
    private List<Category> categories;

    public List<Category> getCategories() {
        return this.categories;
    }
    //...
}

我是新来的休眠而且更喜欢这个。我对解决这个问题的方法很感兴趣,也适用于vanilla JPA(eclipselink)。我不会在我的项目中导入任何hiberante类(除了hibernate-validator @NotEmpty 1}})。这是我的pom:

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>gr.uoa.gr.regas.jee.dvdstore</groupId>
    <artifactId>dvd_store</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>4.3.0.Final</version>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>
    <build>
        <sourceDirectory>src</sourceDirectory>
        <resources>
            <resource>
                <directory>src</directory>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <warSourceDirectory>WebContent</warSourceDirectory>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4 个答案:

答案 0 :(得分:1)

让关系变得热切,将导致所有JPA提供商提前阅读。如果您不确定是否始终需要获取的关系,则可能会在关系上进行使用JOIN FETCH的查询,以便在应用程序知道需要使用该关系时使用该查询。通过这种方式,您可以获得延迟加载的性能优势,并且在需要时可以在关闭的上下文外工作时仍然可以使用该模型。

查询中的联接提取强制使用单个查询读取它,而只是让它急切允许提供程序选择,使用自定义注释指定应如何读取它以实现效率目的。有关EclipseLink&#39}的解决方案,请参阅http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_joinfetch.htm,而Hibernate有@Fetch注释。

对于使用join的JPQL查询的示例可能是:

Query query = em.createQuery("SELECT movie FROM Movie movie LEFT JOIN FETCH movie.Categories LEFT JOIN FETCH movie.moviesHasCrews WHERE movie.id = :id");
query.setParameter("id", id);
return query.getSingleResult();

虽然使用命名查询可能会更好。

答案 1 :(得分:1)

实际上,size()hack或prefetching应该进入你的ejb方法,为两个提供者工作。当事务边界以find方法结束时,它会关闭entityManager并且不再附加您的实体。

一个解决方案:

使用布尔预取标志在MovieService中使用

find()方法。

public Movie find(long id, boolean prefetch) {

  Movie m = // normal find.
  if (prefetch) {
    m.getCategories().size(); 
    // other prefetching like that 
  }
  return m;
}

其他解决方案:急切加载。

答案 2 :(得分:1)

当工作应用程序从Glassfish迁移到Wildfly时会发生此问题。不幸的是,与EclipseLink不同,Wildfly的默认Hibernate JPA实现不允许您在关闭上下文后获取延迟关系。

许多解决方案建议使用热切的提取进行重构,但这不是一个好的解决方案,因为急切的提取会增加响应时间,并且通过重构数据库访问将风险引入到工作的应用程序中。

而是将Hibernate替换为EclipseLink作为Wildfly中的JPA持久性提供程序。 This library实用程序类将EclipseLink集成到Wildfly和the instructions are here

您需要做的是将 eclipselink-2.6.0.jar (或您正在使用的任何版本)复制到 [WILDFLY_HOME] / modules / system / layers / base / org / eclipse / persistence / main 然后在同一文件夹中编辑 module.xml 以包含JAR:

<resources>
   <resource-root path="jipijapa-eclipselink-1.0.1.Final.jar"/>
   <resource-root path="eclipselink-2.6.0.jar">           
      <filter>
         <exclude path="javax/**" />
      </filter>
   </resource-root>
</resources>

不要忘记重启服务器。

答案 3 :(得分:0)

热切的注释奏效了(我放弃了size()次来电):

+import javax.persistence.FetchType;
#...
@@ -74,14 +75,15 @@ public class Movie implements Serializable {
-   @ManyToMany
+   @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "movies_has_categories", joinColumns = { @JoinColumn(
        name = "categories_idcategory") })
    @NotEmpty(message = "Please enter categories for the movie")
    private List<Category> categories;
#...
-   @OneToMany(mappedBy = "movy", cascade = CascadeType.PERSIST)
+   @OneToMany(mappedBy = "movy", cascade = CascadeType.PERSIST,
+           fetch = FetchType.EAGER)
    private List<MoviesHasCrew> moviesHasCrews;

我最终使用了这个

@Stateless
public class MovieService {

    @PersistenceContext
    private EntityManager em;

    public Movie findWithCrewAndCategories(int id) {
        Movie find = em.find(Movie.class, id);
        if (find != null) {
            find.getCategories().size();
            final List<MoviesHasCrew> moviesHasCrews = find.getMoviesHasCrews();
            if (moviesHasCrews != null) {
                moviesHasCrews.size();
            }
        }
        return find;
    }
}

movie = ms.findWithCrewAndCategories(getId());一样。

createQuery方法并没有真正起作用(不得不首先调整语句,不确定我做对了)

public Movie findWithCrewAndCategoriesQuery(int id) {
    // BEFORE adding FETCH (as in LEFT JOIN __FETCH__):
    // javax.servlet.ServletException:
    // org.hibernate.LazyInitializationException: failed to lazily
    // initialize a collection of role: dvd_store.entities.Movie.categories,
    // could not initialize proxy - no Session
    // AFTER adding FETCH (as in LEFT JOIN __FETCH__):
    // javax.servlet.ServletException: javax.ejb.EJBException:
    // javax.persistence.PersistenceException:
    // org.hibernate.loader.MultipleBagFetchException: cannot simultaneously
    // fetch multiple bags
    Query query = em.createQuery("SELECT movie FROM Movie movie"
        + " LEFT JOIN FETCH movie.categories"
        + " LEFT JOIN FETCH movie.moviesHasCrews"
        + " WHERE movie.idmovie = :id");
    query.setParameter("id", id);
    return (Movie) query.getSingleResult();
}