org.hibernate.LazyInitializationExceptionを修正する方法-プロキシを初期化できませんでした-セッションなし


188

次の例外が発生します。

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at sei.persistence.wf.entities.Element_$$_jvstc68_47.getNote(Element_$$_jvstc68_47.java)
    at JSON_to_XML.createBpmnRepresantation(JSON_to_XML.java:139)
    at JSON_to_XML.main(JSON_to_XML.java:84)

メインから次の行を呼び出そうとすると:

Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());

私はgetModelByModelGroup(int modelgroupid)最初にこのようにメソッドを実装しました:

public static Model getModelByModelGroup(int modelGroupId, boolean openTransaction) {

    Session session = SessionFactoryHelper.getSessionFactory().getCurrentSession();     
    Transaction tx = null;

    if (openTransaction) {
        tx = session.getTransaction();
    }

    String responseMessage = "";

    try {
        if (openTransaction) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new Exception("Non esiste ");
            }

            model = (Model)arrModels[0];
        }

        if (openTransaction) {
            tx.commit();
        }

        return model;

   } catch(Exception ex) {
       if (openTransaction) {
           tx.rollback();
       }
       ex.printStackTrace();
       if (responseMessage.compareTo("") == 0) {
           responseMessage = "Error" + ex.getMessage();
       }
       return null;
    }
}

そして例外を得た。次に、友人が私に常にセッションをテストし、このエラーを回避するために現在のセッションを取得するように提案しました。だから私はこれをしました:

public static Model getModelByModelGroup(int modelGroupId) {
    Session session = null;
    boolean openSession = session == null;
    Transaction tx = null;
    if (openSession) {
        session = SessionFactoryHelper.getSessionFactory().getCurrentSession(); 
        tx = session.getTransaction();
    }
    String responseMessage = "";

    try {
        if (openSession) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new RuntimeException("Non esiste");
            }

            model = (Model)arrModels[0];

            if (openSession) {
                tx.commit();
            }
            return model;
        } catch(RuntimeException ex) {
            if (openSession) {
                tx.rollback();
            }
            ex.printStackTrace();
            if (responseMessage.compareTo("") == 0) {
                responseMessage = "Error" + ex.getMessage();
            }
            return null;        
        }
    }
}

それでも、同じエラーが発生します。私はこのエラーについてたくさん読んでいて、いくつかの可能な解決策を見つけました。それらの1つは、lazyLoadをfalseに設定することでしたが、これを行うことは許可されていないため、セッションを制御するよう提案されました

回答:


93

ここで問題なのは、トランザクションをコミットするとセッション管理構成がセッションを閉じるように設定されていることです。次のようなものがあるかどうかを確認します。

<property name="current_session_context_class">thread</property>

あなたの構成で。

この問題を解決するには、セッションファクトリの構成を変更するか、別のセッションを開いて、遅延ロードされたオブジェクトを要求するだけです。しかし、私がここで提案するのは、この遅延コレクションをgetModelByModelGroup自体で初期化して呼び出すことです。

Hibernate.initialize(subProcessModel.getElement());

あなたがまだアクティブなセッションにいるとき。

そして最後にもう一つ。フレンドリーなアドバイス。あなたのメソッドには次のようなものがあります:

for (Model m : modelList) {
    if (m.getModelType().getId() == 3) {
        model = m;
        break;
    }
}

このコードを挿入して、上の数行だけのクエリステートメントで、タイプIDが3のモデルをフィルタリングしてください。

さらに読む:

セッションファクトリ構成

閉じたセッションの問題


1
ありがとうございました!getCurrentSession()の代わりにopenSession()を使用して問題を解決しました。これは、あなたが私に与えたリンクの1つがこれを提案したものですが、そうするのが間違っていると私は恐れています
Blerta Dhimitri 14

2
いいえ、おそらく問題ありません。しかし、セッションとトランザクションを完全に制御できるように、もう少し読んでください。SpringやHibernateなどのより高度なテクノロジーはすべてまったく同じコンセプトで動作するため、基本を理解することは本当に重要です。
goroncy 2014

179

Springを使用している場合、クラスに@Transactionalのマークを付けると、Springがセッション管理を処理します。

