“Bir rol koleksiyonunu tembel olarak başlatamadı” nasıl hazırlanır?


363

Bu sorun var:

org.hibernate.LazyInitializationException: bir rol koleksiyonunu tembel olarak başlatamadı: mvc3.model.Topic.comments, oturum veya oturum kapatılmadı

İşte model:

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

}

Modeli çağıran denetleyici aşağıdaki gibidir:

@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 sayfası aşağıdaki gibi görünür:

<%@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 görüntülenirken kural dışı durum yükselir. C: forEach döngüsü ile aynı satırda

Yanıtlar:


214

CommentHer seferinde her şeyi görmek isteyeceğinizi biliyorsanız Topicalan eşlemenizi şu şekilde commentsdeğiştirin:

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

Koleksiyonları, tembel yüklenen varsayılan olarak bakmak bu Daha fazla bilgi almak istiyorum.


35
Üzgünüm, ama tembel yük kullanmak istiyorum. Bu yüzden 'LinkedHashSet' türünü 'PersistentList' olarak değiştirdim. İstisna hala gerçekleşiyor
Eugene

242
Bu, geçici bir çözüm olarak kullanılabilir, ancak soruna gerçek bir çözüm değildir. Tembel olarak getirmemiz gerekiyorsa ne olur?
Dkyc

14
ancak tembel olmak istiyorsak, bu çözüm işe yaramaz ve çoğu durumda sadece tembel olmak isteriz.
prashant thakre

103
Bu, yığın taşması sırasında her yerde açılan cevap türüdür. Kısacası, sorunu çözer ve MISLEADING. Gelecekteki okuyuculara kendinize bir iyilik yapın ve tembel ve hevesle getirilenin tam olarak ne olduğunu öğrenin ve sonuçlarını anlayın.
Ced

13
@darrengorman JPA'ya başladığımda OP'nin çizgileri etrafında bir soru yayınladım. Verdiğinizle aynı yanıtı aldım. Yakında yüz binlerce sıra ile bazı testler yaptığımda, tahmin et ne oldu? Ben yanıltıcı olduğunu düşünüyorum çünkü çok yeni başlayanlar karşılaşacak bir sorun için çok basit bir cevap sağlar ve yeterince yakında onlar dikkatli değilse onlar tüm veritabanı bellek yüklü olacak (ve onlar olmaz, çünkü onlar olmaz farkında olun) :).
Ced

182

Deneyimlerime göre, ünlü LazyInitializationException'ı çözmek için aşağıdaki yöntemlere sahibim:

(1) Hibernate.initialize komutunu kullanın

Hibernate.initialize(topics.getComments());

(2) FETCH'E KATIL

Alt koleksiyonu açıkça almak için JPQL'inizdeki JOIN FETCH sözdizimini kullanabilirsiniz. EAGER getirme gibi.

(3) OpenSessionInViewFilter kullanın

LazyInitializationException genellikle görünüm katmanında oluşur. Spring çerçevesini kullanıyorsanız, OpenSessionInViewFilter kullanabilirsiniz. Ancak, bunu yapmanızı önermiyorum. Doğru kullanılmazsa performans sorununa yol açabilir.


5
(1) benim için mükemmel çalıştı. Benim durumum: Hibernate.initialize (kayıt defteri.getVehicle (). GetOwner (). GetPerson (). GetAddress ());
Leonel Sanches da Silva

6
Hibernate.initialize, EntityManager ile çalışmaz gibi görünüyor
marionmaiden

8
Bu doğru cevap olmalı. Örneğin iş yerindeki projemde açıkça EAGER getirmeyi kullanmamamız gerekiyor. Bu özel sistemde sorunlara neden olur.
Steve Waters

Çekici görünüyor, ancak başka bir durumda uygulanacak dokümantasyon eksikliği ... lütfen bu çözümü nasıl uygulayacağınızla ilgili daha fazla bağlantı veya açıklama sağlayabilir misiniz?
Pipo

58

