hibernate - 使用 HQL JOIN 填充 JavaFX TableView 会返回奇怪的数据
问题描述
虽然我的 HQL 查询在 Hibernate 控制台中运行良好,但 我得到了奇怪的结果。我尝试结合相应列的@FXML 注释更改下面的行,但要么我得到错误,要么我得到空白单元格。vstcolName.setCellValueFactory(new PropertyValueFactory("peopleByPeopleId"));
这是我的第一个表的实体类。
package Entities;
import javax.persistence.*;
import java.util.Objects;
@Entity
@Table(name = "people", schema = "learn", catalog = "")
public class PeopleEntity {
private int pplId;
private String pplName;
@Id
@Column(name = "pplID")
public int getPplId() {
return pplId;
}
public void setPplId(int pplId) {
this.pplId = pplId;
}
@Basic
@Column(name = "pplName")
public String getPplName() {
return pplName;
}
public void setPplName(String pplName) {
this.pplName = pplName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PeopleEntity that = (PeopleEntity) o;
return pplId == that.pplId &&
Objects.equals(pplName, that.pplName);
}
@Override
public int hashCode() {
return Objects.hash(pplId, pplName);
}
}
这是我的第二张表的实体类。
package Entities;
import javax.persistence.*;
import java.util.Objects;
@Entity
@Table(name = "places", schema = "learn", catalog = "")
public class PlacesEntity {
private int plcId;
private String place;
private PeopleEntity peopleByPeopleId;
@Id
@Column(name = "plcID")
public int getPlcId() {
return plcId;
}
public void setPlcId(int plcId) {
this.plcId = plcId;
}
@Basic
@Column(name = "place")
public String getPlace() {
return place;
}
public void setPlace(String place) {
this.place = place;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PlacesEntity that = (PlacesEntity) o;
return plcId == that.plcId &&
Objects.equals(place, that.place);
}
@Override
public int hashCode() {
return Objects.hash(plcId, place);
}
@ManyToOne
@JoinColumn(name = "people_ID", referencedColumnName = "pplID")
public PeopleEntity getPeopleByPeopleId() {
return peopleByPeopleId;
}
public void setPeopleByPeopleId(PeopleEntity peopleByPeopleId) {
this.peopleByPeopleId = peopleByPeopleId;
}
}
这是我的包含 HQL 查询的模型。
package Models;
import Entities.PlacesEntity;
import Interfaces.PlacesIF;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import java.util.List;
public class PlacesIM implements PlacesIF {
public List<PlacesEntity> readJoin() {
session = sessionFactory.openSession();
Query query;
query = session.createQuery("SELECT PlcEnt.place as PLACES, pep.pplName as NAMES FROM PlacesEntity as PlcEnt JOIN PlcEnt.peopleByPeopleId as pep");
List<PlacesEntity> tableList;
tableList = query.list();
return tableList;
}
}
这是 FXML 控制器
package FX;
import Entities.PeopleEntity;
import Entities.PlacesEntity;
import Models.PlacesIM;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
public class placesController implements Initializable {
//Visit TableView
@FXML
TableView<PlacesEntity> vstTView;
//I think that this line may need modification
@FXML
private TableColumn<PlacesEntity, PeopleEntity> vstcolName;
@FXML
private TableColumn<PlacesEntity, String> vstcolPlace;
public void readJoin() {
plcTView.getItems().clear();
List<PlacesEntity> tableList = plcIM.read();
for (PlacesEntity pEnt : tableList) {
observableList.add(pEnt);
}
//Here is the other tricky part (that's what i think at least)
vstcolName.setCellValueFactory(new PropertyValueFactory<PlacesEntity, PeopleEntity>("peopleByPeopleId"));
vstcolPlace.setCellValueFactory(new PropertyValueFactory<PlacesEntity, String>("place"));
vstTView.setItems(observableList);
}
}
在连接列中重新调整的数据类似于 Entities.PeopleEntity@864db1b7
...等...我应该得到名称 - 我的意思是字符串.. 另一列正常显示。
顺便说一句,如果我尝试在控制台而不是 tableview 中显示 HQL 查询的结果,我会得到完全相同的结果。不要介意怪异列的变化。这是因为我试图将数字(仍然是 strind 数据类型)放在我之前拥有的名称的位置,看看是否会发生任何变化......但问题仍然存在。
解决方案
如果我理解正确,您的问题与“名称”列有关。具体来说,该列中显示的值看起来像是某种奇怪的、深奥的语言。每当您看到与"com.example.Foo@12ef485d"
您最有可能看到的 Java 输出相似的输出时,就是默认Object#toString()
方法的结果:
返回对象的字符串表示形式。通常,该
toString
方法返回一个“以文本方式表示”该对象的字符串。结果应该是一个简洁但信息丰富的表示,易于人们阅读。建议所有子类重写此方法。class的
toString
方法Object
返回一个字符串,该字符串由对象作为实例的类的名称、at 符号字符“@
”和对象的哈希码的无符号十六进制表示形式组成。换句话说,此方法返回一个等于以下值的字符串:getClass().getName() + '@' + Integer.toHexString(hashCode())
您看到此输出的原因是由于工作方式TableColumn
。ATableColumn
有两个cellValueFactory
:
需要设置单元格值工厂以指定如何填充单个 TableColumn 中的所有单元格。单元格值工厂是一个回调,它提供一个 TableColumn.CellDataFeatures 实例,并期望返回一个 ObservableValue。返回的 ObservableValue 实例将在内部进行观察,以允许立即更新要反映在屏幕上的值。如何设置单元格值工厂的示例是:
lastNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() { public ObservableValue<String> call(CellDataFeatures<Person, String> p) { // p.getValue() returns the Person instance for a particular TableView row return p.getValue().lastNameProperty(); } });
一种常见的方法是希望使用 Java bean 中的单个值填充 TableColumn 中的单元格。为了支持这种常见场景,有 PropertyValueFactory 类。有关如何使用它的更多信息,请参阅此类,但这里简要介绍如何使用 PropertyValueFactory 类简化上述用例:
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName"));
还有一个cellFactory
:
此列中所有单元的单元工厂。单元工厂负责为单个表格列呈现每个 TableCell 中包含的数据。
默认情况下,TableColumn 使用默认的单元格工厂,但这可以用自定义实现替换,例如以不同的方式显示数据或支持编辑。有很多关于在其他地方创建自定义单元格工厂的文档(参见 Cell 和 TableView例如)。
最后,javafx.scene.control.cell 包中提供了许多预先构建的单元工厂。
换句话说,它cellValueFactory
决定了将显示什么cellFactory
值,并且可以自定义如何显示所述值。如果未cellFactory
设置自定义,则使用DEFAULT_CELL_FACTORY
:
如果没有在 TableColumn 实例上指定 cellFactory,则默认使用这个。目前,如果它是一个节点,它只是在
graphic
属性中呈现 TableCell 项属性,或者如果它不为空,它只是调用,在属性内设置结果字符串。item
toString()
text
在您的代码中,您目前拥有:
...
@FXML
private TableColumn<PlacesEntity, PeopleEntity> vstcolName;
...
public void readJoin()
...
vstcolName.setCellValueFactory(new PropertyValueFactory<>("peopleByPeopleId"));
...
}
...
这样做是将vstcolName
列配置为从 a 中提取 aPeopleEntity
作为PlacesEntity
each 的值TableCell
。这里的问题是您没有设置 a cellFactory
,这意味着每个TableCell
都将显示PeopleEntity#toString()
(并且由于您没有覆盖该方法,因此您将获得默认Object#toString()
输出)。根据列的名称(即“名称”),我假设您实际上希望单元格显示PeopleEntity#getPplName()
。为此,您需要设置 acellFactory
并返回一个TableCell
覆盖的自定义updateItem(T,boolean)
:
updateItem 方法不应由开发人员调用,但它是开发人员重写以允许他们自定义单元格的视觉效果的最佳方法。澄清一下,开发人员不应该在他们的代码中调用这个方法(他们应该把它留给 UI 控件,例如 ListView 控件)来调用这个方法。但是,使用 updateItem 方法的目的是让开发人员在指定自定义单元工厂(再次,如 ListView 单元工厂)时,可以重写 updateItem 方法以允许对单元进行完全自定义。
Cell 的子类正确覆盖 updateItem方法非常重要,否则会导致出现空白单元格或单元格中出现意外内容等问题。以下是如何正确覆盖 updateItem 方法的示例:
protected void updateItem(T item, boolean empty) { super.updateItem(item, empty); if (empty || item == null) { setText(null); setGraphic(null); } else { setText(item.toString()); } }
请注意此代码示例中的两个重要点:
- 我们调用 super.updateItem(T, boolean) 方法。如果不这样做,则 item 和 empty 属性设置不正确,您最终可能会遇到图形问题。
- 我们测试空条件,如果为真,我们将文本和图形属性设置为空。如果我们不这样做,几乎可以保证最终用户会意外地看到单元格中的图形伪影。
在您的情况下,它可能看起来像:
vstcolName.setCellFactory(tv -> new TableCell<>() {
@Override
protected void updateItem(PeopleEntity item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
setText(item.getPplName());
}
}
});
注意:配置a的cellValueFactory
and应该只发生一次,在初始化期间。您当前在一个名为的公共方法中设置了它,该方法似乎经常被调用。当您使用 FXML 时,您应该将配置移动到控制器的方法中。cellFactory
TableColumn
cellValueFactory
readJoin()
TableColumn
@FXML private void initialize() { ... }
推荐阅读
- atg-dynamo - ATG Dynamo 的文档在哪里?
- flutter - 无法打开设置文件“C:\flutter Zip\flutter\manu\android\settings.gradle”的设置通用类缓存
- java - Springboot应用程序在运行本地vs包maven时读取不同的路径
- docker - 钱包环境变量从 nginx 到 vue
- java - Twilio 为来电者和客户接听后说“正在录制通话”
- python - 为什么 tk windows 在有动作之前以错误的顺序显示
- google-apps-script - 我正在尝试将 paypal IPN 与 Google 表格和 Gmail 结合使用,以在买家购买数字下载但太难时设置自动电子邮件回复
- vb.net - 价格和数量的字典
- r - 保存 RData 文件
- excel - 如何在 Excel 中使用 VBA 复制新行、调整表格大小和删除空白行?