Cách sửa lỗi org.hibernate.LazyInitializationException - không thể khởi tạo proxy - không có Phiên


186

Tôi nhận được ngoại lệ sau:

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at sei.persistence.wf.entities.Element_$$_jvstc68_47.getNote(Element_$$_jvstc68_47.java)
    at JSON_to_XML.createBpmnRepresantation(JSON_to_XML.java:139)
    at JSON_to_XML.main(JSON_to_XML.java:84)

Khi tôi cố gắng gọi từ chính các dòng sau:

Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());

Tôi đã thực hiện getModelByModelGroup(int modelgroupid)phương pháp trước tiên như thế này:

public static Model getModelByModelGroup(int modelGroupId, boolean openTransaction) {

    Session session = SessionFactoryHelper.getSessionFactory().getCurrentSession();     
    Transaction tx = null;

    if (openTransaction) {
        tx = session.getTransaction();
    }

    String responseMessage = "";

    try {
        if (openTransaction) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new Exception("Non esiste ");
            }

            model = (Model)arrModels[0];
        }

        if (openTransaction) {
            tx.commit();
        }

        return model;

   } catch(Exception ex) {
       if (openTransaction) {
           tx.rollback();
       }
       ex.printStackTrace();
       if (responseMessage.compareTo("") == 0) {
           responseMessage = "Error" + ex.getMessage();
       }
       return null;
    }
}

và có ngoại lệ. Sau đó, một người bạn đề nghị tôi luôn kiểm tra phiên và lấy phiên hiện tại để tránh lỗi này. Vì vậy, tôi đã làm điều này:

public static Model getModelByModelGroup(int modelGroupId) {
    Session session = null;
    boolean openSession = session == null;
    Transaction tx = null;
    if (openSession) {
        session = SessionFactoryHelper.getSessionFactory().getCurrentSession(); 
        tx = session.getTransaction();
    }
    String responseMessage = "";

    try {
        if (openSession) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new RuntimeException("Non esiste");
            }

            model = (Model)arrModels[0];

            if (openSession) {
                tx.commit();
            }
            return model;
        } catch(RuntimeException ex) {
            if (openSession) {
                tx.rollback();
            }
            ex.printStackTrace();
            if (responseMessage.compareTo("") == 0) {
                responseMessage = "Error" + ex.getMessage();
            }
            return null;        
        }
    }
}

nhưng vẫn nhận được lỗi tương tự Tôi đã đọc rất nhiều cho lỗi này và tìm thấy một số giải pháp có thể. Một trong số đó là đặt lazyLoad thành false nhưng tôi không được phép làm điều này vì sao tôi được đề nghị kiểm soát phiên

Câu trả lời:


93

Điều sai ở đây là cấu hình quản lý phiên của bạn được đặt thành đóng phiên khi bạn cam kết giao dịch. Kiểm tra nếu bạn có một cái gì đó như:

<property name="current_session_context_class">thread</property>

trong cấu hình của bạn.

Để khắc phục vấn đề này, bạn có thể thay đổi cấu hình của nhà máy phiên hoặc mở một phiên khác và chỉ yêu cầu những đối tượng tải lười biếng đó. Nhưng những gì tôi muốn đề xuất ở đây là khởi tạo bộ sưu tập lười biếng này trong chính getModelByModelgroup và gọi:

Hibernate.initialize(subProcessModel.getElement());

khi bạn vẫn còn trong phiên hoạt động.

Và một điều cuối cùng. Một lời khuyên thân thiện. Bạn có một cái gì đó như thế này trong phương pháp của bạn:

for (Model m : modelList) {
    if (m.getModelType().getId() == 3) {
        model = m;
        break;
    }
}

Vui lòng kích hoạt mã này chỉ lọc các mô hình có id loại bằng 3 trong câu lệnh truy vấn chỉ vài dòng trên.

Một số đọc thêm:

phiên cấu hình nhà máy

vấn đề với phiên kín


