2

I had a web app that run fine on Glassfish 4 and I am migrating it to Wildfly. I get :

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

The exception is thrown at the movie.getCategories().size(); line below

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

where:

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

I am new to hibernate and rather buffled by this. I'd be interested in a way to solve this that is also applicable to vanilla JPA (eclipselink). I do not import any hiberante classes in my project (apart from hibernate-validator for @NotEmpty). Here is my 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>
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
  • 1
    I believe the issue is that if the ms.find() method is closing the EntityManager, Hibernate does not allow fetching lazy relationships once the context is closed while EclipseLink does. If so, then you will need to trigger or prefetch the relationship before closing the EntityManager - make the relationship eager is one way to do this. – Chris Jun 25 '14 at 14:25
  • @Chris: thanks a lot - could you provide some code that would be valid for both eclipse link and hibernate (with no extra hibernate imports) ? Maybe completely dispose of this hack and get rid of size() call to load the categories... Not sure about EAGER's implications – Mr_and_Mrs_D Jun 25 '14 at 14:30
  • Since EclipseLink doesn't have the restriction, you might want to look at hibernate specific options rather than forcing it to be triggered generally for all providers. Unfortunately I can't help there as I'm really only familiar with EclipseLink - I just thought I'd help identify the problem and point you to possible solutions. Sorry. Find does allow hints to be passed in, so hibernate might have an equivalent to http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Query_Hints#Join_Fetch – Chris Jun 25 '14 at 14:37
  • @Chris: I am not satisfied with the `size()` hack - so any way this can be done in eclipse link in a way that is also hibernate safe ? is this eager annotation enough for both ? Should I add it to `private List categories;` ? – Mr_and_Mrs_D Jun 25 '14 at 14:46

4 Answers4

1

Making the relationship as eager will cause it to be read-in upfront by all JPA providers. If you are unsure you will always need the relationship fetched, you might make a query that uses JOIN FETCH over the relationship so your application can use that instead when it knows it will need to use the relationship. This way you get the performance benefit of lazy loading and can still have the model usable when working outside a closed context when needed.

A join fetch in the query forces it to be read in using a single query, while just making it eager allows the provider to pick, with custom annotations to specify how it should be read in for efficiancy purposes. See http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_joinfetch.htm for EclipseLink's solution while Hibernate has a @Fetch annotation.

For an example of a JPQL query using joining might be:

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();

Though using a named query might be better.

Chris
  • 20,138
  • 2
  • 29
  • 43
  • Thanks - could you add some code (the important part is I do not want any hibernate imports) ? Also - is the way I was doing it a (bad) hack ? – Mr_and_Mrs_D Jun 25 '14 at 15:24
  • No, the way you are doing it was not a bad hack, but exploits an implementation detail - accessing the collection forces it to be fetched. Using join fetch in a query allows you to control how and when relationships are brought in, but only your access, usage patterns and how they interact with the provider can tell you what is better over all. Both might be equal if performance is not a consideration. – Chris Jun 25 '14 at 17:43
  • The `createQuery` did not work - got again LazyInitializationException - see my answer. The eager annotation did work - still I ended up adding the code in a method – Mr_and_Mrs_D Jun 26 '14 at 20:07
  • My mistake, I missed the FETCH keyword from the JPQL Query, so it was only performing a join, not the fetch. – Chris Jun 27 '14 at 02:37
  • Added FETCH and now I get : `javax.servlet.ServletException: javax.ejb.EJBException: javax.persistence.PersistenceException: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags` - another weird exception name :) – Mr_and_Mrs_D Jun 27 '14 at 08:55
  • 1
    A hibernate limitation detailed here http://stackoverflow.com/questions/13334831/multiplebagfetchexception-thrown – Chris Jun 27 '14 at 13:11
  • [Part II](http://stackoverflow.com/questions/24489279/npe-when-first-adding-to-a-collection-in-hibernate-but-not-in-eclipse-link) :D – Mr_and_Mrs_D Jun 30 '14 at 11:43
1

Actually the size() hack or prefetching should go inside your ejb method, to work for both providers. As the transaction boundary ends with the find method it closes the entityManager and your entity is not longer attached.

One solution:

find() method in MovieService with a boolean prefetch flag.

public Movie find(long id, boolean prefetch) {

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

Other solution: Make it eagerly loaded.

rangalo
  • 5,448
  • 8
  • 46
  • 66
1

This problem occurs when working applications are migrated off Glassfish onto Wildfly. Unfortunately, Wildfly's default Hibernate JPA implementation does not allow you to fetch a lazy relationship after the context has been closed, unlike EclipseLink.

Many solutions advise refactoring with eager fetches, but this is not a good solution because eager fetches increase response times and you introduce risk into a working application by refactoring the database access.

Rather replace Hibernate with EclipseLink as the JPA Persistence Provider in Wildfly. This library of utility classes integrates EclipseLink into Wildfly and the instructions are here.

What you have to do is copy eclipselink-2.6.0.jar (or whatever version you are using) into [WILDFLY_HOME]/modules/system/layers/base/org/eclipse/persistence/main and then edit module.xml in the same folder to include the 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>

Don't forget to reboot the server.

GeraldScott
  • 481
  • 5
  • 8
0

Eager annotation worked (I dropped the size() calls):

+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;

I ended up using this though

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

As in movie = ms.findWithCrewAndCategories(getId());.

The createQuery approach did not really work (had to tweak the statement a bit first, not sure I got it right)

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();
}
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361