使用getReference()时,为什么Hibernate JPA会抛出LazyInitialization异常?

时间:2017-09-30 23:49:14

标签: java hibernate jpa

我使用Hibernate 5.2.11作为JPA提供程序。我有一个带注释的类(PurchaseOrder)和另一个带注释的类(Customer)作为具有多对一关系的字段。但是因为遗留代码在不同的数据库中有Customer和PO表,所以我在PurchaseOrder中调用EntityManager的getReference()来返回Customer实例。我通过使用Hibernate的自制类访问这些(详情如下)。 这导致以下异常:

org.hibernate.LazyInitializationException:无法初始化代理 - 没有会话

我一直在阅读Hibernate,JPA和Java EE文档,但我们还没有弄清楚我做错了什么。我知道Hibernate在幕后使用Session来启用JPA功能,但是当我访问.getCustomer()时,我正在创建一个新的EntityManager,所以它应该有它需要的会话。

很明显,我错过了一点点理解,但我不知道它是什么。谁能开导我?

以下是Purchase Order的重要部分,其中包括字符串,整数,布尔值和LocalDates(即:它们都是@Basic字段),而Customer是唯一包含的类:

@Entity
@Table(name = "PurchaseOrder")
public class PurchaseOrder extends BaseEntity { // BaseEntity is a @mappedSuperclass containing the primary key info only.
...
    private Integer customerID;
    private Customer customer;
...
    @Column(name = "customerID")
    public Integer getCustomerID() {
        return customerID;
    }

    public void setCustomerID(Integer customerID) {
        this.customerID = customerID;
    }

    @Transient
    public Customer getCustomer() {
        LOG.info("Getting customer #{}", customerID);
        if (customerID != 0 && (customer == null || !customerID.equals(customer.getId()))) {
            customer = VdtsSysDB.getDB().get(Customer.class, customerID);
        }
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
        this.customerID = customer.getId();
    }
...

这是Customer,它只包含@Basic字段:

@Entity
@Table(name = "Customers")
public class Customer extends BaseEntity{
    private String custNo;
    private String businessName;
    private String contact;
...
    @Column(name = "custNo")
    public String getCustNo() {
        return custNo;
    }

    public void setCustNo(String custNo) {
        this.custNo = custNo;
    }

    @Column(name = "Name")
    public String getBusinessName() {
        return businessName;
    }

    public void setBusinessName(String businessName) {
        this.businessName = businessName;
    }

    @Column(name = "Contact")
    public String getContact() {
        return contact;
    }

    public void setContact(String contact) {
        this.contact = contact;
    }
    ...

这是我的Hibernate实用程序类:

public class VdtsSysDB {
    private EntityManagerFactory entityManagerFactory;
    private static VdtsSysDB vdtsSysDB;

    public static VdtsSysDB getDB() {
        if (vdtsSysDB == null) {
            vdtsSysDB = new VdtsSysDB();
        }
        return vdtsSysDB;
    }

    private VdtsSysDB() {
        if (entityManagerFactory == null)
            entityManagerFactory = Persistence.createEntityManagerFactory("VDTS_SYSDB");
    }

    public EntityManager getEntityManager() {
        return entityManagerFactory.createEntityManager();
    }

    public void closeEntityManager(EntityManager entityManager) {
        try {
            entityManager.close();
        } catch (Exception e) {
            // Exception logging.
        }
    }
...
    /**
     * Issues an HQL Query and returns the results as a list.
     *
     * @param queryString - An HQL query.
     * @return A list of items representing the returned dataset.
     */
    public <T extends BaseEntity> List<T> query(String queryString) {
        List<T> results = null;
        LOG.info("Query: {}", queryString);
        EntityManager entityManager = null;
        try {
            entityManager = getEntityManager();
            results = entityManager.createQuery(queryString).getResultList();
            entityManager.close();
            LOG.info("Returned {} results.", results.size());
        } catch (Exception e) {
            if (entityManager != null && entityManager.isOpen()) entityManager.close();
            LOG.error("Unable to complete query {}.", queryString, e);
        }
        return results;
    }

