web-dev-qa-db-ja.com

「ロールのコレクションを遅延初期化できなかった」Hibernate例外を解決する方法

私はこの問題があります:

org.hibernate.LazyInitializationException:ロールのコレクションを遅延初期化できませんでした:mvc3.model.Topic.comments、セッションがないかセッションが閉じられていません

これがモデルです:

@Entity
@Table(name = "T_TOPIC")
public class Topic {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;

    @ManyToOne
    @JoinColumn(name="USER_ID")
    private User author;

    @Enumerated(EnumType.STRING)    
    private Tag topicTag;

    private String name;
    private String text;

    @OneToMany(mappedBy = "topic", cascade = CascadeType.ALL)
    private Collection<Comment> comments = new LinkedHashSet<Comment>();

    ...

    public Collection<Comment> getComments() {
           return comments;
    }

}

Modelを呼び出すコントローラは次のようになります。

@Controller
@RequestMapping(value = "/topic")
public class TopicController {

    @Autowired
    private TopicService service;

    private static final Logger logger = LoggerFactory.getLogger(TopicController.class);


    @RequestMapping(value = "/details/{topicId}", method = RequestMethod.GET)
    public ModelAndView details(@PathVariable(value="topicId") int id)
    {

            Topic topicById = service.findTopicByID(id);
            Collection<Comment> commentList = topicById.getComments();

            Hashtable modelData = new Hashtable();
            modelData.put("topic", topicById);
            modelData.put("commentList", commentList);

            return new ModelAndView("/topic/details", modelData);

     }

}

JSPページは次のようになります。

<%@page import="com.epam.mvc3.helpers.Utils"%>
<%@ page language="Java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://Java.Sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
      <title>View Topic</title>
</head>
<body>

<ul>
<c:forEach items="${commentList}" var="item">
<jsp:useBean id="item" type="mvc3.model.Comment"/>
<li>${item.getText()}</li>

</c:forEach>
</ul>
</body>
</html>

JSPを表示しているときに例外が発生します。 c:forEach loopの行に

293
Eugene

Commentを取得するたびにすべてのTopicを参照する必要があることがわかっている場合は、commentsのフィールドマッピングを次のように変更します。

@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();

コレクションはデフォルトでは遅延ロードされています。もっと知りたい場合は this をご覧ください。

175
darrengorman

私の経験から、私は有名なLazyInitializationExceptionを解決するために以下のメソッドを持っています:

(1)Hibernate.initializeを使う

Hibernate.initialize(topics.getComments());

(2)JOIN FETCHを使用する

JPQLでJOIN FETCH構文を使用して子コレクションを明示的に取得できます。これはEAGERフェッチのようなものです。

(3)OpenSessionInViewFilterを使う

LazyInitializationExceptionは多くの場合ビューレイヤで発生します。 Springフレームワークを使用している場合は、OpenSessionInViewFilterを使用できます。しかし、私はあなたにそうするように勧めません。正しく使用しないと、パフォーマンスの問題が発生する可能性があります。

153
Boris

あなたの問題の起源:

デフォルトでは、Hibernateはコレクション(リレーションシップ)を遅延ロードします。つまり、コードでcollection(ここではcommentsクラスのTopicフィールド)を使用すると、Hibernateはそれをデータベースから取得します。 JPAセッションは閉じられます。これは、例外を発生させるコード行です(ここで、commentsコレクションをロードしています)。

    Collection<Comment> commentList = topicById.getComments();

あなたはあなたのコントローラ( "JPA session"が終了したところ)に "comments"コレクション(topic.getComments())を取得しています、そしてそれは例外を引き起こします。また、jspファイルにcommentsコレクションがこのように含まれている場合は(コントローラに取得するのではなく)、

<c:forEach items="topic.comments" var="item">
//some code
</c:forEach>

あなたはまだ同じ理由で同じ例外を持っているでしょう。

問題を解決する:

EntityクラスにはFetchType.Eager(積極的に取得されたコレクション)を持つコレクションは2つしかないため、遅延ロードは積極的なロードより効率的であるため、この問題を解決する方法はFetchTypeを単にeagerに変更するよりも優れています。

コレクションを遅延初期化して、これを機能させたい場合は、このコードスニペットをweb.xmlに追加することをお勧めします。

<filter>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

このコードがすることはそれがあなたのJPA sessionの長さを増加させるということです、あるいはドキュメンテーションが言うようにそれは"to allow for lazy loading in web views despite the original transactions already being completed."を使用されるのでJPAセッションはもう少し開いています。コントローラクラス.

47
gandalf

私はそれが古い質問であることを知っていますが、私は助けたいです。トランザクションアノテーションを必要なサービスメソッドに置くことができます。この場合findTopicByID(id)は

@Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class)

