使用 HQL JOIN returns 奇怪的数据填充 JavaFX TableView

Populating JavaFX TableView using HQL JOIN returns weird data

HQL Join 给我加入列的奇怪数据

虽然我的 HQL 查询在 Hibernate 控制台中运行良好, 我得到奇怪的结果。 我试图结合相应列的@FXML 注释更改下面的行,但要么出现错误,要么出现空白单元格。 vstcolName.setCellValueFactory(新的 PropertyValueFactory("peopleByPeopleId"));

这是第一个 table 的实体 类。

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);
    }
}

这是我的第二个实体 类 table。

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);
   }
}

joined列中返回的数据是这样的 Entities.PeopleEntity@864db1b7

...等... 我应该得到名字——我的意思是字符串.. 另一列正常显示。

顺便说一句,如果我尝试在控制台而不是 table 视图中显示 HQL 查询的结果,我得到了完全相同的结果。不要介意奇怪的列的变化。这是因为我试图将数字(仍然是 strind 数据类型)放在我以前的名字的位置,看看是否有任何变化......但问题仍然存在。

如果我没理解错的话,你的问题与“姓名”栏有关。具体来说,该列中显示的值看起来像是某种奇怪、深奥的语言。每当您看到 Java 的输出类似于 "com.example.Foo@12ef485d" 时,您最有可能看到的是默认 Object#toString() 方法的结果:

Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method.

The toString method for class Object returns a string consisting of the name of the class of which the object is an instance, the at-sign character '@', and the unsigned hexadecimal representation of the hash code of the object. In other words, this method returns a string equal to the value of:

getClass().getName() + '@' + Integer.toHexString(hashCode())

您看到此输出的原因是 TableColumn 的工作方式。 TableColumn 有两个 cellValueFactory:

The cell value factory needs to be set to specify how to populate all cells within a single TableColumn. A cell value factory is a Callback that provides a TableColumn.CellDataFeatures instance, and expects an ObservableValue to be returned. The returned ObservableValue instance will be observed internally to allow for immediate updates to the value to be reflected on screen. An example of how to set a cell value factory is:

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();
    }   
});  

A common approach is to want to populate cells in a TableColumn using a single value from a Java bean. To support this common scenario, there is the PropertyValueFactory class. Refer to this class for more information on how to use it, but briefly here is how the above use case could be simplified using the PropertyValueFactory class:

lastNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName"));

还有一个cellFactory:

The cell factory for all cells in this column. The cell factory is responsible for rendering the data contained within each TableCell for a single table column.

By default TableColumn uses the default cell factory, but this can be replaced with a custom implementation, for example to show data in a different way or to support editing.There is a lot of documentation on creating custom cell factories elsewhere (see Cell and TableView for example).

Finally, there are a number of pre-built cell factories available in the javafx.scene.control.cell package.

换句话说,cellValueFactory决定了将显示什么值,cellFactory可以自定义如何显示 将显示所述值。如果未设置自定义 cellFactory 则它使用 DEFAULT_CELL_FACTORY:

If no cellFactory is specified on a TableColumn instance, then this one will be used by default. At present it simply renders the TableCell item property within the graphic property if the item is a Node, or it simply calls toString() if it is not null, setting the resulting string inside the text property.

在您的代码中,您目前拥有:

...

@FXML
private TableColumn<PlacesEntity, PeopleEntity> vstcolName;

...

public void readJoin() 
    ...
    vstcolName.setCellValueFactory(new PropertyValueFactory<>("peopleByPeopleId"));
    ...
}

...

这样做是配置 vstcolName 列以提取 PeopleEntity(来自 PlacesEntity)作为每个 TableCell 的值。这里的问题是你没有设置 cellFactory,这意味着每个 TableCell 都会显示 PeopleEntity#toString()(因为你没有重写那个方法,所以你得到默认的 Object#toString() 输出)。基于列的名称(即“名称”),我假设您实际上希望单元格显示 PeopleEntity#getPplName()。为此,您需要设置 cellFactory 和 return 覆盖 updateItem(T,boolean):

的自定义 TableCell

The updateItem method should not be called by developers, but it is the best method for developers to override to allow for them to customise the visuals of the cell. To clarify, developers should never call this method in their code (they should leave it up to the UI control, such as the ListView control) to call this method. However, the purpose of having the updateItem method is so that developers, when specifying custom cell factories (again, like the ListView cell factory), the updateItem method can be overridden to allow for complete customisation of the cell.

It is very important that subclasses of Cell override the updateItem method properly, as failure to do so will lead to issues such as blank cells or cells with unexpected content appearing within them. Here is an example of how to properly override the updateItem method:

protected void updateItem(T item, boolean empty) {
    super.updateItem(item, empty);

    if (empty || item == null) {
        setText(null);
        setGraphic(null);
    } else {
        setText(item.toString());
    }
}

Note in this code sample two important points:

  1. We call the super.updateItem(T, boolean) method. If this is not done, the item and empty properties are not correctly set, and you are likely to end up with graphical issues.
  2. We test for the empty condition, and if true, we set the text and graphic properties to null. If we do not do this, it is almost guaranteed that end users will see graphical artifacts in cells unexpectedly.

在您的情况下,它可能类似于:

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());
        }
    }

});

注意: 配置 TableColumncellValueFactorycellFactory 应该只在初始化期间发生一次。您当前在名为 readJoin() 的 public 方法中设置了 cellValueFactory,这似乎经常被调用。当您使用 FXML 时,您应该将 TableColumn 配置移动到控制器的 @FXML private void initialize() { ... } 方法。