Cách giải quyết các lỗi thất bại trong việc khởi tạo một cách lười biếng một bộ sưu tập các vai trò ngoại lệ Hibernate


363

Tôi có vấn đề này:

org.hibernate.LazyInitializationException: thất bại trong việc khởi tạo một bộ sưu tập vai trò: mvc3.model.Topic.comments, không có phiên hoặc phiên nào bị đóng

Đây là mô hình:

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

}

Bộ điều khiển, gọi mô hình trông như sau:

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

     }

}

Trang jsp trông li như sau:

<%@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>

Ngoại lệ được tăng lên, khi xem jsp. Trong dòng có vòng lặp c: forEach

Câu trả lời:


214

Nếu bạn biết rằng bạn sẽ muốn xem tất cả Commentmỗi lần bạn truy xuất Topicthì hãy thay đổi ánh xạ trường của bạn commentsthành:

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

Bộ sưu tập được tải theo mặc định, hãy xem cái này nếu bạn muốn biết thêm.


35
Xin lỗi, nhưng tôi muốn sử dụng tải chậm. Vì vậy, tôi đã thay đổi loại 'LinkedHashset' t 'Danh sách liên tục'. Ngoại lệ vẫn xảy ra
Eugene

242
Điều này có thể được sử dụng như một cách giải quyết, nhưng không phải là một giải pháp thực sự cho vấn đề. Điều gì nếu chúng ta cần lấy một cách lười biếng?
Dkyc

14
nhưng trong trường hợp nếu chúng ta muốn lười biếng thì giải pháp này sẽ không hiệu quả và hầu hết các trường hợp chúng ta chỉ muốn lười biếng.
prashant thakre

103
Đây là loại câu trả lời bật lên ở mọi nơi trên stack stack. Ngắn gọn, đến mức, giải quyết vấn đề và MISLEADING. Đối với những độc giả tương lai, hãy tạo cho mình một sự ưu ái và tìm hiểu chính xác những gì lười biếng và háo hức, và hiểu hậu quả.
Ced

13
@darrengorman Khi tôi bắt đầu JPA, tôi đã đăng một câu hỏi xung quanh các dòng của OP. Tôi đã nhận được phản hồi giống như bạn đã đưa ra. Chẳng mấy chốc khi tôi chạy thử nghiệm với hàng trăm ngàn hàng, hãy đoán xem điều gì đã xảy ra? Tôi nghĩ đó là sai lầm vì nó cung cấp câu trả lời quá đơn giản cho một vấn đề mà hầu hết những người mới bắt đầu sẽ phải đối mặt và họ sẽ sớm tải toàn bộ cơ sở dữ liệu vào bộ nhớ nếu không cẩn thận (và họ sẽ không làm thế, vì họ sẽ không hãy nhận biết về nó) :).
Ced

182

Từ kinh nghiệm của mình, tôi có các phương pháp sau để giải quyết LazyInitializationException nổi tiếng:

(1) Sử dụng Hibernate.initialize

Hibernate.initialize(topics.getComments());

(2) Sử dụng THAM GIA

Bạn có thể sử dụng cú pháp THAM GIA FETCH trong JPQL của mình để tìm nạp bộ sưu tập con một cách rõ ràng. Đây là một số cách như tìm nạp EAGER.

(3) Sử dụng OpenSessionInViewFilter

LazyInitializationException thường xảy ra trong lớp xem. Nếu bạn sử dụng Spring framework, bạn có thể sử dụng OpenSessionInViewFilter. Tuy nhiên, tôi không đề nghị bạn làm như vậy. Nó có thể dẫn đến vấn đề hiệu suất nếu không sử dụng đúng cách.


5
(1) làm việc cho tôi hoàn hảo. Trường hợp của tôi: Hibernate.initialize (registry.getVehicle (). GetOwner (). GetPerson (). GetAddress ());
Leonel Sanches da Silva

6
Có vẻ như Hibernate.initialize không hoạt động với EntityManager
marion maiden

8
Đây phải là câu trả lời chính xác. Ví dụ, trong dự án của tôi tại nơi làm việc, chúng tôi rõ ràng không nên sử dụng tìm nạp EAGER. Nó gây ra vấn đề trong hệ thống đặc biệt này.
Steve Waters

