it-roy-ru.com

Как исправить 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);
    }

}

Мои классы 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

54
Kleber Mota

Вам нужно либо добавить fetch=FetchType.EAGER в свои аннотации ManyToMany, чтобы автоматически возвращать дочерние объекты:

@ManyToMany(fetch = FetchType.EAGER)

Лучшим вариантом было бы реализовать весенний TransactionManager, добавив следующее в ваш весенний конфигурационный файл:

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

<tx:annotation-driven />

Затем вы можете добавить аннотацию @Transactional к вашему методу аутентификации следующим образом:

@Transactional
public Authentication authenticate(Authentication authentication)

Затем он запустит транзакцию БД на время действия метода authenticate, позволяя извлекать из базы данных любую отложенную коллекцию, как и при попытке их использования.

58
jcmwright80

Добавление следующего свойства к вашему persistence.xml может временно решить вашу проблему

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

Как сказал @ vlad-mihalcea, это антипаттерн и не решает проблему отложенной инициализации полностью, инициализируйте ассоциации перед закрытием транзакции и используйте вместо этого DTO.

21
Mohammad-Hossein Jamali

Лучший способ обработать LazyInitializationException - это использовать директиву JOIN FETCH для всех сущностей, которые вам нужно выбрать.

В любом случае, НЕ используйте следующие анти-паттерны, как это предлагается в некоторых ответах:

Иногда, проекция DTO является лучшим выбором, чем выборка объектов, и таким образом вы не получите никакого LazyInitializationException.

17
Vlad Mihalcea

У меня тоже была эта проблема, когда я занимался модульным тестированием. Очень простое решение этой проблемы - использовать аннотацию @ Transactional, которая сохраняет сеанс открытым до конца выполнения.

7
KarthikaSrinivasan

Причина в том, что когда вы используете ленивую загрузку, сессия закрывается.

Есть два решения.

  1. Не используйте ленивый груз.

    Установите lazy=false в XML или установите @OneToMany(fetch = FetchType.EAGER) в аннотации.

  2. Используйте ленивый груз.

    Установите lazy=true в XML или установите @OneToMany(fetch = FetchType.LAZY) в аннотации.

    и добавьте OpenSessionInViewFilter filter в свой web.xml

Подробно Смотрите мой пост.

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

7
saneryee

Вы можете использовать спящий ленивый инициализатор.

Ниже приведен код, который вы можете сослаться.
Здесь PPIDO - это объект данных, который я хочу получить

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

Ваш пользовательский класс AuthenticationProvider должен быть аннотирован следующим:

@Transactional

Это также обеспечит наличие сеанса гибернации.

3
Bilal Ahmed Yaseen

Прежде всего хочу сказать, что все пользователи, которые говорили о ленивых и транзакциях, были правы. Но в моем случае была небольшая разница в том, что я использовал в тесте метод @ 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()));
1
vk23

Для тех, у кого есть эта проблема с коллекцией перечислений вот как ее решить:

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

Я считаю, что вместо того, чтобы активировать выборку, имеет смысл переинициализировать вашу сущность там, где это необходимо, чтобы избежать исключения LazyInitializationException

Hibernate.initialize(your entity);
1
Pravin

Добавить аннотацию

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