Hibernate LazyInitializationExceptionを修正する方法:ロールのコレクションを遅延初期化できませんでした、プロキシを初期化できませんでした-セッションがありません


108

Springプロジェクトのカスタム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)

StackOverflowでここから他のトピックを読んで、このタイプの属性がフレームワークによって処理される方法が原因でこれが発生することを理解していますが、私の場合の解決策を見つけることができません。誰かが私が間違っていることとそれを修正するために私ができることを指摘することができますか?

私のカスタム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);
    }

}

私のエンティティクラスは次のとおりです。

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


権限を熱心に取得するか、OpenSessionInViewFilterを使用してください。
バート

それはまさに私がやり方を調べようとしていることです。私が試したのはこれでした:List <Autorizacoes> Authority = user.getAutorizacoes()、UsernamePasswordAuthenticationTokenの割り当てからの同じ関数内ですが、それでも機能しません。
Kleber Mota 2014

2
@ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
バート

わかりました、試してみましたが、それでも機能しません。私のエンティティクラス更新:github.com/klebermo/webapp_horario_livre/blob/master/src/com/...、私の現在のAuthenticationProvider:github.com/klebermo/webapp_horario_livre/blob/master/src/com/...
クレベールモタ

回答:


140

fetch=FetchType.EAGER子エンティティを自動的にプルバックするには、ManyToManyアノテーション内に追加する必要があります。

@ManyToMany(fetch = FetchType.EAGER)

より良いオプションは、Spring構成ファイルに以下を追加してSpringtransactionManagerを実装することです。

<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)

これにより、認証メソッドの期間中、dbトランザクションが開始され、レイジーコレクションを使用しようとしたときにdbから取得できるようになります。


1
実際、アプリケーションでtransactionManagerを構成し、DAOクラスで使用しています。あなたが提案するようにAuthenticationProviderから認証するメソッドで使用しようとすると、エラーが発生します原因:java.lang.IllegalArgumentException:com.horariolivre.security.CustomAuthenticationProviderフィールドcom.horariolivre.security.SecurityConfig.authenticationProviderを$ Proxy36に設定できません。ManyToManyアノテーション内でaddfetchType = FetchType.EAGERを使用すると、同じエラーが発生します(これは、1つの属性でのみ使用できます。エンティティクラスUsuarioに同じ種類の3つがあります)。
Kleber Mota 2014

3
LazyInitializationExceptionを回避するには、トランザクション内で使用する子エンティティをウォークする必要があります。トランザクションアノテーションはジェネリックメソッドのdaoレベルにあるため、おそらくそこでは実行したくないでしょう。そのため、@ Transactional境界を持つdaoの前にサービスクラスを実装する必要があります。目的の子エンティティ
jcmwright80 2014

1
将来これに遭遇する誰かのためのアドバイス。@Transactionはパブリックメソッド上にある必要があります。そうでない場合、これは機能しません。警告がある場合とない場合があります。
ニコラス

フェッチタイプを使用し、それは完全に機能しました。@ transactionalカウンターパートへの熱心なフェッチを使用することの違いは何
ですか?Austine Gwa 2010

1
@AustineGwa主な違いは、熱心なfetchTypeを結合に追加すると、メインエンティティが読み込まれるたびに子エンティティのリストが常にDBからプルバックされるため、必要な機能領域がある場合にパフォーマンスが低下する可能性があることです。メインエンティティからのデータであるため、トランザクションと遅延読み込みを使用すると、プルバックされるデータの量をより細かく制御できますが、どのアプローチが適切かは、アプリケーションとユースケースに完全に依存します。
jcmwright 8020

36

を処理する最良の方法LazyInitializationExceptionJOIN FETCH、フェッチする必要のあるすべてのエンティティにディレクティブを使用することです。

とにかく、いくつかの回答で示唆されているように、次のアンチパターンを使用しないでください。

時には、DTOの投影がフェッチ実体よりも良い選択肢であり、この方法で、あなたはいずれかを取得することはありませんLazyInitializationException


1
フェッチ結合は、熱心なフェッチと同等です。これは常に実行可能でも効率的でもないかもしれません。また、オブジェクトをフェッチする通常の方法は、jpqlクエリを使用することではありません。オープンセッションがビューであるという事実はアンチパターンであり、正直なところ、私は同意しません。明らかに注意して使用する必要がありますが、それから恩恵を受ける完全に優れたユースケースはたくさんあります。
fer.marino 2017年