Có vẻ hấp dẫn nhưng thiếu tài liệu để thực hiện trong trường hợp khác ... bạn có thể vui lòng cung cấp thêm một số liên kết hoặc giải thích về cách triển khai giải pháp này không?
Pipo

58

Tôi biết đó là một câu hỏi cũ nhưng tôi muốn giúp đỡ. Bạn có thể đặt chú thích giao dịch vào phương thức dịch vụ bạn cần, trong trường hợp này findTopicByID (id) nên có

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

Thông tin thêm về chú thích này có thể được tìm thấy ở đây

Về các giải pháp khác:

fetch = FetchType.EAGER 

không phải là một thực hành tốt, nó chỉ nên được sử dụng nếu cần thiết.

Hibernate.initialize(topics.getComments());

Trình khởi tạo ngủ đông liên kết các lớp của bạn với công nghệ ngủ đông. Nếu bạn đang hướng đến sự linh hoạt không phải là một cách tốt để đi.

Hy vọng nó giúp


3
Chú thích @Transactional hoạt động với tôi, nhưng lưu ý rằng Tuyên truyền.REQUIRED là mặc định, ít nhất là trong Spring Boot 1.4.2 (Spring 4.3).
ben3000

4
Đúng vậy, nhưng tôi nghĩ nó có thể được đánh giá cao để làm rõ rằng bạn thực sự có thể thay đổi thông số tuyên truyền
sarbuLopex

Không phải là @Transactionalmột điều duy nhất cho mùa xuân sao?
Campa

@Campa đúng vậy. Nếu bạn muốn xử lý thủ công, bạn nên đặt logic nghiệp vụ của mình vào trong giao dịch được truy xuất từ ​​trình quản lý thực thể
sarbuLopex

54

Nguồn gốc của vấn đề của bạn:

Theo mặc định, ngủ đông tải các bộ sưu tập (các mối quan hệ) có nghĩa là khi bạn sử dụng collectionmã của mình ( commentstrường ở đây trong Topiclớp), chế độ ngủ đông nhận được từ cơ sở dữ liệu, bây giờ vấn đề là bạn đang lấy bộ sưu tập trong bộ điều khiển của mình (trong đó phiên JPA đã đóng). Đây là dòng mã gây ra ngoại lệ (nơi bạn đang tải commentsbộ sưu tập):

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

Bạn đang nhận được bộ sưu tập "ý kiến" (topic.getComments ()) trong bộ điều khiển của bạn (nơi JPA sessionđã kết thúc) và điều đó gây ra ngoại lệ. Ngoài ra nếu bạn đã có commentsbộ sưu tập trong tệp jsp của mình như thế này (thay vì lấy nó trong bộ điều khiển của bạn):

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

Bạn vẫn sẽ có ngoại lệ tương tự cho cùng một lý do.

Giải quyết vấn đề:

Bởi vì bạn chỉ có thể có hai bộ sưu tập với FetchType.Eager(bộ sưu tập háo hức) trong một lớp Thực thể và vì tải lười biếng hiệu quả hơn tải háo hức, tôi nghĩ cách giải quyết vấn đề của bạn tốt hơn là chỉ thay đổi FetchTypethành háo hức:

Nếu bạn muốn có bộ sưu tập lười biếng được khởi tạo và cũng làm cho công việc này hoạt động, tốt hơn là thêm đoạn mã này vào 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>

Mã này làm gì là nó sẽ tăng thời lượng của bạn JPA sessionhoặc như tài liệu nói, nó được sử dụng "to allow for lazy loading in web views despite the original transactions already being completed."để theo cách này, phiên JPA sẽ mở lâu hơn một chút và do đó bạn có thể tải các bộ sưu tập trong các tệp jsp và các lớp trình điều khiển của mình một cách lười biếng .


7
Tại sao phiên JPS đóng cửa? Làm thế nào để làm cho nó không bị đóng cửa? Làm thế nào để thực hiện bộ sưu tập lười biếng?
dims

1
Điều gì xác định giới hạn của hai bộ sưu tập FetchType.Eager cho mỗi Thực thể?
chrisinmtown

Trong Spring Boot, bạn có thể thêm 'spring.jpa.open-in-view = true' vào 'application.properies'
Askar

28

Lý do là khi bạn sử dụng tải lười biếng, phiên bị đóng.