    /**
     * Get an object from the database by specifying its class and its ID.
     * @param aClass the class type to be returned.
     * @param id the primary key to the item to be returned.
     * @param <T> the class type to be returned.
     * @return A single instance of the specified item of this class.
     */
    public <T extends BaseEntity> T get(Class aClass, Integer id) {
        LOG.info("Get #: {}, {}", id, aClass.getName());
        T result = null;
        EntityManager entityManager = null;
        try {
            entityManager = getEntityManager();
            Object object = entityManager.getReference(aClass, id);
            result = (T) object;
        } catch (Exception e) {
            LOG.error("Could not get {}", id, e);
        } finally {
            closeEntityManager(entityManager);
        }
        return result;
    }
}

抛出异常的代码是JavaFX 8应用程序控制器的一部分。在类加载时调用initialize(),而只要显示附加的GUI,就会调用refreshPane()。调用Customer.getBusinessName()时会抛出异常。

@FXML
private TableView<PurchaseOrder> poTable;
@FXML
private TableColumn<PurchaseOrder, String> poNoCol;
@FXML
private TableColumn<PurchaseOrder, String> customerNameCol;
@FXML
private TableColumn<PurchaseOrder, LocalDate> orderDateCol;

@Override
protected void initialize() {
    super.initialize();
    poTable.getSelectionModel().selectedItemProperty().addListener(
            (observable, oldValue, newValue) -> {
                if (newValue != null) selectItem();
            });
    poNoCol.setCellValueFactory(new PropertyValueFactory<>("purchaseOrderNo"));
    customerNameCol.setCellValueFactory(param -> {
        PurchaseOrder po = param.getValue();
        Customer customer = po.getCustomer();
        String name = customer.getBusinessName(); /****** This is the line that throws the exception ******/
        StringProperty observableString = new SimpleStringProperty(name);
        return observableString;
            });
    orderDateCol.setCellValueFactory(new PropertyValueFactory<>("orderDate"));    
...
}

@Override
protected void refreshPane() {
    List<Customer> oList = VdtsSysDB.getDB().query("from Customer");
    customerCombo.setItems(FXCollections.observableList(oList));
    changeTable();
    clearWidgets();
    enableWidgets(false);
}

private void changeTable() {
    poTable.getSelectionModel().clearSelection();
    List<PurchaseOrder> oList = VdtsSysDB.getDB()
            .query("from PurchaseOrder where closed = " + (openRadio.isSelected() ? "0" : "1"));
    poTable.setItems(FXCollections.observableList(oList));
}
...

异常的完整堆栈跟踪:

30-09-17 19:42:05.137 ERROR java.lang.Throwable - Exception in thread "JavaFX Application Thread" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
30-09-17 19:42:05.137 ERROR java.lang.Throwable -   at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:146)
30-09-17 19:42:05.138 ERROR java.lang.Throwable -   at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:259)
30-09-17 19:42:05.139 ERROR java.lang.Throwable -   at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73)
30-09-17 19:42:05.139 ERROR java.lang.Throwable -   at ca.vdts.buchanan.model.Customer_$$_jvst799_9.getBusinessName(Customer_$$_jvst799_9.java)
30-09-17 19:42:05.139 ERROR java.lang.Throwable -   at ca.vdts.buchanan.endtally.controllers.POController.lambda$initialize$1(POController.java:103)
30-09-17 19:42:05.140 ERROR java.lang.Throwable -   at javafx.scene.control.TableColumn.getCellObservableValue(TableColumn.java:578)
30-09-17 19:42:05.140 ERROR java.lang.Throwable -   at javafx.scene.control.TableColumn.getCellObservableValue(TableColumn.java:563)
30-09-17 19:42:05.140 ERROR java.lang.Throwable -   at javafx.scene.control.TableCell.updateItem(TableCell.java:644)
30-09-17 19:42:05.141 ERROR java.lang.Throwable -   at javafx.scene.control.TableCell.indexChanged(TableCell.java:468)
30-09-17 19:42:05.141 ERROR java.lang.Throwable -   at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:116)
30-09-17 19:42:05.141 ERROR java.lang.Throwable -   at com.sun.javafx.scene.control.skin.TableRowSkinBase.updateCells(TableRowSkinBase.java:533)
30-09-17 19:42:05.141 ERROR java.lang.Throwable -   at com.sun.javafx.scene.control.skin.TableRowSkinBase.init(TableRowSkinBase.java:147)
30-09-17 19:42:05.142 ERROR java.lang.Throwable -   at com.sun.javafx.scene.control.skin.TableRowSkin.<init>(TableRowSkin.java:64)
30-09-17 19:42:05.142 ERROR java.lang.Throwable -   at javafx.scene.control.TableRow.createDefaultSkin(TableRow.java:212)
30-09-17 19:42:05.142 ERROR java.lang.Throwable -   at javafx.scene.control.Control.impl_processCSS(Control.java:872)
30-09-17 19:42:05.142 ERROR java.lang.Throwable -   at javafx.scene.Node.processCSS(Node.java:9058)
30-09-17 19:42:05.143 ERROR java.lang.Throwable -   at javafx.scene.Node.applyCss(Node.java:9155)
30-09-17 19:42:05.143 ERROR java.lang.Throwable -   at com.sun.javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1964)
30-09-17 19:42:05.143 ERROR java.lang.Throwable -   at com.sun.javafx.scene.control.skin.VirtualFlow.getCell(VirtualFlow.java:1797)
30-09-17 19:42:05.143 ERROR java.lang.Throwable -   at com.sun.javafx.scene.control.skin.VirtualFlow.getCellLength(VirtualFlow.java:1879)
30-09-17 19:42:05.144 ERROR java.lang.Throwable -   at com.sun.javafx.scene.control.skin.VirtualFlow.computeViewportOffset(VirtualFlow.java:2528)
30-09-17 19:42:05.144 ERROR java.lang.Throwable -   at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1189)
30-09-17 19:42:05.144 ERROR java.lang.Throwable -   at javafx.scene.Parent.layout(Parent.java:1087)
30-09-17 19:42:05.144 ERROR java.lang.Throwable -   at javafx.scene.Parent.layout(Parent.java:1093)
30-09-17 19:42:05.145 ERROR java.lang.Throwable -   at javafx.scene.Parent.layout(Parent.java:1093)
30-09-17 19:42:05.145 ERROR java.lang.Throwable -   at javafx.scene.Parent.layout(Parent.java:1093)
30-09-17 19:42:05.145 ERROR java.lang.Throwable -   at javafx.scene.Parent.layout(Parent.java:1093)
30-09-17 19:42:05.145 ERROR java.lang.Throwable -   at javafx.scene.Parent.layout(Parent.java:1093)
30-09-17 19:42:05.146 ERROR java.lang.Throwable -   at javafx.scene.Parent.layout(Parent.java:1093)
30-09-17 19:42:05.146 ERROR java.lang.Throwable -   at javafx.scene.Parent.layout(Parent.java:1093)
30-09-17 19:42:05.146 ERROR java.lang.Throwable -   at javafx.scene.Parent.layout(Parent.java:1093)
30-09-17 19:42:05.147 ERROR java.lang.Throwable -   at javafx.scene.Scene.doLayoutPass(Scene.java:552)
30-09-17 19:42:05.147 ERROR java.lang.Throwable -   at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2397)
30-09-17 19:42:05.147 ERROR java.lang.Throwable -   at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Toolkit.java:355)
30-09-17 19:42:05.147 ERROR java.lang.Throwable -   at java.security.AccessController.doPrivileged(Native Method)
30-09-17 19:42:05.148 ERROR java.lang.Throwable -   at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:354)
30-09-17 19:42:05.148 ERROR java.lang.Throwable -   at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:381)
30-09-17 19:42:05.148 ERROR java.lang.Throwable -   at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:510)
30-09-17 19:42:05.148 ERROR java.lang.Throwable -   at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
30-09-17 19:42:05.149 ERROR java.lang.Throwable -   at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(QuantumToolkit.java:319)
30-09-17 19:42:05.149 ERROR java.lang.Throwable -   at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
30-09-17 19:42:05.150 ERROR java.lang.Throwable -   at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
30-09-17 19:42:05.150 ERROR java.lang.Throwable -   at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
30-09-17 19:42:05.150 ERROR java.lang.Throwable -   at java.lang.Thread.run(Thread.java:745)

1 个答案:

答案 0 :(得分:1)

Oy公司。为什么在发布问题后你才能找到答案?

由于我使用了EntityManager.getReference()。

,因此抛出此异常

来自Java EE JPA Javadocs:

  

获取一个实例,其状态可能会被懒散地取出。如果要求   实例在数据库中不存在EntityNotFoundException   首次访问实例状态时抛出。 (持久性   允许提供者运行时抛出EntityNotFoundException   当调用getReference时。)应用程序不应该期望这样   除非是分离,否则实例状态将在分离时可用   在实体经理开放时由应用程序访问。

我试图访问Customer的字段,getReference是懒惰地获取的。在关闭EntityManager之前,这些字段尚未初始化,因此任何引用它们的尝试都必然会抛出LazyInitialization异常。

这里的解决方案很明显:不要使用getReference。改为使用EntityManager.find()。