Cómo reparar Hibernate LazyInitializationException: no se pudo inicializar de forma perezosa una colección de roles, no se pudo inicializar el proxy – sin sesión

14 minutos de lectura

avatar de usuario
Kléber Mota

En el AuthenticationProvider personalizado de mi proyecto Spring, intento leer la lista de autoridades del usuario registrado, pero me encuentro con el siguiente error:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.horariolivre.entity.Usuario.autorizacoes, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:124)
    at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:266)
    at com.horariolivre.security.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:45)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
    at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:211)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

Al leer otros temas de aquí en StackOverflow, entiendo que esto sucede debido a la forma en que el marco maneja este tipo de atributo, pero no puedo encontrar ninguna solución para mi caso. Alguien me puede indicar lo que estoy haciendo mal y lo que puedo hacer para solucionarlo?

El código de mi Custom AuthenticationProvider es:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UsuarioHome usuario;

    public CustomAuthenticationProvider() {
        super();
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        System.out.println("CustomAuthenticationProvider.authenticate");

        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        Usuario user = usuario.findByUsername(username);

        if (user != null) {
            if(user.getSenha().equals(password)) {
                List<AutorizacoesUsuario> list = user.getAutorizacoes();

                List <String> rolesAsList = new ArrayList<String>();
                for(AutorizacoesUsuario role : list){
                    rolesAsList.add(role.getAutorizacoes().getNome());
                }

                List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
                for (String role_name : rolesAsList) {
                    authorities.add(new SimpleGrantedAuthority(role_name));
                }

                Authentication auth = new UsernamePasswordAuthenticationToken(username, password, authorities);
                return auth;
            }
            else {
                return null;
            }
        } else {
            return null;
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

Mis clases de entidad son:

UsuarioHome.java

@Entity
@Table(name = "usuario")
public class Usuario implements java.io.Serializable {

    private int id;
    private String login;
    private String senha;
    private String primeiroNome;
    private String ultimoNome;
    private List<TipoUsuario> tipoUsuarios = new ArrayList<TipoUsuario>();
    private List<AutorizacoesUsuario> autorizacoes = new ArrayList<AutorizacoesUsuario>();
    private List<DadosUsuario> dadosUsuarios = new ArrayList<DadosUsuario>();
    private ConfigHorarioLivre config;

    public Usuario() {
    }

    public Usuario(String login, String senha) {
        this.login = login;
        this.senha = senha;
    }

    public Usuario(String login, String senha, String primeiroNome, String ultimoNome, List<TipoUsuario> tipoUsuarios, List<AutorizacoesUsuario> autorizacoesUsuarios, List<DadosUsuario> dadosUsuarios, ConfigHorarioLivre config) {
        this.login = login;
        this.senha = senha;
        this.primeiroNome = primeiroNome;
        this.ultimoNome = ultimoNome;
        this.tipoUsuarios = tipoUsuarios;
        this.autorizacoes = autorizacoesUsuarios;
        this.dadosUsuarios = dadosUsuarios;
        this.config = config;
    }

    public Usuario(String login, String senha, String primeiroNome, String ultimoNome, String tipoUsuario, String[] campos) {
        this.login = login;
        this.senha = senha;
        this.primeiroNome = primeiroNome;
        this.ultimoNome = ultimoNome;
        this.tipoUsuarios.add(new TipoUsuario(this, new Tipo(tipoUsuario)));
        for(int i=0; i<campos.length; i++)
            this.dadosUsuarios.add(new DadosUsuario(this, null, campos[i]));
    }

    @Id
    @Column(name = "id", unique = true, nullable = false)
    @GeneratedValue(strategy=GenerationType.AUTO)
    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(name = "login", nullable = false, length = 16)
    public String getLogin() {
        return this.login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    @Column(name = "senha", nullable = false)
    public String getSenha() {
        return this.senha;
    }

    public void setSenha(String senha) {
        this.senha = senha;
    }

    @Column(name = "primeiro_nome", length = 32)
    public String getPrimeiroNome() {
        return this.primeiroNome;
    }

    public void setPrimeiroNome(String primeiroNome) {
        this.primeiroNome = primeiroNome;
    }

    @Column(name = "ultimo_nome", length = 32)
    public String getUltimoNome() {
        return this.ultimoNome;
    }

    public void setUltimoNome(String ultimoNome) {
        this.ultimoNome = ultimoNome;
    }

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(name = "tipo_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_tipo") })
    @LazyCollection(LazyCollectionOption.TRUE)
    public List<TipoUsuario> getTipoUsuarios() {
        return this.tipoUsuarios;
    }

    public void setTipoUsuarios(List<TipoUsuario> tipoUsuarios) {
        this.tipoUsuarios = tipoUsuarios;
    }

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(name = "autorizacoes_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_autorizacoes") })
    @LazyCollection(LazyCollectionOption.TRUE)
    public List<AutorizacoesUsuario> getAutorizacoes() {
        return this.autorizacoes;
    }

    public void setAutorizacoes(List<AutorizacoesUsuario> autorizacoes) {
        this.autorizacoes = autorizacoes;
    }

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(name = "dados_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_dados") })
    @LazyCollection(LazyCollectionOption.TRUE)
    public List<DadosUsuario> getDadosUsuarios() {
        return this.dadosUsuarios;
    }

    public void setDadosUsuarios(List<DadosUsuario> dadosUsuarios) {
        this.dadosUsuarios = dadosUsuarios;
    }

    @OneToOne
    @JoinColumn(name="fk_config")
    public ConfigHorarioLivre getConfig() {
        return config;
    }

    public void setConfig(ConfigHorarioLivre config) {
        this.config = config;
    }
}