Có hai giải pháp.

  1. Đừng sử dụng tải lười biếng.

    Đặt lazy=falsetrong XML hoặc Đặt @OneToMany(fetch = FetchType.EAGER)trong chú thích.

  2. Sử dụng tải lười biếng.

    Đặt lazy=truetrong XML hoặc Đặt @OneToMany(fetch = FetchType.LAZY)trong chú thích.

    và thêm OpenSessionInViewFilter filtervào của bạnweb.xml

Xem chi tiết BÀI ĐĂNG của tôi .


1
... và cả hai giải pháp đều không tốt. Đề xuất sử dụng EAGER có thể tạo ra các vấn đề lớn. Sử dụng OpenSessionInViewFilter là một mô hình chống.
Rafael

27
@Controller
@RequestMapping(value = "/topic")
@Transactional

tôi giải quyết vấn đề này bằng cách thêm vào @Transactional, tôi nghĩ rằng điều này có thể làm cho phiên mở


Tại sao điều này nhận được một phiếu bầu trừ? Thêm một giao dịch vào hoạt động kéo dài phiên
Tudor Grigoriu

1
Đó là một thực tế xấu khi quảng cáo @Transactional cho bộ điều khiển.
Rafael

@Rafael Tại sao đó là một thực hành xấu?
Amr Ellafy

@AmrEllafy -> Đây là một lời giải thích hay: stackoverflow.com/a/18498834/1261162
Rafael

22

Vấn đề được gây ra bởi việc truy cập một thuộc tính với phiên ngủ đông đã đóng. Bạn không có giao dịch ngủ đông trong bộ điều khiển.

Phương pháp khả thi:

  1. Thực hiện tất cả logic này, trong lớp dịch vụ , (với @Transactional), không phải trong bộ điều khiển. Cần có đúng nơi để làm điều này, nó là một phần logic của ứng dụng, không phải trong bộ điều khiển (trong trường hợp này là giao diện để tải mô hình). Tất cả các hoạt động trong lớp dịch vụ nên được giao dịch. tức là: Di chuyển dòng này sang phương thức TopicService.findTopicByID:

    Bộ sưu tập bình luậnList = topicById.getComments ();

  2. Sử dụng 'háo hức' thay vì 'lười biếng' . Bây giờ bạn không sử dụng 'lười biếng' .. đó không phải là một giải pháp thực sự, nếu bạn muốn sử dụng lười biếng, hãy làm việc như một cách giải quyết tạm thời (rất tạm thời).

  3. sử dụng @Transactional trong Bộ điều khiển . Nó không nên được sử dụng ở đây, bạn đang trộn lớp dịch vụ với trình bày, nó không phải là một thiết kế tốt.
  4. sử dụng OpenSessionInViewFilter , nhiều nhược điểm được báo cáo, có thể không ổn định.

Nói chung, giải pháp tốt nhất là 1.


2
Fetch loại Háo hức cho rằng ngủ đông sẽ được kéo tất cả dữ liệu trong truy vấn đầu tiên, không phải tất cả những nơi đó là một cách chính xác
Жасулан Бердибеков

Bạn nên NÂNG CẤP rằng GIẢI PHÁP TỐT NHẤT LÀ 1 ... trên thực tế là giải pháp TỐT NHẤT vì tất cả các giải pháp khác đều chống lại mô hình!
Rafael

19

Để lười tải một bộ sưu tập phải có một phiên hoạt động. Trong một ứng dụng web có hai cách để làm điều này. Bạn có thể sử dụng mẫu Mở phiên trong Chế độ xem , trong đó bạn sử dụng thiết bị chặn để mở phiên ở đầu yêu cầu và đóng ở cuối. Rủi ro ở đây là bạn phải xử lý ngoại lệ vững chắc hoặc bạn có thể ràng buộc tất cả các phiên của mình và ứng dụng của bạn có thể bị treo.

Một cách khác để xử lý việc này là thu thập tất cả dữ liệu bạn cần trong bộ điều khiển của bạn, đóng phiên của bạn và sau đó nhét dữ liệu vào mô hình của bạn. Cá nhân tôi thích cách tiếp cận này, vì nó có vẻ gần hơn với tinh thần của mẫu MVC. Ngoài ra nếu bạn gặp lỗi từ cơ sở dữ liệu theo cách này, bạn có thể xử lý nó tốt hơn rất nhiều so với nếu nó xảy ra trong trình kết xuất chế độ xem của bạn. Bạn của bạn trong kịch bản này là Hibernate.initialize (myTopic.getComments ()). Bạn cũng sẽ phải gắn lại đối tượng vào phiên vì bạn đang tạo một giao dịch mới với mỗi yêu cầu. Sử dụng session.lock (myTopic, LockMode.NONE) cho điều đó.