@Transactional
public class MyClass {
    ...
}

を使用することにより@Transactional、トランザクションの伝播などの多くの重要な側面が自動的に処理されます。この場合、別のトランザクションメソッドが呼び出された場合、そのメソッドには、「セッションなし」の例外を回避して、進行中のトランザクションに参加するオプションがあります。

警告を使用する場合@Transactionalは、結果として生じる動作に注意してください。一般的な落とし穴については、この記事を参照してください。たとえば、明示的に呼び出さなくてもエンティティの更新は永続化されますsave


21
この答えの重要性を誇張することはできません。私は真剣にこのオプションを試すことをお勧めします。
sparkyspider 2016

6
また@EnableTransactionManagement、トランザクションを有効にするには、構成に追加する必要があることにも注意してください。「別のトランザクションメソッドが呼び出された場合、そのメソッドには進行中のトランザクションに参加するオプションがあります」この動作は、トランザクションの実装方法によって異なります。つまり、インターフェースプロキシとクラスプロキシまたはAspectJウィービングです。ドキュメントを参照してください。
Erik Hofer

1
Transactionalこのように、トランザクションの変更だけでなくトランザクションへのアクセスにもSpring アノテーションが推奨されることを理解する必要がありますか?
ステファン

8
テストのためだけにこのアノテーションをクラスの上に使用することを真剣に勧めます。実際のコードでは、各メソッドをクラスのトランザクションとして個別にマークする必要があります。クラス内のすべてのメソッドがデータベースへのトランザクションとの開かれた接続を必要としない限り。
m1ld 2018

1
@Transactionalの代わりに@Transactional(readOnly = true)を置くのは安全ではありませんか?
Hamedz

105

あなたは設定しようとすることができます

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

hibernate.cfg.xmlまたはpersistence.xml

このプロパティで覚えておくべき問題は、ここで十分に説明されています


8
その意味も説明できますか?
Mohit Kanwar、2015

2
これが何をするのかも知りたいです。私が抱えていた問題は修正されましたが、その理由を知りたいのですが。
Hassan

3
persistence.xmlの場合:<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
ACV 2016

33
あなたhibernate.enable_lazy_load_no_transそれがアンチパターンあることを知っていますか?
Vlad Mihalcea

6
このプロパティは使用しないでください。SPRINGがトランザクションを管理している場合、このプロパティがトランザクションの爆発につながり、単純にSPRINGがアプリケーションをシャットダウンします
Youans

54

を処理する最良の方法LazyInitializationExceptionは、JOIN FETCHディレクティブを使用することです。

Query query = session.createQuery(
    "from Model m " +
    "join fetch m.modelType " +
    "where modelGroup.id = :modelGroupId"
);

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

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


どの通話に問題があるかを特定するにはどうすればよいですか?通話を特定するのが難しいと感じています。何か方法はありますか?テスト目的で使用FetchType=EAGERしましたが、これは正しいソリューションではありませんよね?
Shantaram Tupe 2017年

ロギングを使用してください。そして、イーガーは悪いです、はい。
Vlad Mihalcea 2017年

次に、@Transactionalサービスを終了する前に、DTOを使用するか、すべての関連付けを初期化する必要があります。
Vlad Mihalcea 2017年

2
迅速な解決策ではなく、企業のベストプラクティスを推奨する必要があります。
etlds

21

以下の注釈の1対多の関係で同じエラーが発生しました。

@OneToMany(mappedBy="department", cascade = CascadeType.ALL)

fetch = FetchType.EAGERを追加した後、以下のように変更しましたが、うまくいきました。

@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)

26
はい、それはそれを修正するかもしれませんが、今あなたはデータのツリー全体をロードしています。これは、ほとんどの場合、パフォーマンスに悪影響を及ぼします
astro8891


9

この例外は、を呼び出すsession.getEntityById()と、セッションが閉じられます。したがって、エンティティをセッションに再アタッチする必要があります。または、Easyソリューションはdefault-lazy="false" 、自分に設定するentity.hbm.xmlか、注釈を使用している場合は@Proxy(lazy=false)エンティティクラスに追加するだけです。


5

同じ問題が発生しました。これを修正する別の方法は、次のようにクエリを変更して結合してモデルから要素をフェッチできることです。

Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")