Bunun eski bir soru olduğunu biliyorum ama yardım etmek istiyorum. İşlem ek açıklamasını ihtiyacınız olan hizmet yöntemine koyabilirsiniz, bu durumda findTopicByID (id)

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

bu ek açıklama hakkında daha fazla bilgiyi burada bulabilirsiniz

Diğer çözümler hakkında:

fetch = FetchType.EAGER 

iyi bir uygulama değildir, SADECE gerekirse kullanılmalıdır.

Hibernate.initialize(topics.getComments());

Hazırda bekletme başlatıcısı, sınıflarınızı hazırda bekletme teknolojisine bağlar. Esnek olmayı hedefliyorsanız, gitmek için iyi bir yol değildir.

Umarım yardımcı olur


3
@Tacactional ek açıklama benim için çalıştı, ancak propagation.REQUIRED varsayılan, en azından Spring Boot 1.4.2 (Bahar 4.3) varsayılan olduğunu unutmayın.
ben3000

4
Evet, ama aslında yayılma parametresini değiştirebileceğinizi açıklığa kavuşturmanın takdir edileceğini düşündüm
sarbuLopex

@TransactionalSadece bahar değil mi?
Campa

@Campa evet öyle. Elle işlemek istiyorsanız, işletme mantığınızı varlık yöneticisinden alınan bir işlemin içine
koymalısınız

54

Sorununuzun kaynağı:

Varsayılan olarak hazırda bekletme modu, koleksiyonunuzda (ilişkiler) tembel olarak yükler; bu collection, kodunuzda ( sınıftaki commentsalan Topic) her kullandığınızda, hazırda bekletme veritabanından alır, şimdi sorun koleksiyonunuzu denetleyicinizde (JPA oturumu) almanızdır. Bu, istisnanın ( commentskoleksiyonun yüklendiği konum ) neden olduğu kod satırıdır :

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

Oyun kumandanızda ( JPA sessionsona erdiğinde) "comments" koleksiyonu (topic.getComments ()) alıyorsunuz ve bu da istisnayı yaratıyor . Ayrıca commentsjsp dosyanızdaki koleksiyonu (denetleyicinize almak yerine) bu şekilde aldıysanız:

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

Aynı nedenden ötürü hala aynı istisnanız olur.

Sorunu çözmek:

FetchType.EagerBir Entity sınıfında (hevesle getirilen koleksiyon) yalnızca iki koleksiyona sahip olabileceğiniz ve tembel yükleme hevesli yüklemeden daha verimli olduğu için, sorununuzu çözmenin bu yolunun sadece FetchTypeistekli olarak değiştirmekten daha iyi olduğunu düşünüyorum :

Koleksiyonun tembel olarak başlatılmasını ve ayrıca bu çalışmanın yapılmasını istiyorsanız, bu kod snippet'ini aşağıdakilere eklemek daha iyidir 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>

Bu kodun yaptığı, sizin uzunluğunuzu artıracağı JPA sessionveya belgelerin söylediği gibi, "to allow for lazy loading in web views despite the original transactions already being completed."JPA oturumunun biraz daha uzun açılacağı ve bu nedenle jsp dosyalarınızda ve denetleyici sınıflarınızda koleksiyonları tembel olarak yükleyebileceğiniz şekilde kullanılmasıdır. .


7
JPS oturumu neden kapalı? Kapatılmaması nasıl yapılır? Tembel koleksiyon nasıl yapılır?
Dims

1
Varlık başına iki FetchType.Eager koleksiyonu sınırını ne tanımlar?
chrisinmtown

Spring Boot'da, 'application.properties' e 'spring.jpa.open-in-view = true' ekleyebilirsiniz
Askar

28

Bunun nedeni, tembel yük kullandığınızda oturumun kapatılmasıdır.

İki çözüm var.

  1. Tembel yük kullanmayın.

    Set lazy=falseXML veya Set içinde @OneToMany(fetch = FetchType.EAGER)de ek açıklama.

  2. Tembel yük kullanın.

    Set lazy=trueXML veya Set içinde @OneToMany(fetch = FetchType.LAZY)de ek açıklama.

    ve ekleme OpenSessionInViewFilter filterde seninweb.xml