このアノテーションについてのより多くの情報は見つけることができます ここ

他の解決策について:

fetch = FetchType.EAGER 

これは良い習慣ではありません、必要ならばそれだけを使うべきです。

Hibernate.initialize(topics.getComments());

Hibernateイニシャライザは、クラスをHibernateテクノロジにバインドします。あなたが柔軟であることを目指しているなら、行くのは良い方法ではありません。

それが役に立てば幸い

39
sarbuLopex

遅延ロードを使用すると、セッションが閉じられるからです。

解決策は2つあります。

  1. 遅延負荷を使用しないでください。

    XMLでlazy=falseを設定するか、アノテーションでSet @OneToMany(fetch = FetchType.EAGER)を設定します。

  2. 遅延負荷を使用します。

    XMLでlazy=trueを設定するか、アノテーションでSet @OneToMany(fetch = FetchType.LAZY)を設定します。

    OpenSessionInViewFilter filterweb.xmlを追加

詳細my _ post _ を参照してください。

28
saneryee

問題は休止状態のセッションが閉じられた状態で属性にアクセスすることによって引き起こされます。コントローラに休止状態のトランザクションがありません。

可能な解決策:

  1. コントローラ内ではなく、サービス層内でこのロジックをすべて実行してください (@Transactionalを使用)。これを行うための適切な場所があるはずです、それはコントローラではなく、アプリのロジックの一部です(この場合、モデルをロードするためのインターフェース)。サービス層のすべての操作はトランザクション的であるべきです。例:この行をTopicService.findTopicByIDメソッドに移動します。

    コレクションcommentList = topicById.getComments();

  2. 'lazy'の代わりに 'eager'を使用してください 。今あなたは 'lazy'を使っていません..あなたがlazyを使いたいのであれば、それは本当の解決策ではありません、一時的な(非常に一時的な)回避策のように働きます。

  3. コントローラで@Transactionalを使う 。ここでは使用しないでください。サービス層とプレゼンテーションを混在させています。これは良い設計ではありません。
  4. OpenSessionInViewFilterを使用してください 、多くの欠点が報告されていますが、不安定な可能性があります。

一般に、最良の解決策は1です。

18
abentan

コレクションを遅延ロードするには、アクティブなセッションがなければなりません。 Webアプリでこれを行うには2つの方法があります。 インターセプター を使用してリクエストの最初にセッションを開き、最後にそれを閉じるには、 Open Session in View パターンを使用できます。危険なのは、しっかりとした例外処理をしなければならない、あるいはすべてのセッションを拘束してアプリケーションがハングする可能性があるということです。

これを処理するもう1つの方法は、コントローラに必要なすべてのデータを収集し、セッションを閉じてから、そのデータをモデルに格納することです。私は個人的にはMVCパターンの精神に少し近いように思われるので、このアプローチを好みます。また、この方法でデータベースからエラーが発生した場合は、ビューレンダラーで発生した場合よりもはるかにうまく処理できます。このシナリオでのあなたの友人は Hibernate.initialize (myTopic.getComments())です。リクエストごとに新しいトランザクションを作成しているので、オブジェクトをセッションに再アタッチする必要もあります。そのためにはsession.lock(myTopic、LockMode.NONE)を使用してください。

18
GMK
@Controller
@RequestMapping(value = "/topic")
@Transactional

私は@Transactionalを追加することによってこの問題を解決します、私はこれがセッションをオープンにすることができると思います

17
RuiZhi Wang

エンティティとCollectionまたはJavaオブジェクトのリスト(たとえばLong型)の間に関係を持たせようとしている場合は、次のようになります。

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;
11
javaboygo

私は、EXTENDEDとして@PersistenceContextを宣言することがこの問題を解決することを発見しました:

@PersistenceContext(type = PersistenceContextType.EXTENDED)
9
Elcin ABD

この記事 で説明したように、LazyInitializationExceptionを処理する最良の方法は、次のようにクエリ時にそれを取得することです。

select t
from Topic t
left join fetch t.comments

次のアンチパターンを避けるようにしてください。

したがって、FetchType.LAZYアソシエーションが、クエリ時またはセカンダリコレクションに@Transactionalを使用して元のHibernate.initializeスコープ内で初期化されていることを確認してください。

6
Vlad Mihalcea

あなたのリストは遅延ロードであるため、リストはロードされませんでした。リストに乗るための呼び出しは十分ではありません。リストを初期化するためにHibernate.initializeで使用します。 dosntがリスト要素に対して実行され、それぞれに対してHibernate.initializeを呼び出す場合は、これはトランザクションスコープから戻る前にする必要があります。 これ postを見てください。
検索する -

Node n = // .. get the node
Hibernate.initialize(n); // initializes 'parent' similar to getParent.
Hibernate.initialize(n.getChildren()); // pass the lazy collection into the session 
5