AutorizacoesUsuario.java

@Entity
@Table(name = "autorizacoes_usuario", uniqueConstraints = @UniqueConstraint(columnNames = "id"))
public class AutorizacoesUsuario implements java.io.Serializable {

    private int id;
    private Usuario usuario;
    private Autorizacoes autorizacoes;

    public AutorizacoesUsuario() {
    }

    public AutorizacoesUsuario(Usuario usuario, Autorizacoes autorizacoes) {
        this.usuario = usuario;
        this.autorizacoes = autorizacoes;
    }

    @Id
    @Column(name = "id", unique = true, nullable = false)
    @GeneratedValue(strategy=GenerationType.AUTO)
    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @OneToOne
    @JoinColumn(name = "fk_usuario", nullable = false, insertable = false, updatable = false)
    public Usuario getUsuario() {
        return this.usuario;
    }

    public void setUsuario(Usuario usuario) {
        this.usuario = usuario;
    }

    @OneToOne
    @JoinColumn(name = "fk_autorizacoes", nullable = false, insertable = false, updatable = false)
    public Autorizacoes getAutorizacoes() {
        return this.autorizacoes;
    }

    public void setAutorizacoes(Autorizacoes autorizacoes) {
        this.autorizacoes = autorizacoes;
    }

}

Autorizacoes.java

@Entity
@Table(name = "autorizacoes")
public class Autorizacoes implements java.io.Serializable {

    private int id;
    private String nome;
    private String descricao;

    public Autorizacoes() {
    }

    public Autorizacoes(String nome) {
        this.nome = nome;
    }

    public Autorizacoes(String nome, String descricao) {
        this.nome = nome;
        this.descricao = descricao;
    }

    @Id
    @Column(name = "id", unique = true, nullable = false)
    @GeneratedValue(strategy=GenerationType.AUTO)
    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(name = "nome", nullable = false, length = 16)
    public String getNome() {
        return this.nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    @Column(name = "descricao", length = 140)
    public String getDescricao() {
        return this.descricao;
    }

    public void setDescricao(String descricao) {
        this.descricao = descricao;
    }
}

Proyecto completo disponible en github

–> https://github.com/klebermo/webapp_horario_livre

  • Busque sus autoridades con entusiasmo o use un OpenSessionInViewFilter.

    – Bart

    02/04/2014 a las 19:59

  • es exactamente lo que intento ver cómo hacerlo. Lo que he probado ha sido esto: List autoridad = usuario.getAutorizacoes()dentro de la misma función de la asignación de UsernamePasswordAuthenticationToken, pero aún no funciona.

    – Kléber Mota

    02/04/2014 a las 20:13

  • @ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)

    – Bart

    02/04/2014 a las 20:16

  • Ok, lo intento, pero todavía no funciona. Mi clase de entidad actualizada: github.com/klebermo/webapp_horario_livre/blob/master/src/com/…Mi proveedor de autenticación actual: github.com/klebermo/webapp_horario_livre/blob/master/src/com/…

    – Kléber Mota

    02/04/2014 a las 20:31

  • Posible duplicado de Cómo resolver la excepción de Hibernate “no se pudo inicializar perezosamente una colección de roles”

    – Martín Schröder

    8 de agosto de 2018 a las 9:06