1
Cảm ơn bạn! Tôi đã giải quyết vấn đề của mình bằng cách sử dụng openSession () thay vì getCienSession () như một trong những liên kết mà bạn đưa ra cho tôi, nhưng bây giờ tôi rất lo lắng nếu làm sai
Blerta Dhimitri 5/214

2
Không, nó có lẽ là tốt. Nhưng đọc thêm một số để có thể kiểm soát hoàn toàn các phiên và giao dịch của bạn. Điều thực sự quan trọng là phải biết những điều cơ bản bởi vì tất cả các công nghệ cấp cao hơn như Spring, Hibernate và hoạt động nhiều hơn trên cùng một khái niệm.
goroncy

177

Nếu bạn sử dụng Spring đánh dấu lớp là @Transactional , thì Spring sẽ xử lý việc quản lý phiên.

@Transactional
public class MyClass {
    ...
}

Bằng cách sử dụng @Transactional, nhiều khía cạnh quan trọng như tuyên truyền giao dịch được xử lý tự động. Trong trường hợp này nếu một phương thức giao dịch khác được gọi là phương thức sẽ có tùy chọn tham gia giao dịch đang diễn ra để tránh ngoại lệ "không có phiên".

CẢNH BÁO Nếu bạn sử dụng @Transactional, xin vui lòng lưu ý về hành vi kết quả. Xem bài viết này cho những cạm bẫy phổ biến. Ví dụ: cập nhật cho các thực thể được duy trì ngay cả khi bạn không gọi rõ ràngsave


20
Tôi không thể nói quá tầm quan trọng của câu trả lời này. Tôi thực sự khuyên bạn nên thử tùy chọn này trước.
Sparkyspider

6
Cũng lưu ý rằng bạn phải thêm @EnableTransactionManagementvào cấu hình của mình để kích hoạt giao dịch. " Nếu một phương thức giao dịch khác được gọi là phương thức sẽ có tùy chọn tham gia giao dịch đang diễn ra " thì hành vi này khác với các cách thức khác nhau được thực hiện, tức là giao diện proxy so với proxy lớp hoặc dệt AspectJ. Tham khảo tài liệu .
Erik Hofer

1
TransactionalDo đó, chúng ta nên hiểu chú thích mùa xuân được khuyên dùng, không chỉ để sửa đổi các giao dịch, mà còn cho chỉ truy cập các giao dịch?
Stephane

8
Tôi thực sự khuyên bạn chỉ nên sử dụng chú thích này trên đầu lớp để thử nghiệm. Mã thực nên đánh dấu mỗi phương thức là giao dịch trong lớp riêng biệt. Trừ khi tất cả các phương thức trong lớp sẽ yêu cầu kết nối mở với giao dịch đến cơ sở dữ liệu.
m1ld

1
Không an toàn khi đặt @Transactional (readOnly = true) thay vì chỉ @Transactional?
Hamedz

105

Bạn có thể thử đặt

<property name="hibernate.enable_lazy_load_no_trans">true</property>

trong hibernate.cfg.xml hoặc continence.xml

Vấn đề cần lưu ý với tài sản này được giải thích rõ ở đây


8
Bạn có thể giải thích ý nghĩa của nó là tốt?
Mohit Kanwar

2
Tôi cũng tò mò không biết điều này có tác dụng gì. Nó đã khắc phục vấn đề tôi gặp phải, nhưng tôi muốn hiểu tại sao.
Hassan

3
cho sự kiên trì:<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
ACV

33
Bạn có nhận ra rằng đó hibernate.enable_lazy_load_no_translà một Anti-Forms , phải không?
Vlad Mihalcea

6
KHÔNG SỬ DỤNG QUYỀN SỞ HỮU NÀY, NẾU XUÂN QUẢN LÝ GIAO DỊCH CỦA BẠN SỞ HỮU NÀY SILL ĐƯỢC CHẤP NHẬN GIAO DỊCH GIAO DỊCH, XUÂN ĐƠN
GIẢN S SH CHIA SẺ

