私はこの問題があります:
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の行に
Comment
を取得するたびにすべてのTopic
を参照する必要があることがわかっている場合は、comments
のフィールドマッピングを次のように変更します。
@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
コレクションはデフォルトでは遅延ロードされています。もっと知りたい場合は this をご覧ください。
私の経験から、私は有名なLazyInitializationExceptionを解決するために以下のメソッドを持っています:
(1)Hibernate.initializeを使う
Hibernate.initialize(topics.getComments());
(2)JOIN FETCHを使用する
JPQLでJOIN FETCH構文を使用して子コレクションを明示的に取得できます。これはEAGERフェッチのようなものです。
(3)OpenSessionInViewFilterを使う
LazyInitializationExceptionは多くの場合ビューレイヤで発生します。 Springフレームワークを使用している場合は、OpenSessionInViewFilterを使用できます。しかし、私はあなたにそうするように勧めません。正しく使用しないと、パフォーマンスの問題が発生する可能性があります。
デフォルトでは、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セッションはもう少し開いています。コントローラクラス.
私はそれが古い質問であることを知っていますが、私は助けたいです。トランザクションアノテーションを必要なサービスメソッドに置くことができます。この場合findTopicByID(id)は
@Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class)
このアノテーションについてのより多くの情報は見つけることができます ここ
他の解決策について:
fetch = FetchType.EAGER
これは良い習慣ではありません、必要ならばそれだけを使うべきです。
Hibernate.initialize(topics.getComments());
Hibernateイニシャライザは、クラスをHibernateテクノロジにバインドします。あなたが柔軟であることを目指しているなら、行くのは良い方法ではありません。
それが役に立てば幸い
遅延ロードを使用すると、セッションが閉じられるからです。
解決策は2つあります。
遅延負荷を使用しないでください。
XMLでlazy=false
を設定するか、アノテーションでSet @OneToMany(fetch = FetchType.EAGER)
を設定します。
遅延負荷を使用します。
XMLでlazy=true
を設定するか、アノテーションでSet @OneToMany(fetch = FetchType.LAZY)
を設定します。
OpenSessionInViewFilter filter
にweb.xml
を追加
詳細my _ post _ を参照してください。
問題は休止状態のセッションが閉じられた状態で属性にアクセスすることによって引き起こされます。コントローラに休止状態のトランザクションがありません。
可能な解決策:
コントローラ内ではなく、サービス層内でこのロジックをすべて実行してください (@Transactionalを使用)。これを行うための適切な場所があるはずです、それはコントローラではなく、アプリのロジックの一部です(この場合、モデルをロードするためのインターフェース)。サービス層のすべての操作はトランザクション的であるべきです。例:この行をTopicService.findTopicByIDメソッドに移動します。
コレクションcommentList = topicById.getComments();
'lazy'の代わりに 'eager'を使用してください 。今あなたは 'lazy'を使っていません..あなたがlazyを使いたいのであれば、それは本当の解決策ではありません、一時的な(非常に一時的な)回避策のように働きます。
一般に、最良の解決策は1です。
コレクションを遅延ロードするには、アクティブなセッションがなければなりません。 Webアプリでこれを行うには2つの方法があります。 インターセプター を使用してリクエストの最初にセッションを開き、最後にそれを閉じるには、 Open Session in View パターンを使用できます。危険なのは、しっかりとした例外処理をしなければならない、あるいはすべてのセッションを拘束してアプリケーションがハングする可能性があるということです。
これを処理するもう1つの方法は、コントローラに必要なすべてのデータを収集し、セッションを閉じてから、そのデータをモデルに格納することです。私は個人的にはMVCパターンの精神に少し近いように思われるので、このアプローチを好みます。また、この方法でデータベースからエラーが発生した場合は、ビューレンダラーで発生した場合よりもはるかにうまく処理できます。このシナリオでのあなたの友人は Hibernate.initialize (myTopic.getComments())です。リクエストごとに新しいトランザクションを作成しているので、オブジェクトをセッションに再アタッチする必要もあります。そのためにはsession.lock(myTopic、LockMode.NONE)を使用してください。
@Controller
@RequestMapping(value = "/topic")
@Transactional
私は@Transactional
を追加することによってこの問題を解決します、私はこれがセッションをオープンにすることができると思います
エンティティとCollectionまたはJavaオブジェクトのリスト(たとえばLong型)の間に関係を持たせようとしている場合は、次のようになります。
@ElementCollection(fetch = FetchType.EAGER)
public List<Long> ids;
私は、EXTENDED
として@PersistenceContext
を宣言することがこの問題を解決することを発見しました:
@PersistenceContext(type = PersistenceContextType.EXTENDED)
この記事 で説明したように、LazyInitializationException
を処理する最良の方法は、次のようにクエリ時にそれを取得することです。
select t
from Topic t
left join fetch t.comments
次のアンチパターンを避けるようにしてください。
したがって、FetchType.LAZY
アソシエーションが、クエリ時またはセカンダリコレクションに@Transactional
を使用して元のHibernate.initialize
スコープ内で初期化されていることを確認してください。
あなたのリストは遅延ロードであるため、リストはロードされませんでした。リストに乗るための呼び出しは十分ではありません。リストを初期化するために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
それは私が使用して解決した私が最近直面した問題でした
<f:attribute name="collectionType" value="Java.util.ArrayList" />
より詳細な説明 ここ そしてこれは私の日を救った。
コントローラの@Transactionalアノテーションがありません
@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
私の場合の問題を解決するには、この行が足りないだけでした。
<tx:annotation-driven transaction-manager="myTxManager" />
アプリケーションコンテキストファイル内。
メソッドに対する@Transactional
アノテーションは考慮されませんでした。
答えが誰かに役立つことを願っています
最善の解決策の1つは、application.propertiesファイルに次の内容を追加することです。 spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true
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()
を呼び出すという事実は、あなたが明示的に要求したので、売上を取得するために別のクエリを行うということです。
私の場合、次のコードが問題でした。
entityManager.detach(topicById);
topicById.getComments() // exception thrown
データベースから切り離され、Hibernateは必要になったときにフィールドからリストを取得しなくなりました。デタッチする前に初期化します。
Hibernate.initialize(topicById.getComments());
entityManager.detach(topicById);
topicById.getComments() // works like a charm
Criteria で作業している人には、
criteria.setFetchMode("lazily_fetched_member", FetchMode.EAGER);
私が必要としていたすべてのことをしました。
パフォーマンスを向上させるために、コレクションの初期フェッチモードはFetchMode.LAZYに設定されていますが、データが必要なときは、その行を追加して、完全に移入されたオブジェクトを楽しんでください。
モデルクラス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
解決策は次のとおりです。
comments
にfetch = FetchType.EAGER
で注釈を付ける
@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
それでもコメントを遅延ロードしたい場合は、後で要求に応じてコメントを取得できるように、 Hibernateのステートフルセッション を使用します。
こんにちはすべての投稿は非常に遅くそれが他の人を助けることを願って、この記事のために@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'にアクセスできるようになりました。
さらに別の方法では、 TransactionTemplate を使用して遅延フェッチをラップすることができます。好き
Collection<Comment> commentList = this.transactionTemplate.execute
(status -> topicById.getComments());
私のcaeでは、私はのようなマッピングB/W AとBを持っていた
Aは持っています
@OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
Set<B> bs;
dAO層では、マッピングに Fetch Type - Eager のアノテーションを付けていない場合は、 method に@Transactional
のアノテーションを付ける必要があります。
その理由は、サービス内のセッションを閉じた後に、コントローラ上のcommentListを取得しようとしているからです。
topicById.getComments();
上記は、あなたの休止状態のセッションがアクティブである場合にのみcommentListをロードします。これはあなたがあなたのサービスで閉じたと思います。
そのため、セッションを閉じる前にcommentListを取得する必要があります。