4
いいえ、そうではありませ。Open Session in Viewはハックであり、読み取り専用のプロジェクションであってもエンティティがフェッチされることを示しています。あなたがそれを正当化しようとどんなに懸命に努力しても、それから利益を得る多くの完全に素晴らしいユースケースのようなものはありません。本当に必要な数よりも多くのデータをフェッチする言い訳はありません。また、トランザクションサービスレイヤーの境界外でデータフェッチをリークする言い訳もありません。
ヴラッドミハルセア2017年

こんにちはVlad、FETCHJOINが積極的な読み込みと同等ではない理由を説明してください。私はこの記事を読んでい ます:blog.arnoldgalovics.com/2017/02/27/…。また、「親–会社–エンティティをロードするときにリレーションをロードすることをお勧めします。これはフェッチ結合で実行できます」と書かれています。したがって、これは熱心な読み込みです。そうですね。
オタク

2
熱心な指導とはFetchType.EAGER、あなたの協会に加わることを意味します。JOIN FETCHはFetchType.LAZY、クエリ時に熱心にフェッチする必要があるアソシエーション用です。
ヴラッドミハルセア2018年

25

次のプロパティをpersistence.xmlに追加すると、問題が一時的に解決する場合があります

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

@ vlad-mihalceaがアンチパターンであり、遅延初期化の問題を完全に解決しないと述べたように、トランザクションを閉じる前に関連付けを初期化し、代わりにDTOを使用します。


16

私もユニットテストをしているときにこの問題を抱えていました。この問題の非常に簡単な解決策は、実行が終了するまでセッションを開いたままにする@Transactionalアノテーションを使用することです。


HibernateTransationalまたはJPATransactionalを使用していますか?
jDub9 2018年

1
私はHibernate
KarthikaSrinivasan18年

11

その理由は、遅延読み込みを使用すると、セッションが閉じられるためです。

2つの解決策があります。

  1. 遅延読み込みは使用しないでください。

    lazy=falseXMLまたはSetIn@OneToMany(fetch = FetchType.EAGER)アノテーションで設定します。

  2. 遅延ロードを使用します。

    lazy=trueXMLまたはSetIn@OneToMany(fetch = FetchType.LAZY)アノテーションで設定します。

    そしてOpenSessionInViewFilter filterあなたのweb.xml

詳細私の投稿を参照してください。

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


1
OpenSessionInViewFilterもアンチパターンです。また、EAGERコレクションにそのデータを必要としない場合が多く、それらのユースケースが必要とするよりもはるかに多くのデータをプルダウンしてパフォーマンスを大幅に低下させるため、EAGERへのマッピングを設定しないことをお勧めします。すべてのマッピングをLAZYのままにして、代わりに結合フェッチをクエリに追加してください。
user1567291 2016


6

Hibernateレイジーイニシャライザーを使用できます。

以下は、参照できるコードです。
ここでPPIDO私が取得するデータオブジェクトがあります

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

4

列挙型のコレクションでこの問題を抱えている人のためにここでそれを解決する方法があります:

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

これは私にとってはうまくいきます。@Transactionalを追加するオプションもテストしましたが、これも機能します。しかし、私はこのオプションを選択します。
rickdana20年

2

まず第一に、怠惰な取引について言ったすべてのユーザーが正しかったと言いたいです。しかし、私の場合、テストで@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

熱心なフェッチを有効にするのではなく、LazyInitializationException例外を回避するために必要な場所でエンティティを再初期化する方が理にかなっていると思います

Hibernate.initialize(your entity);

0

JaVersを使用している場合、監査対象のエンティティクラスを指定すると、例外の原因となっているプロパティを無視することができLazyInitializationExceptionます(@DiffIgnoreアノテーションを使用するなど)。

これは、オブジェクトの差異を計算するときにこれらのプロパティを無視するようにフレームワークに指示するため、トランザクションスコープ外の関連オブジェクトをDBから読み取ろうとはしません(したがって、例外が発生します)。


0

一般的な方法は@Transactional、サービスクラスの上に配置することです。

@Service
@Transactional
public class MyServiceImpl implements MyService{
...
}

-1

注釈を追加します

@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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.