Detay benim Bkz POST .


1
... ve yine de her iki çözüm de iyi değil. EAGER kullanmanızı önermek büyük sorunlar yaratabilir. OpenSessionInViewFilter kullanmak bir anti-kalıptır.
Rafael


22

Sorun, hazırda bekletme oturumu kapalıyken bir özniteliğe erişilmesinden kaynaklanır. Denetleyicide hazırda bekletme modunuz yok.

Olası çözümler:

  1. Tüm bu mantığı denetleyicide değil , hizmet katmanında (@Transactional ile) yapın. Bunu yapmak için doğru yer olmalı, kontrol cihazında değil, uygulamanın mantığının bir parçasıdır (bu durumda, modeli yüklemek için bir arayüz). Hizmet katmanındaki tüm işlemler işlemsel olmalıdır. ie: Bu satırı TopicService.findTopicByID yöntemine taşıyın:

    Koleksiyon commentList = topicById.getComments ();

  2. 'Tembel' yerine 'istekli' kullanın . Şimdi 'tembel' kullanmıyorsunuz .. bu gerçek bir çözüm değil, eğer tembel kullanmak istiyorsanız, geçici (çok geçici) bir geçici çözüm gibi çalışır.

  3. Denetleyicide @ Transactional kullanın . Burada kullanılmamalı, servis katmanını sunumla karıştırıyorsunuz, iyi bir tasarım değil.
  4. OpenSessionInViewFilter kullanın , bildirilen birçok dezavantaj, olası kararsızlık.

Genel olarak, en iyi çözüm 1'dir.


2
Eager getirme, hazırda bekletme modunun ilk sorgudaki tüm verilerin doğru olarak
alınacağını varsaydığını varsayıyor

EN İYİ ÇÖZÜM 1 OLDUĞUNU ÜSTELMELİSİNİZ ... aslında tüm diğerleri anti-desen olduğundan TEK SADECE çözümdür!
Rafael

19

Bir koleksiyonu tembel yüklemek için etkin bir oturum olması gerekir. Bir web uygulamasında bunu yapmanın iki yolu vardır. Sen kullanabilirsiniz Açık Oturum Görüntüle bir kullanmak deseni, önleme isteği başında oturumu açıp sonunda kapatmak için. Buradaki risk, katı bir istisna yönetimine sahip olmanız veya tüm oturumlarınızı bağlamanız ve uygulamanızın askıda kalmasıdır.

Bunu ele almanın diğer yolu, kontrol cihazınızda ihtiyacınız olan tüm verileri toplamak, oturumunuzu kapatmak ve ardından verileri modelinize doldurmaktır. Ben şahsen bu yaklaşımı tercih ediyorum, çünkü MVC modelinin ruhuna biraz daha yakın görünüyor. Ayrıca veritabanından bu şekilde bir hata alırsanız, bunu görünüm oluşturucunuzda olduğundan çok daha iyi işleyebilirsiniz. Bu senaryoda arkadaşınız Hibernate.initialize (myTopic.getComments ()). Ayrıca, her istekte yeni bir işlem oluşturduğunuz için nesneyi oturuma yeniden bağlamanız gerekir. Bunun için session.lock (myTopic, LockMode.NONE) kullanın.


15

Bu makalede açıkladığım gibi , işlemek için en iyi yolu, LazyInitializationExceptionsorgu süresi üzerine aşağıdaki gibi getirmektir:

select t
from Topic t
left join fetch t.comments

DAİMA aşağıdaki anti-kalıplardan kaçınmalısınız:

Bu nedenle, FetchType.LAZYilişkilendirmelerinizin ikincil koleksiyonlar için sorgu zamanında veya orijinal @Transactionalkapsam içinde başlatıldığından emin olun Hibernate.initialize.


