1
resposta

LazyInitializationException ao usar token sem anotação EAGER

No curso anterior, quando os usuários ainda não tinham perfis, implementei a classe Usuario sem o EAGER na anotação ManyToMany:

    @ManyToMany
    private List<Perfil> perfis = new ArrayList<>();

Imaginei que esse seria o mais correto, para não trazer sempre os Perfis mesmo em casos em que não fossem necessários, e que além disso não teria problemas porque os perfis seriam buscados no Banco de Dados com um SELECT extra. No pior dos casos seria apenas uma penalidade de performance.

Para minha surpresa, agora que mudei a SecurityConfigurations para exigir roles, e os perfis estão sendo efetivamente usados para restringir acessos, passei a tomar o seguinte erro ao tentar deletar um Topico:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.ljpeixoto.forum.modelo.Usuario.perfis, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:614) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:591) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:387) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.springframework.security.authentication.AbstractAuthenticationToken.<init>(AbstractAuthenticationToken.java:58) ~[spring-security-core-5.6.2.jar:5.6.2]
    at org.springframework.security.authentication.UsernamePasswordAuthenticationToken.<init>(UsernamePasswordAuthenticationToken.java:68) ~[spring-security-core-5.6.2.jar:5.6.2]
    at com.ljpeixoto.forum.config.security.AutenticacaoViaTokenFilter.autenticarCliente(AutenticacaoViaTokenFilter.java:42) ~[classes/:na]

Tentei de várias maneiras evitar colocar o EAGER em perfis, pois mesmo que se argumente que no caso específico a melhor opção seria o EAGER mesmo, creio que essa seja uma situação costumeira - um relacionamento em que uma propriedade do tipo lista seja recuperada apenas sob demanda - mas não consegui - sempre dá essa exceção, com a mensagem de erro dizendo que não há uma session aberta. Anotei a classe AutenticacaoViaTokenFilter e até o método doFilterInternal com a anotação @Transactional, mas o erro permaneceu. No meu entendimento, se usei o @Transactional o Spring deveria abrir uma Session antes de iniciar o método, e mantê-lo aberto até o fim da execução, não ?

Qual seria a maneira correta de fazer o JPA recuperar a lista de Perfis sob demanda (ou seja, com comportamento LAZY) ?

1 resposta

Oi Leonardo,

Nesse caso é um problema em relação ao funcionamento da JPA em relação ao carregamento de relacionamentos lazy.

Tem 2 soluções possíveis: colocar o carregamento como eager na anotação @ManyToMany ou fazer uma querie planejada(carregar o usuário junto com os seus perfis utilizando o join fetch)