avatar de usuario
jcmwright80

Necesitas agregar fetch=FetchType.EAGER dentro de sus anotaciones ManyToMany para retirar automáticamente las entidades secundarias:

@ManyToMany(fetch = FetchType.EAGER)

Una mejor opción sería implementar un administrador de transacciones de primavera agregando lo siguiente a su archivo de configuración de primavera:

<bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:annotation-driven />

Luego puede agregar una anotación @Transactional a su método de autenticación de la siguiente manera:

@Transactional
public Authentication authenticate(Authentication authentication)

Esto luego iniciará una transacción de base de datos durante la duración del método de autenticación, lo que permitirá recuperar cualquier colección perezosa de la base de datos cuando intente usarla.

  • En realidad, tengo el administrador de transacciones configurado en mi aplicación y lo uso en mis clases de DAO. Si trato de usar en el método de autenticación de AuthenticationProvider como sugiere, aparece un error Provocado por: java.lang.IllegalArgumentException: no se puede establecer el campo com.horariolivre.security.CustomAuthenticationProvider com.horariolivre.security.SecurityConfig.authenticationProvider en $Proxy36. Recibo el mismo error si uso add fetchType=FetchType.EAGER dentro de mi anotación ManyToMany (y puedo usar esto en un solo atributo; tengo tres del mismo tipo en mi clase de Entidad Usuario).

    – Kléber Mota

    02/04/2014 a las 20:57

  • Bueno, debe recorrer las entidades secundarias que desea usar dentro de una Transacción para evitar la LazyInitializationException. Dado que su anotación transaccional está en el nivel de dao en un método genérico, probablemente no querrá hacer eso allí, por lo que deberá implementar una clase de servicio frente al dao que tenga los límites @Transactional desde dentro de los cuales puede recorrer el entidades secundarias deseadas

    – jcmwright80

    02/04/2014 a las 21:32

  • Protip para alguien que se encuentre con esto en el futuro; @Transaction debe estar en un método público. Si no es así, esto no funcionará. Puede haber o no advertencias.

    – Nicolás

    20 de enero de 2020 a las 15:03

  • usó el tipo de búsqueda y funcionó perfectamente, pregúntese cuál es la diferencia en usar la búsqueda ansiosa para la contraparte @transactional

    – Austine Gwa

    4 de septiembre de 2020 a las 0:58

  • @AustineGwa La principal diferencia es agregar el fetchType ansioso a la unión, lo que significará que la lista de entidades secundarias siempre se retirará de la base de datos cada vez que se cargue la entidad principal, por lo que existe un impacto potencial en el rendimiento si hay áreas de funcionalidad que solo requieren los datos de la entidad principal, por lo que el uso de transacciones y la carga diferida le brinda más control sobre la cantidad de datos que se retiran, pero depende completamente de su aplicación y casos de uso en cuanto a qué enfoque es el adecuado para usted.

    – jcmwright80

    4 de diciembre de 2020 a las 15:51


avatar de usuario
Vlad Mihalcea

La mejor manera de manejar el LazyInitializationException es usar el JOIN FETCH directiva para todas las entidades que necesita traer.

De todos modos, NO use los siguientes antipatrones como lo sugieren algunas de las respuestas:

A veces, un proyección DTO es una mejor opción que obtener entidades, y de esta manera, no obtendrá ninguna LazyInitializationException.

  • fetch join es equivalente a la búsqueda ansiosa. Lo cual puede no ser siempre factible ni eficiente. Además, la forma habitual de obtener un objeto no es a través de consultas jpql. El hecho de que la vista de sesión abierta sea un antipatrón es una posibilidad remota y, sinceramente, no estoy de acuerdo. Debe usarse con precaución, obviamente, pero hay muchos casos de uso perfectamente buenos que se benefician de él.

    – fer.marino

    7 de junio de 2017 a las 8:39

  • No es NO. La sesión abierta en vista es cortar a tajos y una señal de que las entidades se obtienen incluso para proyecciones de solo lectura. No existe tal cosa como un muchos casos de uso perfectamente buenos que se benefician de él, no importa cuánto intente justificarlo. No hay excusa para buscar más datos que tú De Verdad necesidad, así como no hay excusa para filtrar la obtención de datos fuera de los límites de la capa de servicio transaccional.

    – Vlad Mihalcea

    7 de junio de 2017 a las 9:01


  • HOLA Vlad, ¿Puedes explicar por qué FETCH JOIN no es equivalente a carga ansiosa? Estoy leyendo este artículo: blog.arnoldgalovics.com/2017/02/27/… . Y dice “Una mejor idea es cargar la relación en el momento en que está cargando la entidad principal, la empresa, la entidad. Esto se puede hacer con Fetch Join”. Así que es una carga ansiosa. ¿no es así?

    – Adicto

    8 de enero de 2018 a las 3:13

  • Liderar con entusiasmo significa agregar FetchType.EAGER a sus asociaciones. UNIRSE A FETCH es para FetchType.LAZY asociaciones que deben buscarse ansiosamente en el momento de la consulta.

    – Vlad Mihalcea

    8 de enero de 2018 a las 6:24