1
Vlad, Spring tarafından üretilen bir deponun findById () yöntemi tarafından getirilen bir varlıkta tembel olarak başlatılan bir koleksiyonla çalışmak için herhangi bir öneriniz var mı? Sorguyu yazmıyorum ve işlem kodumun dışında.
chrisinmtown

Tembel koleksiyonları başlatma hakkında daha fazla bilgi için bu makaleye göz atın .
Vlad Mihalcea

'Orijinal @Transactional kapsamı içinde' ile ne demek istediğinizi açıklığa kavuşturabilir misiniz? Bu açık bir oturumdayken (ama doğru olanı değil) bu hatayı aldığım için benim için net değil mi?
Michiel Haisma

Kapsam içinde iken o da işlem ağ geçidi olarak da bilinen en üst işlem hizmet yöntemi. TrassctionInterceptorYığın izlemesini kontrol edin ve işte bu.
Vlad Mihalcea

Şimdiye kadarki en iyi cevaplardan biri ... bu doğru olarak işaretlenmelidir. BTW ... OSIV'ün bir anti-desen olduğu varsayılarak, son sürüm önyükleme son sürümlerinde varsayılan olarak nasıl etkinleştirilebilir? ... belki o kadar da kötü değil mi?
Rafael

10

Bir varlık ile bir Koleksiyon veya java nesnelerinin listesi (örneğin, Uzun tip) arasında bir ilişki kurmaya çalışıyorsanız, şöyle bir şey istersiniz:

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

1
çoğu durumda, bunu gerçekten yapmak istemezsiniz. Burada tembel yükleme tüm faydalarını kaybedersiniz
kiedysktos

EAGER kullanmak profesyonel bir çözüm değildir.
Rafael

9

En iyi çözümlerden biri application.properties dosyanıza aşağıdakileri eklemektir: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true


1
OP'ye tam olarak ne yaptığını, herhangi bir yan etkiyi, performans etkisini söyleyebilir misiniz?
PeS

3
Tembel yüklemenin arkasında, bir ilişkilendirme tembel olarak her yüklendiğinde yeni bir oturum çatallanır, böylece daha fazla bağlantı çatallanır ve bağlantı havuzu üzerinde biraz baskı oluşturur. Bağlantı sayısında bir sınırınız varsa, bu özellik kullanmak için doğru olmayabilir.
sreekmatta

2
bazıları için anti-desen olarak kabul edilir vladmihalcea.com/…
Uri Loya

7

Ben ilan öğrendim @PersistenceContextolarak EXTENDEDda bu sorunu çözer:

@PersistenceContext(type = PersistenceContextType.EXTENDED)

1
Merhaba, bu tür değişikliklere dikkat edin. TRANSACTION kapsamındaki kalıcılık bağlamı oluşturma, OP amacı olan tembeldir. Yani soru, vatansız olmak isteyip istemediğinizdir. Bu ayar sistemin amacına bağlıdır ve değiştirilmemelidir ... hevesle. Eğer ne demek istediğimi anlıyorsan. Burada okuyun stackoverflow.com/questions/2547817/…
kiedysktos

Tehlikeli. Bu doğru cevap değil. Yukarıda çok daha doğru ve güvenli diğerleri var.
Rafael

5

Son zamanlarda karşılaştığım sorunla çözdüm

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

burada daha ayrıntılı bir açıklama ve bu benim günümü kurtardı.


5

listeniz tembel yükleniyor, bu yüzden liste yüklenmedi. listeye girmek için çağrı yeterli değil. listeyi başlatmak için Hibernate.initialize öğesinde kullanın. Dosnt çalışması liste öğesinde çalışır ve Hibernate.initialize her biri için arayın. bunun işlem kapsamından dönmeden önce olması gerekir. bakmak bu yazı.
aramak -

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

4

Benim durumumdaki sorunu çözmek için sadece bu satırı kaçırmıştım

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

uygulama bağlam dosyasında.

@TransactionalBir yöntem üzerinde açıklama dikkate alınmadı.

Umarım cevap birine yardımcı olur