4

これは、アクセスしようとしているオブジェクトがロードされていないことを意味するので、アクセスしようとしているオブジェクトの結合フェッチを行うクエリを記述します。

例えば:

ObjectBがObjectAの外部キーであるObjectAからObjectBを取得しようとしている場合。

クエリ:

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

3

このエラーを広い範囲で処理する良い答えがいくつかあります。Spring Securityで特定の状況に遭遇しましたが、おそらく最適ではありませんが、すぐに修正できました。

ユーザーの認証中(ログインして認証に合格した直後)、SimpleUrlAuthenticationSuccessHandlerを拡張するカスタムクラスで、特定の権限についてユーザーエンティティをテストしていました。

私のユーザーエンティティはUserDetailsを実装し、「org.hibernate.LazyInitializationException-プロキシを初期化できませんでした-セッションなし」例外をスローする遅延ロードされたロールのセットがあります。そのセットを "fetch = FetchType.LAZY"から "fetch = FetchType.EAGER"に変更すると、これが修正されました。


2

JPQLを使用している場合は、JOIN FETCHを使用するのが最も簡単な方法です。http://www.objectdb.com/java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_


2

異なるユースケースで同じ例外に直面しました。

ここに画像の説明を入力してください

使用例:DTOプロジェクションを使用してDBからデータを読み取ってみます。

解決策:loadではなくgetメソッドを使用します

一般的な操作

public class HibernateTemplate {
public static Object loadObject(Class<?> cls, Serializable s) {
    Object o = null;
    Transaction tx = null;
    try {
        Session session = HibernateUtil.getSessionFactory().openSession();
        tx = session.beginTransaction();
        o = session.load(cls, s); /*change load to get*/
        tx.commit();
        session.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return o;
}

}

永続クラス

public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int customerId;

@Column(name = "Name")
private String customerName;

@Column(name = "City")
private String city;

//constructors , setters and getters

}

CustomerDAOインターフェース

public interface CustomerDAO 
     {
   public CustomerTO getCustomerById(int cid);
     }

エンティティ転送オブジェクトクラス

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

工場クラス

public class DAOFactory {

static CustomerDAO customerDAO;
static {
    customerDAO = new HibernateCustomerDAO();
}

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

エンティティ固有のDAO

public class HibernateCustomerDAO implements CustomerDAO {

@Override
public CustomerTO getCustomerById(int cid) {
    Customer cust = (Customer) HibernateTemplate.loadObject(Customer.class, cid);
    CustomerTO cto = new CustomerTO(cust.getCustomerId(), cust.getCustomerName(), cust.getCity());
    return cto;
}

}

データの取得:テストクラス

CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());

現在のデータ

ここに画像の説明を入力してください

Hibernateシステムによって生成されたクエリと出力

Hibernate:CustomerLab31のcustomer0_からcustomer0_.IdをId1_0_0_、customer0_.CityをCity2_0_0_、customer0_.NameをName3_0_0_として選択します。customer0_.Id=?

CustomerName-> Cody、CustomerCity-> LA


1

Grail'sフレームワークを使用している場合は、ドメインクラスの特定のフィールドでキーワードを使用することで、遅延初期化例外を簡単に解決できますLazy

例えば:

class Book {
    static belongsTo = [author: Author]
    static mapping = {
        author lazy: false
    }
}

詳細はこちら



1

これは、コードでJPAまたはhibernateを使用し、ビジネスロジックトランザクションを行わずにDBで変更操作を実行していることを意味します。このための簡単な解決策は、コードに@Transactionalのマークを付けることです



-2

* .hbm.xmlファイルにlazy = falseを追加して解決することも、dbからオブジェクトを取得するときにHibernate.init(Object)でオブジェクトを初期化することもできます。


10
通常、lazy = falseを追加することはお勧めできません。そのため、lazyはデフォルトでtrueになっています
MoienGK 2014年

OPは彼がそれをすることは許されないことを前もってはっきり言った。
GingerHead

-2

servlet-context.xmlで次の変更を行います

    <beans:property name="hibernateProperties">
        <beans:props>

            <beans:prop key="hibernate.enable_lazy_load_no_trans">true</beans:prop>

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