복붙노트

[SPRING] Hibernate LazyInitializationException을 고치기위한 방법 : 롤 컬렉션을 늦게 초기화하지 못했습니다. 프록시를 초기화 할 수 없습니다 - 세션 없음

SPRING

Hibernate LazyInitializationException을 고치기위한 방법 : 롤 컬렉션을 늦게 초기화하지 못했습니다. 프록시를 초기화 할 수 없습니다 - 세션 없음

내 봄 프로젝트에서 사용자 지정 AuthenticationProvider에서 나는 기록 된 사용자의 권한 목록을 읽으려고하는데 다음 오류가 발생했습니다.

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)

여기에서 StackOberflow에서 다른 주제를 읽으면이 유형의 속성이 프레임 워크에서 처리되는 방식으로 인해 이러한 일이 발생하는 것으로 알고 있지만 제 경우에 대한 솔루션을 찾을 수 없습니다. 누군가 내가 잘못하고있는 것과 내가 고칠 수있는 것을 지적 할 수 있습니까?

내 Custom AuthenticationProvider의 코드는 다음과 같습니다.

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

}

My Entity 클래스는 다음과 같습니다.

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

github에서 사용할 수있는 전체 프로젝트

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

해결법

  1. ==============================

    1.ManyToMany 주석 안에 fetchType = FetchType.EAGER를 추가하여 자동으로 하위 엔티티를 끌어 오면 더 나은 옵션은 스프링 구성 파일에 다음을 추가하여 spring transactionManager를 구현하는 것입니다.

    ManyToMany 주석 안에 fetchType = FetchType.EAGER를 추가하여 자동으로 하위 엔티티를 끌어 오면 더 나은 옵션은 스프링 구성 파일에 다음을 추가하여 spring transactionManager를 구현하는 것입니다.

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    
    <tx:annotation-driven />
    

    그런 다음 @Transactional 주석을 authenticate 메소드에 다음과 같이 추가 할 수 있습니다.

    @Transactional
    public Authentication authenticate(Authentication authentication)
    

    이렇게하면 authenticate 메소드가 지속되는 동안 db 트랜잭션이 시작되어 db와 db를 사용하려고 할 때 lazy collection을 db에서 가져올 수 있습니다.

  2. ==============================

    2.persistence.xml에 다음과 같은 속성을 추가하면 문제가 일시적으로 해결 될 수 있습니다.

    persistence.xml에 다음과 같은 속성을 추가하면 문제가 일시적으로 해결 될 수 있습니다.

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

    @ vlad-mihalcea는 반 패턴이며 지연 초기화 문제를 완전히 해결하지 못했기 때문에 트랜잭션을 닫기 전에 DTO를 사용하기 전에 연결을 초기화하십시오.

  3. ==============================

    3.LazyInitializationException을 처리하는 가장 좋은 방법은 반입해야하는 모든 엔터티에 JOIN FETCH 지시문을 사용하는 것입니다.

    LazyInitializationException을 처리하는 가장 좋은 방법은 반입해야하는 모든 엔터티에 JOIN FETCH 지시문을 사용하는 것입니다.

    어쨌든 일부 답변에서 제안하는대로 다음과 같은 반 패턴을 사용하지 마십시오.

    때로는 DTO 투영이 엔티티을 가져 오는 것보다 나은 선택이며,이 방법으로는 LazyInitializationException을 얻을 수 없습니다.

  4. ==============================

    4.이유는 지연로드를 사용할 때 세션이 닫혀 있기 때문입니다.

    이유는 지연로드를 사용할 때 세션이 닫혀 있기 때문입니다.

    두 가지 해결책이 있습니다.

    세부 내 게시물을 참조하십시오.

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

  5. ==============================

    5.나는 단위 테스트를 할 때이 문제도 가지고 있었다. 이 문제에 대한 매우 간단한 해결책은 실행 종료까지 세션을 열린 상태로 유지하는 @Transactional 주석을 사용하는 것입니다.

    나는 단위 테스트를 할 때이 문제도 가지고 있었다. 이 문제에 대한 매우 간단한 해결책은 실행 종료까지 세션을 열린 상태로 유지하는 @Transactional 주석을 사용하는 것입니다.

  6. ==============================

    6.당신은 최대 절전 모드의 lazy initializer를 사용할 수있다.

    당신은 최대 절전 모드의 lazy initializer를 사용할 수있다.

    다음은 참조 할 수있는 코드입니다. 여기서 PPIDO는 검색하고자하는 데이터 객체입니다.

    Hibernate.initialize(ppiDO);
    if (ppiDO instanceof HibernateProxy) {
        ppiDO = (PolicyProductInsuredDO) ((HibernateProxy) ppiDO).getHibernateLazyInitializer()
            .getImplementation();
        ppiDO.setParentGuidObj(policyDO.getBasePlan());
        saveppiDO.add(ppiDO);
        proxyFl = true;
    }
    
  7. ==============================

    7.사용자 정의 AuthenticationProvider 클래스에는 다음과 같이 주석을 추가해야합니다.

    사용자 정의 AuthenticationProvider 클래스에는 다음과 같이 주석을 추가해야합니다.

    이렇게하면 최대 절전 모드 세션이 있는지도 확인할 수 있습니다.

  8. ==============================

    8.eager fetch를 가능하게하는 것보다 믿는다. LazyInitializationException 예외를 피할 필요가있는 곳에 엔티티를 다시 초기화하는 것이 합리적이다.

    eager fetch를 가능하게하는 것보다 믿는다. LazyInitializationException 예외를 피할 필요가있는 곳에 엔티티를 다시 초기화하는 것이 합리적이다.

    Hibernate.initialize(your entity);
    
  9. ==============================

    9.먼저 게으름과 거래에 대해 말한 모든 사용자가 옳았다 고 말하고 싶습니다. 그러나 제 경우에는 테스트에서 @Transactional 메서드의 결과를 사용한다는 점에서 약간의 차이가있었습니다. 그리고 이것은 실제 트랜잭션 밖이었습니다. 그래서이 게으른 예외가 있습니다.

    먼저 게으름과 거래에 대해 말한 모든 사용자가 옳았다 고 말하고 싶습니다. 그러나 제 경우에는 테스트에서 @Transactional 메서드의 결과를 사용한다는 점에서 약간의 차이가있었습니다. 그리고 이것은 실제 트랜잭션 밖이었습니다. 그래서이 게으른 예외가 있습니다.

    내 서비스 방법 :

    @Transactional
    User get(String uid) {};
    

    내 테스트 코드 :

    User user = userService.get("123");
    user.getActors(); //org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role
    

    내 솔루션이 같은 다른 트랜잭션 코드를 래핑하는 :

    List<Actor> actors = new ArrayList<>();
    transactionTemplate.execute((status) 
     -> actors.addAll(userService.get("123").getActors()));
    
  10. ==============================

    10.열거 형 컬렉션과 관련된 문제가있는 사람들은이를 해결하는 방법을 배웁니다.

    열거 형 컬렉션과 관련된 문제가있는 사람들은이를 해결하는 방법을 배웁니다.

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

    11.특수 효과 추가

    특수 효과 추가

    @JsonManagedReference
    

    예 :

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(name = "autorizacoes_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_autorizacoes") })
    @JsonManagedReference
    public List<AutorizacoesUsuario> getAutorizacoes() {
        return this.autorizacoes;
    }
    
  12. from https://stackoverflow.com/questions/22821695/how-to-fix-hibernate-lazyinitializationexception-failed-to-lazily-initialize-a by cc-by-sa and MIT license