4

@ Denetleyicideki işlem ek açıklaması eksik

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

17
İşlem yönetiminin, iş mantığının bulunduğu hizmet katmanına ait olduğunu iddia ediyorum.
Sõber

İşlem ek açıklaması eksik. Denetleyicide böyle bir açıklama bulunmamalıdır. Bu ek açıklamalar Hizmet düzeyinde olmalıdır.
Rafael

4

Hazırda bekletme @Transactionalek açıklamasını kullanarak, veritabanından tembel getirilen özniteliklere sahip bir nesne alırsanız, bunları aşağıdaki gibi öznitelikleri getirerek elde edebilirsiniz:

@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();
        });
}

Burada, Hazırda Bekletme proxy'si tarafından yönetilen bir işlemde, ticket.getSales()açıkça sormanız nedeniyle satış getirmek için başka bir sorgu yapma çağrısı gerçeği .


4

Sizin için iki şey olmalı fetch = FetchType.LAZY.

@Transactional

ve

Hibernate.initialize(topicById.getComments());

2

Kriterlerle çalışanlar için şunu buldum:

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

ihtiyacım olan her şeyi yapmıştı.

Koleksiyonlar için ilk getirme modu performans sağlamak için FetchMode.LAZY olarak ayarlanmıştır, ancak verilere ihtiyacım olduğunda, sadece bu satırı ekler ve tamamen doldurulmuş nesnelerin tadını çıkarırım.


2

Benim durumumda aşağıdaki kod bir sorun oldu:

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

Veritabanından ayrıldığı ve Hazırda Bekletme listesinin artık gerektiğinde alandan alınmadığı için. Bu yüzden ayırmadan önce başlatırım:

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

1

Bunun nedeni, hizmet içindeki oturumu kapattıktan sonra denetleyicinize commentList'i almaya çalışmanızdır.

topicById.getComments();

Yukarıdaki commentList dosyasını yalnızca hazırda bekletme oturumunuz etkinse yükler;

Bu nedenle, oturumu kapatmadan önce commentList'ini almanız gerekir.


2
Evet, bu sorun ifadesidir, ayrıca bir cevap Answer
vermelisiniz

1

commentsModel sınıfınızdaki koleksiyon Topictembel olarak yüklenir; bu, fetch = FetchType.EAGERözellikle ek açıklama eklemezseniz varsayılan davranıştır .

Büyük olasılıkla findTopicByIDhizmetinizin durum bilgisi olmayan bir Hazırda Bekletme oturumu kullanıyor olması muhtemeldir . Durum bilgisi olmayan bir oturumun ilk düzey önbelleği yoktur, yani kalıcılık bağlamı yoktur. Daha sonra yinelemeye çalıştığınızda comments, Hibernate bir istisna atar.

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

Çözüm şu olabilir:

  1. Annotatesekmesindeki commentsilefetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
  2. Yorumların tembel olarak yüklenmesini istiyorsanız, Hazırda Bekleme'nin durum bilgisi olan oturumlarını kullanın , böylece daha sonra talep üzerine yorumları alabilirsiniz.


1

Benim durumumda, b / w Ave Bbenzeri eşleme vardı

A vardır

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

içinde DAOkatmana, yöntem ek not edilmesi gerekmektedir @TransactionalBirlikte eşleme açıklamalı değil eğer Fetch Tip - Hevesli


1

En iyi çözüm değil, LazyInitializationExceptionözellikle Serializationbununla karşı karşıya olanlar için yardımcı olacaktır. Burada tembel olarak başlatılan özellikleri ve bunların ayarlarını kontrol edeceksiniz null. Bunun için aşağıdaki sınıfı oluşturun

public class RepositoryUtil {
    public static final boolean isCollectionInitialized(Collection<?> collection) {
        if (collection instanceof PersistentCollection)
            return ((PersistentCollection) collection).wasInitialized();
        else 
            return true;
    }   
}