15

Như tôi đã giải thích trong bài viết này , cách tốt nhất để xử lý LazyInitializationExceptionlà tìm nạp nó theo thời gian truy vấn, như thế này:

select t
from Topic t
left join fetch t.comments

Bạn nên LUÔN tránh các kiểu chống sau:

Do đó, hãy đảm bảo rằng các FetchType.LAZYliên kết của bạn được khởi tạo tại thời điểm truy vấn hoặc trong @Transactionalphạm vi ban đầu sử dụng Hibernate.initializecho các bộ sưu tập thứ cấp.


1
Bạn có bất cứ đề xuất nào để làm việc với một bộ sưu tập được khởi tạo lười biếng trong một thực thể được tìm nạp bởi phương thức findById () của kho lưu trữ được tạo bởi Spring không? Tôi không viết truy vấn và giao dịch nằm ngoài mã của tôi.
chrisinmtown

Kiểm tra bài viết này để biết thêm chi tiết về việc khởi tạo các bộ sưu tập lười biếng.
Vlad Mihalcea

Bạn có thể làm rõ ý của bạn bằng cách 'trong phạm vi @Transactional ban đầu' Điều này không rõ ràng với tôi vì tôi dường như gặp phải lỗi này trong khi trong một phiên mở (nhưng không phải là đúng?)
Michiel Haisma 21/03/19

Trong khi bên trong phạm vi o phương thức dịch vụ giao dịch hàng đầu, còn được gọi là cổng giao dịch. Kiểm tra TrassctionInterceptordấu vết trong ngăn xếp và đó là cái.
Vlad Mihalcea

Một trong những câu trả lời tốt nhất cho đến nay ... điều này nên được đánh dấu là chính xác. BTW ... giả sử OSIV là một mẫu chống sao có thể được bật theo mặc định trên các phiên bản khởi động mùa xuân gần đây? ... Có lẽ điều đó không tệ?
Rafael

10

Nếu bạn đang cố gắng có mối quan hệ giữa một thực thể và Bộ sưu tập hoặc Danh sách các đối tượng java (ví dụ Loại dài), thì nó sẽ giống như thế này:

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

1
trong nhiều trường hợp, bạn thực sự không muốn làm điều đó. Bạn mất tất cả những lợi ích của việc lười tải ở đây
kiedysktos

Sử dụng EAGER không phải là một giải pháp chuyên nghiệp.
Rafael

9

Một trong những giải pháp tốt nhất là thêm phần sau vào tệp application.properIES của bạn: spring.jpa.properIES.hibernate.enable_lazy_load_no_trans = true


1
Bạn có thể nói với OP những gì nó làm chính xác, bất kỳ tác dụng phụ, tác động hiệu suất?
PeS

3
Ở mặt sau của tải lười biếng, một phiên mới được chia rẽ mỗi khi một liên kết được tải một cách lười biếng, do đó nhiều kết nối được chia rẽ và tạo ra một chút áp lực cho nhóm kết nối. Nếu bạn có giới hạn về số lượng kết nối thì thuộc tính này có thể không phải là kết nối chính xác để sử dụng.
sreekmatta

2
đối với một số người, nó được coi là một vladmihalcea.com/
Uri Loya

7

Tôi phát hiện ra rằng tuyên bố @PersistenceContextnhư EXTENDEDcũng giải quyết vấn đề này:

@PersistenceContext(type = PersistenceContextType.EXTENDED)

1
Xin chào, hãy cẩn thận với những thay đổi như vậy. GIAO DỊCH phạm vi liên tục tạo ra bối cảnh là lười biếng, đó là ý định của OP. Vì vậy, câu hỏi là bạn có muốn không quốc tịch hay không. Cài đặt này phụ thuộc vào mục đích của hệ thống và không nên thay đổi quá ... háo hức. Nếu bạn hiểu ý tôi. Đọc ở đây stackoverflow.com/questions/2547817/
Mạnh

Nguy hiểm. Đây không phải là câu trả lời chính xác. Có những người khác ở trên chính xác hơn và an toàn.
Rafael

5

đó là vấn đề gần đây tôi gặp phải mà tôi đã giải quyết bằng cách sử dụng

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

decription chi tiết hơn ở đây và điều này đã cứu ngày của tôi.


5

danh sách của bạn lười tải, vì vậy danh sách không được tải. gọi để có được trong danh sách là không đủ. sử dụng trong Hibernate.initialize để khởi tạo danh sách. Nếu công việc dosnt chạy trên phần tử danh sách và gọi Hibernate.initialize cho mỗi phần tử. điều này cần phải có trước khi bạn trở về từ phạm vi giao dịch. nhìn vào bài này
tìm kiếm -

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

Để giải quyết vấn đề trong trường hợp của tôi, nó chỉ thiếu dòng này

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

trong tập tin ngữ cảnh ứng dụng.

Các @Transactionalchú thích trên một phương pháp đã không được đưa vào tính toán.

Hy vọng câu trả lời sẽ giúp được ai đó


4

Chú thích @Transactional trên bộ điều khiển bị thiếu

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

17
Tôi sẽ lập luận rằng quản lý giao dịch thuộc về lớp dịch vụ nơi logic kinh doanh cư trú.
Sõber

Chú thích giao dịch không thiếu. Bộ điều khiển không nên có chú thích như vậy. Những chú thích nên ở cấp độ Dịch vụ.
Rafael

4

Bằng cách sử dụng @Transactionalchú thích ngủ đông , nếu bạn nhận được một đối tượng từ cơ sở dữ liệu với các thuộc tính được tìm nạp lười biếng, bạn có thể chỉ cần lấy chúng bằng cách tìm nạp các thuộc tính như thế này:

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

Ở đây, trong một giao dịch được quản lý proxy Hibernate, thực tế gọi điện ticket.getSales()thực hiện một truy vấn khác để lấy doanh số vì bạn đã hỏi rõ ràng về nó.


4

Hai điều bạn nên có fetch = FetchType.LAZY.

@Transactional

Hibernate.initialize(topicById.getComments());

2

Đối với những người làm việc với Tiêu chí , tôi thấy rằng

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

đã làm mọi thứ tôi cần đã làm.

Chế độ tìm nạp ban đầu cho các bộ sưu tập được đặt thành FetchMode.LAZY để cung cấp hiệu suất, nhưng khi tôi cần dữ liệu, tôi chỉ cần thêm dòng đó và thưởng thức các đối tượng được điền đầy đủ.


2

Trong trường hợp của tôi, mã sau đây là một vấn đề:

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

Bởi vì nó tách ra khỏi cơ sở dữ liệu và Hibernate không còn lấy danh sách từ trường khi cần. Vì vậy, tôi khởi tạo nó trước khi tách ra:

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

1

Lý do là bạn đang cố lấy bình luận trên bộ điều khiển của mình sau khi đóng phiên bên trong dịch vụ.

topicById.getComments();

Ở trên sẽ tải bình luận Danh sách chỉ khi phiên ngủ đông của bạn hoạt động, mà tôi đoán bạn đã đóng trong dịch vụ của mình.

Vì vậy, bạn phải lấy bình luận ListList trước khi kết thúc phiên.


2
Vâng, đây là tuyên bố vấn đề, Bạn cũng nên cung cấp câu trả lời trong mộtAnswer
Sarz

1

Bộ sưu tập commentstrong lớp mô hình của bạn Topicđược tải một cách lười biếng, đó là hành vi mặc định nếu bạn không chú thích fetch = FetchType.EAGERcụ thể.

Rất có khả năng findTopicByIDdịch vụ của bạn đang sử dụng phiên Hibernate không trạng thái. Một phiên không trạng thái không có bộ đệm cấp đầu tiên, nghĩa là không có bối cảnh tồn tại. Sau này khi bạn cố gắng lặp lại comments, Hibernate sẽ đưa ra một ngoại lệ.

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

Giải pháp có thể là:

  1. Chú thích commentsvớifetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
  2. Nếu bạn vẫn muốn các bình luận được tải một cách lười biếng, hãy sử dụng các phiên có trạng thái của Hibernate để bạn có thể lấy các bình luận sau này theo yêu cầu.


1

Trong trường hợp của tôi, tôi đã có ánh xạ b / w AB thích

A

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

trong DAOlớp, phương thức cần được chú thích @Transactionalnếu bạn chưa chú thích ánh xạ với Fetch Type - Eager


1

Không phải là giải pháp tốt nhất, nhưng đối với những người đang phải đối mặt LazyInitializationExceptionđặc biệt là Serializationđiều này sẽ giúp ích. Tại đây bạn sẽ kiểm tra các thuộc tính khởi tạo uể oải và thiết lập các thuộc tính nullđó. Cho rằng tạo lớp dưới đây

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

Trong lớp Thực thể mà bạn đang có các thuộc tính khởi tạo uể oải thêm một phương thức như hiển thị bên dưới. Thêm tất cả các thuộc tính tải lười biếng của bạn bên trong phương pháp này.

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

Gọi checkLazyIntialzation()phương thức này sau trên tất cả những nơi bạn đang tải dữ liệu.

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

0

Xin chào Tất cả các bài đăng khá muộn hy vọng nó sẽ giúp người khác, Cảm ơn trước @GMK cho bài đăng này Hibernate.initialize (object)

khi lười = "đúng"

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

Bây giờ nếu tôi truy cập 'set' sau khi đóng phiên thì nó sẽ ném ngoại lệ.

Giải pháp của tôi :

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

bây giờ tôi có thể truy cập 'set' ngay cả sau khi đóng Phiên Hibernate.


0

Tuy nhiên, một cách khác để làm điều đó, bạn có thể sử dụng TransactionTemplate để quấn quanh việc tìm nạp lười biếng. Giống

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

0

Vấn đề được gây ra bởi vì mã đang truy cập vào mối quan hệ JPA lười biếng khi "kết nối" với cơ sở dữ liệu bị đóng ( bối cảnh tồn tại là tên chính xác theo thuật ngữ Hibernate / JPA).

Một cách đơn giản để giải quyết nó trong Spring Boot là xác định một lớp dịch vụ và sử dụng @Transactional chú thích. Chú thích này trong một phương thức tạo ra một giao dịch lan truyền vào lớp kho lưu trữ và tiếp tục mở bối cảnh bền vững cho đến khi phương thức kết thúc. Nếu bạn truy cập vào bộ sưu tập bên trong phương thức giao dịch Hibernate / JPA sẽ lấy dữ liệu từ cơ sở dữ liệu.

Trong trường hợp của bạn, bạn chỉ cần chú thích với @Transactionalphương thức findTopicByID(id)trong đó TopicServicevà buộc tìm nạp bộ sưu tập trong phương thức đó (ví dụ: bằng cách hỏi kích thước của nó):

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

0

Để thoát khỏi ngoại lệ khởi tạo lười biếng, bạn không nên gọi cho bộ sưu tập lười biếng khi bạn hoạt động với đối tượng tách rời.

Theo ý kiến ​​của tôi, cách tiếp cận tốt nhất là sử dụng DTO chứ không phải thực thể. Trong trường hợp này, bạn có thể đặt rõ ràng các trường mà bạn muốn sử dụng. Như thường lệ là đủ. Không cần phải lo lắng rằng một cái gì đó như jackson ObjectMapper, hoặc hashCodeđược tạo bởi Lombok sẽ gọi các phương thức của bạn một cách ngầm định.

Đối với một số trường hợp cụ thể, bạn có thể sử dụng @EntityGrpaphchú thích, cho phép bạn thực hiện eagertải ngay cả khi bạn có fetchType=lazytrong thực thể của mình.


0

Có nhiều giải pháp cho vấn đề Khởi tạo Lười này -

1) Thay đổi loại Tìm nạp liên kết từ LAZY sang EAGER nhưng đây không phải là cách thực hành tốt vì điều này sẽ làm giảm hiệu suất.

2) Sử dụng FetchType.LAZY trên Đối tượng được liên kết và cũng sử dụng chú thích Giao dịch trong phương thức lớp dịch vụ của bạn để phiên sẽ vẫn mở và khi bạn sẽ gọi topicById.getComments (), đối tượng con (nhận xét) sẽ được tải.

3) Ngoài ra, vui lòng thử sử dụng đối tượng DTO thay vì thực thể trong lớp trình điều khiển. Trong trường hợp của bạn, phiên được đóng ở lớp điều khiển. SO tốt hơn để chuyển đổi thực thể sang DTO trong lớp dịch vụ.


-11

tôi đã giải quyết bằng cách sử dụng Danh sách thay vì Đặt:

private List<Categories> children = new ArrayList<Categories>();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.