54

Cách tốt nhất để xử lýLazyInitializationException là sử dụng JOIN FETCHchỉ thị:

Query query = session.createQuery(
    "from Model m " +
    "join fetch m.modelType " +
    "where modelGroup.id = :modelGroupId"
);

Dù sao, KHÔNG sử dụng các Mô hình chống sau như được đề xuất bởi một số câu trả lời:

Đôi khi, phép chiếu DTO là lựa chọn tốt hơn so với tìm nạp các thực thể và theo cách này, bạn sẽ không nhận được bất kỳ LazyInitializationException.


Làm thế nào tôi có thể xác định cuộc gọi nào đang có vấn đề? Tôi đang tìm thấy nó là khó khăn để xác định cuộc gọi. Không có cách nào khác ư ? Đối với mục đích thử nghiệm tôi đã sử dụng FetchType=EAGER, nhưng đây không phải là giải pháp chính xác, phải không?
Trì Tupe

Chỉ cần sử dụng đăng nhập. Và EAGER là xấu, vâng.
Vlad Mihalcea

Sau đó, bạn nên sử dụng DTO hoặc khởi tạo tất cả các hiệp hội trước khi rời @Transactionaldịch vụ.
Vlad Mihalcea

2
Chúng ta nên ủng hộ thực hành doanh nghiệp tốt nhất nhưng không phải là sửa chữa nhanh chóng.
etlds

20

Tôi đã nhận được cùng một lỗi cho một đến nhiều mối quan hệ cho chú thích bên dưới.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL)

Thay đổi như dưới đây sau khi thêm fetch = FetchType.EAGER, nó hoạt động với tôi.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)

24
Có nó có thể sửa nó nhưng bây giờ bạn đang tải toàn bộ cây dữ liệu. Điều này sẽ có tác động tiêu cực trong hầu hết các trường hợp
astro8891

13

nếu bạn sử dụng dữ liệu mùa xuân jpa, khởi động mùa xuân, bạn có thể thêm dòng này trong application.properies

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

7
Đây được coi là một mô hình chống. vladmihalcea.com/ từ
Sudip Bhandari

9

Ngoại lệ này vì khi bạn gọi session.getEntityById(), phiên sẽ bị đóng. Vì vậy, bạn cần phải gắn lại thực thể vào phiên. Hoặc giải pháp Easy chỉ là cấu hình default-lazy="false" cho bạn entity.hbm.xmlhoặc nếu bạn đang sử dụng chú thích, chỉ cần thêm @Proxy(lazy=false)vào lớp thực thể của bạn.


5

Tôi gặp phải vấn đề tương tự. Tôi nghĩ một cách khác để khắc phục điều này là bạn có thể thay đổi truy vấn để tham gia tìm nạp Element từ Model như sau:

Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")

4

Điều này có nghĩa là đối tượng mà bạn đang cố truy cập không được tải, vì vậy hãy viết một truy vấn để thực hiện tìm nạp tham gia của đối tượng mà bạn đang cố gắng truy cập.

Ví dụ:

Nếu bạn đang cố gắng lấy ObjectB từ ObjectA trong đó ObjectB là khóa ngoại trong ObjectA.

Truy vấn :

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

3

Có một số câu trả lời tốt ở đây xử lý lỗi này trong phạm vi rộng. Tôi gặp phải một tình huống cụ thể với Spring Security, cách khắc phục nhanh, mặc dù có lẽ không tối ưu.

Trong quá trình ủy quyền người dùng (ngay sau khi đăng nhập và chuyển qua xác thực), tôi đã kiểm tra một thực thể người dùng cho một quyền cụ thể trong một lớp tùy chỉnh mở rộng SimpleUrlAuthenticationSuccessHandler.