Tembel olarak başlatılan özelliklere sahip olduğunuz Entity sınıfınızın içine aşağıda gösterilen gibi bir yöntem ekleyin. Tüm tembel yükleme özelliklerinizi bu yönteme ekleyin.

public void checkLazyIntialzation() {
    if (!RepositoryUtil.isCollectionInitialized(yourlazyproperty)) {
        yourlazyproperty= null;
    }

checkLazyIntialzation()Veri yüklediğiniz tüm yerlerde bu yöntemi çağırın .

 YourEntity obj= entityManager.find(YourEntity.class,1L);
  obj.checkLazyIntialzation();

0

Merhaba Tüm gönderiler oldukça geç umut diğerleri yardımcı olur, bu yazı için @ GMK için şimdiden teşekkür Hibernate.initialize (nesne)

Lazy = "true" olduğunda

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

şimdi oturum kapattıktan sonra 'set' erişirseniz istisna atar.

Çözümüm :

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

Şimdi Hazırda Bekletme Oturumunu kapattıktan sonra bile 'set'e erişebiliyorum.


0

Bunu yapmanın başka bir yolu da , tembel getirmeyi sarmak için TransactionTemplate'i kullanabilirsiniz . Sevmek

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

0

Soruna, veritabanına "bağlantı" kapatıldığında kodun tembel bir JPA ilişkisine erişmesi nedeniyle oluşur ( kalıcılık bağlamı Hazırda Bekletme / JPA açısından doğru addır).

Spring Boot'da çözmenin basit bir yolu, bir hizmet katmanı tanımlamak ve @Transactionalek açıklamayı kullanmaktır . Bir yöntemdeki bu ek açıklama, veri havuzu katmanına yayılan bir işlem oluşturur ve yöntem bitene kadar kalıcılık bağlamını açık tutar. Koleksiyona işlem yöntemiyle erişirseniz, Hibernate / JPA verileri veritabanından alır.

Sizin durumunuzda, yalnızca @Transactionalyönteminizdeki açıklamaya ek açıklama findTopicByID(id)eklemeniz TopicServiceve bu yöntemdeki koleksiyonun getirilmesini zorlamanız gerekir (örneğin, boyutunu sorarak):

    @Transactional(readOnly = true)
    public Topic findTopicById(Long id) {
        Topic topic = TopicRepository.findById(id).orElse(null);
        topic.getComments().size();
        return topic;
    }

0

Tembel başlatma istisnasından kurtulmak için, müstakil nesne ile çalışırken tembel toplama çağırmamalısınız.

Benim düşünceme göre, en iyi yaklaşım varlık yerine DTO kullanmaktır. Bu durumda, kullanmak istediğiniz alanları açıkça ayarlayabilirsiniz. Her zamanki gibi yeterli. Jackson gibi ObjectMapperveya hashCodeLombok tarafından üretilen bir şeyin yöntemlerinizi dolaylı olarak çağıracağından endişelenmenize gerek yok .

Bazı özel durumlarda , kuruluşunuzda olsa bile yük @EntityGrpapholuşturmanıza olanak tanıyan ek açıklama kullanabilirsiniz .eagerfetchType=lazy


0

Bu Tembel Başlatma sorunu için birden fazla çözüm var -

1) Fetch türünü LAZY'den EAGER'a değiştirin, ancak bu iyi bir uygulama değildir, çünkü bu performansı düşürecektir.

2) İlişkili Nesnede FetchType.LAZY kullanın ve ayrıca hizmet katmanı yönteminizde İşlemsel ek açıklama kullanın, böylece oturum açık kalır ve topicById.getComments () öğesini ne zaman çağırırsanız alt nesne (yorumlar) yüklenir.

3) Ayrıca, denetleyici katmanında varlık yerine DTO nesnesini kullanmayı deneyin. Sizin durumunuzda, oturum denetleyici katmanında kapatılır. Varlığı hizmet katmanında DTO'ya dönüştürmek çok daha iyi.


-11

Set yerine List kullanarak çözdüm:

private List<Categories> children = new ArrayList<Categories>();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.