我使用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)
答案 0 :(得分:1)
Oy公司。为什么在发布问题后你才能找到答案?
由于我使用了EntityManager.getReference()。
,因此抛出此异常来自Java EE JPA Javadocs:
获取一个实例,其状态可能会被懒散地取出。如果要求 实例在数据库中不存在EntityNotFoundException 首次访问实例状态时抛出。 (持久性 允许提供者运行时抛出EntityNotFoundException 当调用getReference时。)应用程序不应该期望这样 除非是分离,否则实例状态将在分离时可用 在实体经理开放时由应用程序访问。
我试图访问Customer的字段,getReference是懒惰地获取的。在关闭EntityManager之前,这些字段尚未初始化,因此任何引用它们的尝试都必然会抛出LazyInitialization异常。
这里的解决方案很明显:不要使用getReference。改为使用EntityManager.find()。