Thực thể người dùng của tôi triển khai UserDetails và có một tập hợp các vai trò được tải lười biếng đã ném "org.hibernate.LazyInitializationException - không thể khởi tạo proxy - không có phiên ngoại lệ". Việc thay đổi Set đó từ "fetch = FetchType.LAZY" thành "fetch = FetchType.EAGER" đã sửa lỗi này cho tôi.



2

Đối mặt với cùng một ngoại lệ trong trường hợp sử dụng khác nhau.

nhập mô tả hình ảnh ở đây

Ca sử dụng: Cố gắng đọc dữ liệu từ DB với phép chiếu DTO.

Giải pháp: Sử dụng phương thức get thay vì tải .

Hoạt động chung

public class HibernateTemplate {
public static Object loadObject(Class<?> cls, Serializable s) {
    Object o = null;
    Transaction tx = null;
    try {
        Session session = HibernateUtil.getSessionFactory().openSession();
        tx = session.beginTransaction();
        o = session.load(cls, s); /*change load to get*/
        tx.commit();
        session.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return o;
}

}

Lớp học bền bỉ

public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int customerId;

@Column(name = "Name")
private String customerName;

@Column(name = "City")
private String city;

//constructors , setters and getters

}

Giao diện khách hàng

public interface CustomerDAO 
     {
   public CustomerTO getCustomerById(int cid);
     }

Lớp đối tượng chuyển giao

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

Lớp học nhà máy

public class DAOFactory {

static CustomerDAO customerDAO;
static {
    customerDAO = new HibernateCustomerDAO();
}

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

Thực thể cụ thể DAO

public class HibernateCustomerDAO implements CustomerDAO {

@Override
public CustomerTO getCustomerById(int cid) {
    Customer cust = (Customer) HibernateTemplate.loadObject(Customer.class, cid);
    CustomerTO cto = new CustomerTO(cust.getCustomerId(), cust.getCustomerName(), cust.getCity());
    return cto;
}

}

Lấy dữ liệu: Lớp kiểm tra

CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());

Dữ liệu hiện tại

nhập mô tả hình ảnh ở đây

Truy vấn và đầu ra được tạo bởi Hibernate System

Hibernate: chọn customer0_.Id là Id1_0_0_, customer0_.City là City2_0_0_, customer0_.Name là Name3_0_0_ từ CustomerLab31 customer0_ trong đó customer0_.Id =?

Tên khách hàng -> Cody, Khách hàng thành phố -> LA


1

Nếu bạn đang sử dụng Grail'sFramework, thật đơn giản để giải quyết ngoại lệ khởi tạo lười biếng bằng cách sử dụng Lazytừ khóa trên trường cụ thể trong Lớp Miền.

Ví dụ:

class Book {
    static belongsTo = [author: Author]
    static mapping = {
        author lazy: false
    }
}

Tìm thêm thông tin ở đây


1

Trong trường hợp của tôi, một vị trí sai session.clear()đã gây ra vấn đề này.


1

Điều này có nghĩa là bạn đang sử dụng JPA hoặc ngủ đông trong mã của mình và thực hiện thao tác sửa đổi trên DB mà không thực hiện giao dịch logic nghiệp vụ. Vì vậy, giải pháp đơn giản cho việc này là đánh dấu đoạn mã của bạn @Transactional



-2

bạn cũng có thể giải quyết nó bằng cách thêm lazy = false vào tệp * .hbm.xml của bạn hoặc bạn có thể khởi tạo đối tượng của mình trong Hibernate.init (Object) khi bạn lấy đối tượng từ db


10
nói chung thêm lười = sai không phải là một ý tưởng tốt. đó là lý do tại sao lười biếng là đúng theo mặc định
MoienGK

OP nói rõ ràng rằng anh ấy không được phép làm điều đó.
GingerHead

-2

Thực hiện các thay đổi sau trong servlet-bối cảnh

    <beans:property name="hibernateProperties">
        <beans:props>

            <beans:prop key="hibernate.enable_lazy_load_no_trans">true</beans:prop>

        </beans:props>
    </beans:property>
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.