avatar de usuario
Jamalí

Agregar la siguiente propiedad a su persistencia.xml puede resolver su problema temporalmente

<property name="hibernate.enable_lazy_load_no_trans" value="true" />

Como dijo @vlad-mihalcea, es un antipatrón y no resuelve completamente el problema de inicialización diferida, inicialice sus asociaciones antes de cerrar la transacción y use DTO en su lugar.

  • Un experto en Hibernate lo considera un antipatrón stackoverflow.com/users/1025118/vlad-mihalcea

    – Zona

    21 de marzo a las 14:15

Yo también tuve este problema cuando estaba haciendo pruebas unitarias. Una solución muy simple a este problema es usar @Transaccional anotación que mantiene la sesión abierta hasta el final de la ejecución.

avatar de usuario
saneryee

La razón es que cuando usa la carga diferida, la sesión se cierra.

Hay dos soluciones.

  1. No uses carga diferida.

    Establecer lazy=false en XML o Conjunto @OneToMany(fetch = FetchType.EAGER) En anotación.

  2. Usa carga diferida.

    Establecer lazy=true en XML o Conjunto @OneToMany(fetch = FetchType.LAZY) En anotación.

    y añadir OpenSessionInViewFilter filter en tus web.xml

Detalle Ver mi publicación.

https://stackoverflow.com/a/27286187/1808417

  • OpenSessionInViewFilter también es un antipatrón. También sugiero nunca establecer una asignación a EAGER, ya que habrá muchos casos en los que no necesitará esos datos en la colección EAGER y obtendrá muchos más datos de los que necesitan esos casos de uso y reducirá en gran medida su rendimiento. Mantenga todas las asignaciones LAZY y agregue búsquedas de combinación a sus Consultas en su lugar.

    – bytor99999

    29 de septiembre de 2016 a las 8:14

  • “y agregue búsquedas de unión a sus Consultas en su lugar”. ¿Qué significa eso?

    – Daniel Metner

    11 de noviembre de 2021 a las 8:25

avatar de usuario
Bilal Ahmed Yaseen

Su clase Custom AuthenticationProvider debe anotarse con lo siguiente:

@Transaccional

Esto asegurará la presencia de la sesión de hibernación allí también.

  • OpenSessionInViewFilter también es un antipatrón. También sugiero nunca establecer una asignación a EAGER, ya que habrá muchos casos en los que no necesitará esos datos en la colección EAGER y obtendrá muchos más datos de los que necesitan esos casos de uso y reducirá en gran medida su rendimiento. Mantenga todas las asignaciones LAZY y agregue búsquedas de combinación a sus Consultas en su lugar.

    – bytor99999

    29 de septiembre de 2016 a las 8:14

  • “y agregue búsquedas de unión a sus Consultas en su lugar”. ¿Qué significa eso?

    – Daniel Metner

    11 de noviembre de 2021 a las 8:25

Para los que tienen este problema con colección de enumeraciones aquí está cómo resolverlo:

@Enumerated(EnumType.STRING)
@Column(name = "OPTION")
@CollectionTable(name = "MY_ENTITY_MY_OPTION")
@ElementCollection(targetClass = MyOptionEnum.class, fetch = EAGER)
Collection<MyOptionEnum> options;

  • Esto funciona para mí. También probé la opción de agregar @Transactional y también funciona. Pero elijo esta opción.

    -Rick Dana

    3 de abril de 2020 a las 8:44

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad