2008-12-06 32 views
67

Đây là một trong đó có tôi bối rối. Tôi đang cố gắng để thực hiện một cấu trúc DAO Hibernate cơ bản, nhưng có một vấn đề.hibernate: LazyInitializationException: không thể khởi tạo proxy

Dưới đây là đoạn code cần thiết:

int startingCount = sfdao.count(); 
sfdao.create(sf); 
SecurityFiling sf2 = sfdao.read(sf.getId()); 
sfdao.delete(sf); 
int endingCount = sfdao.count(); 

assertTrue(startingCount == endingCount); 
assertTrue(sf.getId().longValue() == sf2.getId().longValue()); 
assertTrue(sf.getSfSubmissionType().equals(sf2.getSfSubmissionType())); 
assertTrue(sf.getSfTransactionNumber().equals(sf2.getSfTransactionNumber())); 

Nó thất bại trên assertTrue thứ ba mà nó đang cố gắng để so sánh một giá trị trong sf với giá trị tương ứng trong SF2. Dưới đây là ngoại lệ:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session 
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86) 
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140) 
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190) 
    at com.freightgate.domain.SecurityFiling_$$_javassist_7.getSfSubmissionType(SecurityFiling_$$_javassist_7.java) 
    at com.freightgate.dao.SecurityFilingTest.test(SecurityFilingTest.java:73) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40) 

Trả lời

15

Điều này thường có nghĩa là phiên Hibernate đang sở hữu đã đóng. Bạn có thể làm một trong các cách sau để sửa chữa nó:

  1. bất cứ đối tượng tạo ra vấn đề này, sử dụng HibernateTemplate.initialize(object name)
  2. Sử dụng lazy=false trong file hbm của bạn.
+0

Đã cùng một vấn đề và lười biếng = false cố định nó . Cảm ơn – autonomatt

+1

bây giờ trong trường hợp của tôi đang sử dụng 'lazy = false' cho tất cả các cấp độ dao nhưng nó chỉ ra rằng hiệu suất ứng dụng chậm vì nó, cố gắng đặt' lazy = true' nhưng bây giờ lazyException được ném, bất kỳ đề xuất như thế nào có thể được đã sửa. – Rachel

+0

pakore, bạn có thể chỉ ra lý do tại sao không phải là giải pháp và làm thế nào để hiểu nó? – Victor

2

Được rồi, cuối cùng đã tìm ra nơi mà tôi đã bất cẩn. Tôi đã theo quan niệm sai lầm rằng tôi nên bọc mỗi phương pháp DAO trong một giao dịch. Sai lầm khủng khiếp! Tôi đã học được bài học của mình. Tôi đã kéo tất cả mã giao dịch từ tất cả các phương thức DAO và đã thiết lập giao dịch nghiêm ngặt ở lớp ứng dụng/người quản lý. Điều này đã hoàn toàn giải quyết tất cả các vấn đề của tôi. Dữ liệu được tải xuống đúng cách khi tôi cần, được đóng gói và đóng lại khi tôi thực hiện cam kết.

Cuộc sống tốt ... :)

+0

Tôi không chắc tôi hoàn toàn hiểu, vì tôi không nhớ lại việc này trong các dự án khác. Nhưng bạn nói đúng: thêm '@ org.springframework.transaction.annotation.Transactional (readOnly = true)' vào các phương thức trong lớp dịch vụ đã khắc phục vấn đề. (Trong lớp đó, chúng tôi đang tìm nạp một thực thể và chuyển nó đến một cuộc gọi khác đến DAO.) – Arjan

1

Tôi nghĩ Piko có nghĩa là trong phản hồi của mình rằng có tệp hbm. Tôi có một tệp có tên Tax.java. Thông tin ánh xạ được lưu trong tệp hbm (= hibernate mapping). Trong thẻ lớp, có một thuộc tính được gọi là lười biếng. Đặt thuộc tính đó thành true. Ví dụ hbm sau đây cho biết cách đặt thuộc tính lười thành false.

' id ...'

Nếu bạn đang sử dụng chú thích thay vì nhìn vào documenation hibernate. http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/

Tôi hy vọng điều đó sẽ hữu ích.

4

nếu bạn đang sử dụng Lazy tải phương pháp của bạn phải được chú thích với

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) cho Stateless Session EJB

3

Chúng tôi gặp phải lỗi này là tốt. Những gì chúng tôi đã làm để giải quyết vấn đề là chúng tôi đã thêm lazy = false vào tệp ánh xạ Hibernate.

Có vẻ như chúng tôi đã có một lớp A bên trong một phiên tải lớp B. Chúng tôi đang cố gắng truy cập dữ liệu trên lớp B nhưng lớp B này được tách ra khỏi phiên.

Để chúng tôi truy cập Lớp B này, chúng tôi phải chỉ định trong tệp ánh xạ Hibernate của lớp A thuộc tính lazy = false. Ví dụ:

 <many-to-one name="classA" 
       class="classB" 
       lazy="false"> 
     <column name="classb_id" 
       sql-type="bigint(10)" 
       not-null="true"/> 
    </many-to-one> 
68

Vấn đề là bạn đang cố truy cập bộ sưu tập trong một đối tượng là detached. Bạn cần phải đính kèm lại đối tượng trước khi truy cập bộ sưu tập vào phiên hiện tại. Bạn có thể làm điều đó thông qua

session.update(object); 

Sử dụng lazy=false không phải là giải pháp tốt vì bạn đang ném đi tính năng khởi tạo lười biếng của ngủ đông. Khi lazy=false, bộ sưu tập được tải vào bộ nhớ cùng một lúc mà đối tượng được yêu cầu. Điều này có nghĩa rằng nếu chúng ta có một bộ sưu tập với 1000 mục, tất cả chúng sẽ được nạp vào bộ nhớ, mặc dù chúng ta sẽ truy cập chúng hay không. Và điều này là không tốt.

Vui lòng đọc số này article nơi giải thích sự cố, giải pháp khả thi và lý do được triển khai theo cách này. Ngoài ra, để hiểu Phiên và Giao dịch, bạn phải đọc this other article.

7

Nếu bạn đang sử dụng hibernate với chú thích JPA thì điều này sẽ hữu ích. Trong lớp dịch vụ của bạn, cần có trình thiết lập cho trình quản lý thực thể với @PersistenceContext. thay đổi điều này thành @PersistenceContext (type = PersistenceContextType.EXTENDED). Sau đó, bạn có thể truy cập tài sản lười biếng ở bất kỳ đâu.

+1

Điều này không chính xác trừ khi bạn đang quản lý giao dịch của mình theo cách thủ công. Kiểu bối cảnh persistend Spring EXTENDED là dành cho mẫu cuộc hội thoại dài, không phải là mẫu phiên cho mỗi yêu cầu mà OP đang hỏi. – HDave

+0

Tôi đoán @HDave là đúng; xem thêm [Sự khác biệt giữa bối cảnh Persistence có phạm vi giao dịch và bối cảnh Persistence Extended là gì?] (http://stackoverflow.com/questions/2547817/what-is-the-difference-between-transaction-scoped-persistence-context- và mở rộng) – Arjan

2

Nếu bạn biết về tác động của lazy=false và vẫn muốn làm cho nó như là mặc định (ví dụ, cho các mục đích tạo mẫu), bạn có thể sử dụng bất kỳ những điều sau đây:

  • nếu bạn đang sử dụng cấu hình XML: thêm default-lazy="false" để <hibernate-mapping> yếu tố của bạn
  • nếu bạn đang sử dụng cấu hình chú thích: thêm @Proxy(lazy=false) đến lớp thực thể của bạn (es)
11

Xem bài viết của tôi. Tôi đã có cùng một vấn đề - LazyInitializationException - và đây là câu trả lời cuối cùng tôi đã đưa ra:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
Đặt lười = false không phải là câu trả lời - nó có thể tải mọi thứ cùng một lúc và điều đó không nhất thiết phải tốt. Ví dụ:
1 kỷ lục tài liệu tham khảo bảng A:
5 hồ sơ tài liệu tham khảo bảng B:
25 hồ sơ tài liệu tham khảo bảng C:
125 hồ sơ bảng D
...
vv Đây là một ví dụ về những gì có thể đi sai rồi.
--Tim Sabin

+0

Bạn nên giải thích giải pháp tại đây, không liên kết đến trang web của bên thứ ba .. –

1

sử dụng Hibernate.initialize cho lĩnh vực lười biếng

2

Dường như chỉ DAO của bạn đang sử dụng phiên. Vì vậy, một phiên mới được mở sau đó đóng cho mỗi cuộc gọi đến một phương pháp DAO. Do đó việc thực hiện các chương trình có thể được nối lại như:

// open a session, get the number of entity and close the session 
int startingCount = sfdao.count(); 

// open a session, create a new entity and close the session 
sfdao.create(sf); 

// open a session, read an entity and close the session 
SecurityFiling sf2 = sfdao.read(sf.getId()); 

// open a session, delete an entity and close the session 
sfdao.delete(sf); 

etc... 

Theo mặc định, việc thu thập và hiệp hội trong một tổ chức là lười biếng: họ được nạp từ cơ sở dữ liệu theo yêu cầu.Như vậy:

sf.getSfSubmissionType().equals(sf2.getSfSubmissionType())

được ném một ngoại lệ vì nó yêu cầu tải mới từ cơ sở dữ liệu, và phiên liên quan đến việc tải của đơn vị đã được đóng lại.

Có hai cách tiếp cận để giải quyết vấn đề này:

  • tạo ra một phiên để kèm theo tất cả các mã của chúng tôi. Do đó, điều đó có nghĩa là thay đổi nội dung DAO của bạn để tránh mở phiên thứ hai

  • tạo phiên rồi cập nhật (tức là kết nối lại) thực thể của bạn với phiên này trước khi xác nhận.

    session.update (đối tượng);

0

Theo mặc định, tất cả one-to-manymany-to-many hiệp hội đang lấy uể oải khi được truy cập lần đầu tiên.

Trong trường hợp sử dụng của bạn, bạn có thể khắc phục vấn đề này bằng cách gói toàn bộ hoạt động DAO vào một giao dịch hợp lý:

transactionTemplate.execute(new TransactionCallback<Void>() { 
    @Override 
    public Void doInTransaction(TransactionStatus transactionStatus) { 

     int startingCount = sfdao.count(); 

     sfdao.create(sf); 

     SecurityFiling sf2 = sfdao.read(sf.getId()); 

     sfdao.delete(sf); 

     int endingCount = sfdao.count(); 

     assertTrue(startingCount == endingCount); 
     assertTrue(sf.getId().longValue() == sf2.getId().longValue()); 
     assertTrue(sf.getSfSubmissionType().equals(sf2.getSfSubmissionType())); 
     assertTrue(sf.getSfTransactionNumber().equals(sf2.getSfTransactionNumber())); 

     return null; 
    } 
}); 

lựa chọn khác là để lấy tất cả các hiệp hội LAZY khi tải tổ chức của bạn, do đó:

SecurityFiling sf2 = sfdao.read(sf.getId()); 

nên lấy LAZY submissionType quá:

select sf 
from SecurityFiling sf 
left join fetch.sf.submissionType 

Bằng cách này, bạn háo hức tìm nạp tất cả các thuộc tính lười và bạn có thể truy cập chúng sau khi phiên được đóng lại.

Bạn có thể tìm nạp nhiều liên kết [one|many]-to-one và một liên kết danh sách "[một | nhiều] đến nhiều" (vì chạy một sản phẩm Descartes).

Để khởi tạo nhiều "[một | nhiều] thành nhiều", bạn nên sử dụng Hibernate.initialize(collection), ngay sau khi tải thực thể gốc của bạn.

1

Nếu bạn sử dụng Spring và JPA chú thích, cách đơn giản nhất để tránh trục trặc với phiên trong khởi tạo lười biếng được replaysing:

@PersistenceContext 

để

@PersistenceContext(type = PersistenceContextType.EXTENDED) 
+1

Điều này chỉ hoạt động nếu bạn đang quản lý thủ công giao dịch của mình – Gemasoft

Các vấn đề liên quan