それは私が使用して解決した私が最近直面した問題でした

<f:attribute name="collectionType" value="Java.util.ArrayList" />

より詳細な説明 ここ そしてこれは私の日を救った。

5
tolgayilmaz

コントローラの@Transactionalアノテーションがありません

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
5
Xcoder

私の場合の問題を解決するには、この行が足りないだけでした。

<tx:annotation-driven transaction-manager="myTxManager" />

アプリケーションコンテキストファイル内。

メソッドに対する@Transactionalアノテーションは考慮されませんでした。

答えが誰かに役立つことを願っています

4
Mario Biasi

最善の解決策の1つは、application.propertiesファイルに次の内容を追加することです。 spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true

4
sreekmatta

Hibernateの@Transactionalアノテーションを使用して、lazyフェッチされた属性を持つオブジェクトをデータベースから取得する場合は、次のようにこれらの属性をフェッチすることでこれらを取得できます。

@Transactional
public void checkTicketSalePresence(UUID ticketUuid, UUID saleUuid) {
        Optional<Ticket> savedTicketOpt = ticketRepository.findById(ticketUuid);
        savedTicketOpt.ifPresent(ticket -> {
            Optional<Sale> saleOpt = ticket.getSales().stream().filter(sale -> sale.getUuid() == saleUuid).findFirst();
            assertThat(saleOpt).isPresent();
        });
}

ここで、Hibernateのプロキシ管理トランザクションでは、ticket.getSales()を呼び出すという事実は、あなたが明示的に要求したので、売上を取得するために別のクエリを行うということです。

3
Alex

私の場合、次のコードが問題でした。

entityManager.detach(topicById);
topicById.getComments() // exception thrown

データベースから切り離され、Hibernateは必要になったときにフィールドからリストを取得しなくなりました。デタッチする前に初期化します。

Hibernate.initialize(topicById.getComments());
entityManager.detach(topicById);
topicById.getComments() // works like a charm
2
kiedysktos

Criteria で作業している人には、

criteria.setFetchMode("lazily_fetched_member", FetchMode.EAGER);

私が必要としていたすべてのことをしました。

パフォーマンスを向上させるために、コレクションの初期フェッチモードはFetchMode.LAZYに設定されていますが、データが必要なときは、その行を追加して、完全に移入されたオブジェクトを楽しんでください。

2
velis

モデルクラスcommentsのコレクションTopicは遅延ロードされます。特にfetch = FetchType.EAGERでアノテーションを付けない場合はこれがデフォルトの動作です。

ほとんどの場合、あなたのfindTopicByIDサービスはステートレスHibernateセッションを使用しています。 ステートレスセッション は第1レベルのキャッシュを持っていません。つまり永続コンテキストはありません。後でcommentsを反復しようとすると、Hibernateは例外をスローします。

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: mvc3.model.Topic.comments, no session or session was closed

解決策は次のとおりです。

  1. commentsfetch = FetchType.EAGERで注釈を付ける

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
    
  2. それでもコメントを遅延ロードしたい場合は、後で要求に応じてコメントを取得できるように、 Hibernateのステートフルセッション を使用します。

1
Yuci

こんにちはすべての投稿は非常に遅くそれが他の人を助けることを願って、この記事のために@GMKに前もって感謝/ Hibernate.initialize(object)

lazy = "true"の場合

Set<myObject> set=null;
hibernateSession.open
set=hibernateSession.getMyObjects();
hibernateSession.close();

今私はセッションを閉じた後に 'set'にアクセスすると例外をスローします。

私の解決策:

Set<myObject> set=new HashSet<myObject>();
hibernateSession.open
set.addAll(hibernateSession.getMyObjects());
hibernateSession.close();

hibernate Sessionを閉じた後でも 'set'にアクセスできるようになりました。

0
vic

さらに別の方法では、 TransactionTemplate を使用して遅延フェッチをラップすることができます。好き

Collection<Comment> commentList = this.transactionTemplate.execute
(status -> topicById.getComments());
0
aristotll

私のcaeでは、私はのようなマッピングB/W AとBを持っていた

Aは持っています

@OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
Set<B> bs;

dAO層では、マッピングに Fetch Type - Eager のアノテーションを付けていない場合は、 method @Transactionalのアノテーションを付ける必要があります。

0
Shirgill Farhan

その理由は、サービス内のセッションを閉じた後に、コントローラ上のcommentListを取得しようとしているからです。

topicById.getComments();

上記は、あなたの休止状態のセッションがアクティブである場合にのみcommentListをロードします。これはあなたがあなたのサービスで閉じたと思います。

そのため、セッションを閉じる前にcommentListを取得する必要があります。

0
aditya lath