Failed to lazily initialize a collection of role: com.companyname.cine.domain.Peli.actors, could not initialize proxy - no Session

0

I'm starting with Spring and I've come across this common mistake. After reading a lot and trying many of the suggested solutions (for loops for initializing, using transactional etc) in StackOverflow and other sites I have been unable to fix it.

The objects involved are an Actor object that has an @manytomany relationship with a Peli object (intermediate table id_actor / id_peli). In addition there is a relation @onetomany between Peli and Genre (id_genero in the table pelis).

The screen in question is a form where it is about loading a list with all the actors.

Here is the relevant code:

Actor.java

package com.companyname.cine.domain;

import java.io.Serializable;
import java.util.List;
import javax.persistence.*;

@Entity
@Table(name = "actores")
public class Actor implements Serializable {

    private Integer id;
    private String nombre;
    private String biografia;
    private List<Peli> pelis; 

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Integer getId() {
        return id;
    }

//otros setters y getters

    @ManyToMany(mappedBy = "actores")
    public List<Peli> getPelis() {
        return pelis;
    }
}

Peli.java

package com.companyname.cine.domain;

import java.io.Serializable;
import java.util.List;

import javax.persistence.*;

@Entity
@Table(name = "pelis")
public class Peli implements Serializable {

    private Integer id;
    private String titulo;
    private String argumento;
    private Integer id_genero;
    private List<Actor> actores;

    public Peli(String titulo, String argumento, Integer id_genero) {
        //constructor
    }

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Integer getId() {
        return id;
    }

    //otros setters y getters, hashcode, equals, toString etc

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "actores_pelis", joinColumns = @JoinColumn(name = "id_actor", referencedColumnName = "id"), 
            inverseJoinColumns = @JoinColumn(name = "id_peli", referencedColumnName = "id"))
    public List<Actor> getActores() {
        return actores;
    }
}

ActorDaoImpl.java

package com.companyname.cine.repository;

import java.util.List;
import javax.persistence.*;
import org.hibernate.Hibernate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.companyname.cine.domain.*;

@Repository
public class ActorDaoImpl implements ActorDao {

    private EntityManager em = null;

    public EntityManager getEntityManager() {
        return em;
    }

    @PersistenceContext
    public void setEntityManager(EntityManager em) {
        this.em = em;
    }

    @SuppressWarnings("unchecked")
    @Transactional
    public List<Actor> findByPeli(int id_peli) {
        // TODO Auto-generated method stub
        List<Actor> actores = em.createQuery("select distinct a from Peli p join p.actores a where p.id=:id")
            .setParameter("id", id_peli).getResultList();
        //intento de inicializar la coleccion       
        for (Actor actor : actores) {       
            for (Peli peli : actor.getPelis()) {
                peli.getId();
            }
        }
        return actores;
    }

    @SuppressWarnings("unchecked")
    @Transactional //<- intento de mantener la sesion abierta para no perder el acceso al objeto
    public List<Actor> findAll() {
        List<Actor> actores = em.createQuery("select a from Actor a order by a.id").getResultList();
        //intento de inicializar la coleccion   
        for (Actor actor : actores) {
            for (Peli peli : actor.getPelis()) {
                peli.getId();
            }
        }
        return actores;
    }

//otros métodos (save, update, delete, findById, etc etc

    }
}

PeliDaoImpl.java

package com.companyname.cine.repository;

import java.util.List;
import javax.persistence.*;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.companyname.cine.domain.*;

@Repository
public class PeliDaoImpl implements PeliDao {
    private EntityManager em;

    public EntityManager getEntityManager() {
    return em;
    }

    @PersistenceContext
    public void setEntityManager(EntityManager em) {
    this.em = em;
    }

    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    public List<Peli> findAll() {
        List<Peli> pelis = em.createQuery("select p from Peli p order by p.id").getResultList();
        for (Peli peli : pelis) {
            for (Actor actor : peli.getActores()) {
                actor.getId();
            }
        }
        return pelis;
    }

    @Transactional(readOnly = true)
    public Peli findById(int id) {
        return em.find(Peli.class, id);
    }
//métodos save, update, delete
}

ActorServiceImpl.java

package com.companyname.cine.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.companyname.cine.domain.Actor;
import com.companyname.cine.repository.ActorDao;

@Service("actorService")
public class ActorServiceImpl implements ActorService {

    ActorDao actorDao;

    @Autowired
    public void setActorDao(ActorDao actorDao) {
        this.actorDao = actorDao;
    }

    public Actor findById(int id) {
        return actorDao.findById(id);
    }

    public List<Actor> findByPeli(int id){
            List<Actor> actores = actorDao.findByPeli(id);
        return actores;
    }

    public List<Actor> findAll() {
            List<Actor> resultado = actorDao.findAll();         
        return resultado;
    }

//metodos save, update, delete
}

PeliServiceImpl.java

package com.companyname.cine.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.companyname.cine.domain.Peli;
import com.companyname.cine.repository.PeliDao;

@Service("peliService")
public class PeliServiceImpl implements PeliService {

    PeliDao peliDao;

    @Autowired
    public void setPeliDao(PeliDao peliDao) {
        this.peliDao = peliDao;
    }

    public Peli findById(int id) {
        return peliDao.findById(id);
    }

    public List<Peli> findAll() {
        return peliDao.findAll();
    }

//metodos save, update, delete

}

PeliController.java

package com.companyname.cine.web;

import java.util.List;
import org.springframework.*;


import com.companyname.cine.domain.*;
import com.companyname.cine.service.*;

@Controller("/peli")
public class PeliController {

    @Autowired
    private PeliService peliService;

    @Autowired
    private GeneroService generoService;

    @Autowired
    private ActorService actorService;

    // Este es el método donde se produce el problema
    @RequestMapping(value = "/peli/{id}/update", method = RequestMethod.GET)
    @Transactional
    public String showUpdatePeliForm(@PathVariable("id") int id, Model model) {

        Peli peli = peliService.findById(id);
        model.addAttribute("peliform", peli);
        model.addAttribute("listaGeneros", generoService.findAll());

        //++++++++++++++++++++++++++
        //lo pongo así para facilitar el debug, pero el problema es al renderizar listaActores en la template
        List <Actor> listaActores = actorService.findAll();
        model.addAttribute("listaActores",listaActores);
        //++++++++++++++++++++++++++

        List <Actor> listaActoresEnPeli = actorService.findByPeli(id);
        model.addAttribute("listaActoresEnPeli", listaActoresEnPeli);

        return "pelis/peliform";
    }   

  //metodos de save, delete y update, mostrar formulario de alta, etc

}

Template pelis / peliform summarized

<%@ include file="/WEB-INF/views/include.jsp"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<html>
<head>
<title><fmt:message key="title" /></title>
<style>
.error {
    color: red;
}
</style>
</head>
<body>
    <h1>
        <fmt:message key="editpeli.heading" />
    </h1>
    <form:form method="post" modelAttribute="peliform"
        action="/cine/pelis.htm">
        <table>
            <tr>
                <td>Seleccionar más actores: <form:select multiple="true" path="actores">                       
                        <form:options items="${listaActores}" itemLabel="nombre"
                            itemValue="id"></form:options>
                    </form:select></td>
            </tr> 

        </table>
        <br>
        <form:hidden path="id" />
        <input type="submit" value="Execute">
    </form:form>
    <a href="<c:url value="../../pelis.htm"/>">Home</a>
</body>
</html>

Application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">


    <!-- holding properties for database connectivity / -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!-- enabling annotation driven configuration / -->
    <context:annotation-config />

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter">
        <property name="loadTimeWeaver">
            <bean
                class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
        </property>
        <property name="persistenceUnitName" value="cinePU"></property>
    </bean>

    <bean id="jpaAdapter"
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
        p:database="${jpa.database}" p:showSql="${jpa.showSql}" />

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
        p:entityManagerFactory-ref="entityManagerFactory" />

    <tx:annotation-driven transaction-manager="transactionManager" />

    <!-- Scans the classpath of this application for @Components to deploy as 
        beans -->
    <context:component-scan base-package="com.companyname.cine.repository,com.companyname.cine.service, com.companyname.cine.validator" />


</beans>

Let's see if someone can guide me a bit to tell me what I'm wrong about or what I'm forgetting.

Thanks in advance!

    
asked by Sergio 24.04.2017 в 14:50
source

1 answer

0

As Luiggi Mendoza says, moving the @transactional should work.

In my projects this annotation I put it above the @Services in the services classes in your case it would be this way:

 package com.companyname.cine.service;

    import java.util.List;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import com.companyname.cine.domain.Actor;
    import com.companyname.cine.repository.ActorDao;
    @Transactional
    @Service("actorService")
    public class ActorServiceImpl implements ActorService {

        ActorDao actorDao;
    ...

Instead of putting it on the repository methods. This is because the collections are loaded in a lazy manner. Another option is to put the FetchType.Eager in the domain class, in the manyToMany:

@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
    
answered by 16.05.2017 в 13:26