如何使用Java Record with Data from a Database使用数据填充JavaFX TableView

6xfqseft  于 5个月前  发布在  Java
关注(0)|答案(2)|浏览(42)

简短问题:使用记录从数据库中填充数据到Java FX表视图的最佳方法是什么?(而不是使用具有getter和setter的典型对象类)
**完整问题:**我最近创建了这个InventoryRecord.java文件,以取代我用来填充tableview的传统对象“Inventory”:

import java.util.List;

public record InventoryRecord(
        String make
        , String model
        , String officialModel
        , String upc
        , List<String> category
        , String type
        , String description
        ,String vendor
        , Double cost
        , Double price
        ) {}

字符串
我想使用这个记录作为我的框架,使用数据库中的数据填充我的TableView(在这种情况下是Neo4j,但这并不重要)
在我有这个InventoryRecord记录之前,我使用了一个典型的名为Inventory的类,它看起来像这样(当然,我已经缩短了它以保持问题的可读性,假设每个Inventory属性都有getter/setter。

public class Inventory {
    private String make;
    private String model;
    private String officialModel;
    private List<String> category;
    private String type;
    private String description;
    private String vendor;
    private Double cost;
    private Double price;
    private String upc;
    //Method for actual instantiation of an Inventory Object, uses setter methods in order to perform validations for each field
    public Inventory(String make, String model, String officialModel, String upc, List<String> category, String type, String description,String vendor, Double cost, Double price) {
        this.setMake(make);
        this.setModel(model);
        this.setOfficialModel(officialModel);
        this.setUpc(upc);
        this.setCategory(category);
        this.setType(type);
        this.setDescription(description);
        this.setVendor(vendor);
        this.setCost(cost);
        this.setPrice(price);
    }


这就是我用查询结果填充表的方式

while (result.hasNext()) {
                //result.next() serves as our iterator that increments us through the records and through the while loop
                Record record = result.next();
                
                String make = record.get("make").asString();
                String model = record.get("model").asString();
                String officialModel = record.get("officialModel").asString();
                String upc = record.get("upc").asString();                
                List<String> category = record.get("categories").asList(Value::asString);
                String type = record.get("type").asString();
                String desc = record.get("description").asString();
                String vendor = record.get("vendor").asString();
                Double cost = record.get("cost").isNull() ? null : record.get("cost").asDouble();
                Double price = record.get("price").isNull() ? null : record.get("price").asDouble();
                
                //when creating the anonymous Inventory items, you can send them to the constructor with any name
                inventoryTable.getItems().add(new Inventory(make, model, officialModel,upc, category, type, desc,vendor, cost, price));
            }
            
            //The property value factory must use the property names as string arguments in the constructor
            makeColumn.setCellValueFactory(new PropertyValueFactory<>("make"));
            modelColumn.setCellValueFactory(new PropertyValueFactory<>("model"));
            officialModelColumn.setCellValueFactory(new PropertyValueFactory<>("officialModel"));
            categoryColumn.setCellValueFactory(new PropertyValueFactory<>("category"));
            upcColumn.setCellValueFactory(new PropertyValueFactory<>("upc"));
            typeColumn.setCellValueFactory(new PropertyValueFactory<>("type"));
            descriptionColumn.setCellValueFactory(new PropertyValueFactory<>("description"));
            vendorColumn.setCellValueFactory(new PropertyValueFactory<>("vendor"));
            costColumn.setCellValueFactory(new PropertyValueFactory<>("cost"));
            priceColumn.setCellValueFactory(new PropertyValueFactory<>("price"));
            
            inventoryTable.setItems(inventoryTable.getItems());


但是我怎么能把所有的东西都切换到一个库存记录而不仅仅是库存对象,.setCellValueFactory和PropertyValueFactory不喜欢他们看到的东西。我试过这个(在Google Bard的帮助下),它似乎没有给予任何字符串的错误:makeColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().make()));但是对于List和Double属性/列,我试过这个:

categoryColumn.setCellValueFactory(cellData -> new SimpleListProperty<List<String>>(cellData.getValue().category()));
costColumn.setCellValueFactory(cellData -> new SimpleDoubleProperty(cellData.getValue().cost()));


类型不匹配:无法从SimpleListProperty<List >转换为ObservableValue<List>类型不匹配:无法从SimpleDoubleProperty转换为ObservableValue
任何帮助是非常,非常感谢!
我试过这个:

while (result.hasNext()) {
                //result.next() serves as our iterator that increments us through the records and through the while loop
                Record record = result.next();
                
                String make = record.get("make").asString();
                String model = record.get("model").asString();
                String officialModel = record.get("officialModel").asString();
                String upc = record.get("upc").asString();                
                List<String> category = record.get("categories").asList(Value::asString);
                String type = record.get("type").asString();
                String desc = record.get("description").asString();
                String vendor = record.get("vendor").asString();
                Double cost = record.get("cost").isNull() ? null : record.get("cost").asDouble();
                Double price = record.get("price").isNull() ? null : record.get("price").asDouble();
                
                // Create an InventoryRecord directly from the Record
                InventoryRecord inventoryRecord = new InventoryRecord(make,model,officialModel,upc,category,type,desc,vendor,cost,price);
                // Add the InventoryRecord to the table items
                inventoryTable.getItems().add(inventoryRecord);
            }
            
            //The property value factory must use the property names as string arguments in the constructor
            makeColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().make()));
            modelColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().model()));
            officialModelColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().officialModel()));
            categoryColumn.setCellValueFactory(cellData -> new SimpleListProperty<List<String>>(cellData.getValue().category()));
            upcColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().upc()));
            typeColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().type()));
            descriptionColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().description()));
            vendorColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().vendor()));
            costColumn.setCellValueFactory(cellData -> new SimpleDoubleProperty(cellData.getValue().cost()));
            priceColumn.setCellValueFactory(cellData -> new SimpleDoubleProperty(cellData.getValue().price()));
            
            inventoryTable.setItems(inventoryTable.getItems());


但得到这些错误:类型不匹配:无法从SimpleListProperty<List >转换为ObservableValue<List>类型不匹配:无法从SimpleDoubleProperty转换为ObservableValue
我希望看到类似这样的东西(这是它如何与我的Inventory类一起工作的,我试图用InventoryRecord替换它):TableView using Inventory Class

nue99wik

nue99wik1#

PropertyValueFactory类不能处理记录,至少在JavaFX 21中不能,但是你已经知道了(I recommend avoiding that class regardless)。你得到的错误与你将类改为记录没有直接关系。它们是由在一些单元格值工厂中返回错误的类型引起的。
一个SimpleListProperty<E>已经是一个E的列表。通过用List<String>参数化它,你就说你有一个list of lists的字符串。根据你得到的错误,这不是列的值类型。从适当的单元格值工厂返回一个SimpleObjectProperty<List<String>>
另外,SimpleDoublePropertyObservableValue<Number>。但是从你得到的错误来看,你实际上需要返回ObservableValue<Double>。改变适当的单元格值工厂以返回SimpleObjectProperty<Double>。另一种选择是将列的值类型从Double改为Number。然而,我推荐前一种方法。
为了简化这一点,你可以在你的类中创建一个实用方法。类似于:

private <T> void setCellValueFactory(
    TableColumn<InventoryRecord, T> column, Callback<InventoryRecord, T> callback) {
  column.setCellValueFactory(
      data -> {
        T value = callback.call(data.getValue());
        return new SimpleObjectProperty<>(value);
      });
}

字符串
这将允许您像这样定义所有单元格值工厂:

setCellValueFactory(makeColumn, InventoryRecord::make);
setCellValueFactory(modelColumn, InventoryRecord::model);
setCellValueFactory(officialModelColumn, InventoryRecord::officialModel);
setCellValueFactory(categoryColumn, InventoryRecord::category);
setCellValueFactory(upcColumn, InventoryRecord::upc);
setCellValueFactory(typeColumn, InventoryRecord::type);
setCellValueFactory(descriptionColumn, InventoryRecord::description);
setCellValueFactory(vendorColumn, InventoryRecord::vendor);
setCellValueFactory(costColumn, InventoryRecord::cost);
setCellValueFactory(priceColumn, InventoryRecord::price);

kpbwa7wx

kpbwa7wx2#

感谢@Slaw非常优雅的解决方案和解释。我达到了一个稍微不同的解决方案,因为在我的研究中,我意识到我不知道什么是JavaFX bean。主要的解决方案是我只是设置真正的JavaFX getter和setter,在我的实际库存构造函数中使用bean“property”方法。
这是我的构造函数最终看起来像(缩短)的各种数据类型:

public class Inventory {
  private StringProperty officialModel;
  private ListProperty<String> category;
  private DoubleProperty cost;

    public final String getOfficialModel() {
        return this.officialModelProperty().get();
    }
    

    public final void setOfficialModel(final String officialModel) {
        this.officialModelProperty().set(officialModel);
    }
    

    public final ListProperty<String> categoryProperty() {
        if (category == null) {
            category = new SimpleListProperty<String>(this, "category");
        }
        return category;
    }
    

    public final ObservableList<String> getCategory() {
        return this.categoryProperty().get();
    }
    

    public final void setCategory(final ObservableList<String> category) {
        this.categoryProperty().set(category);
    }
public final DoubleProperty costProperty() {
        if (cost == null) {
            cost = new SimpleDoubleProperty(this, "cost");
        }
        return cost;
    }
    

    public final double getCost() {
        return this.costProperty().get();
    }
    

    public final void setCost(final double cost) {
        this.costProperty().set(cost);
    }

字符串
我的CellValueFactory看起来像这样:

makeColumn.setCellValueFactory(cellData -> cellData.getValue().makeProperty());
modelColumn.setCellValueFactory(cellData -> cellData.getValue().modelProperty());
officialModelColumn.setCellValueFactory(cellData -> cellData.getValue().officialModelProperty());
//categoryColumn must be read only, so it can't be mutated by user, which protects the list data
categoryColumn.setCellValueFactory(cellData -> new ReadOnlyObjectWrapper<>(cellData.getValue().categoryProperty().get()));
upcColumn.setCellValueFactory(cellData -> cellData.getValue().upcProperty());
typeColumn.setCellValueFactory(cellData -> cellData.getValue().typeProperty());
descriptionColumn.setCellValueFactory(cellData -> cellData.getValue().descriptionProperty());
vendorColumn.setCellValueFactory(cellData -> cellData.getValue().vendorProperty());
costColumn.setCellValueFactory(cellData -> cellData.getValue().costProperty().asObject());  // Wrap double as Object for ObservableValue
priceColumn.setCellValueFactory(cellData -> cellData.getValue().priceProperty().asObject()); // Wrap double as Object for ObservableValue


让我知道这个解决方案是否有任何重大缺陷,如果有,我可能会改用你的方法!

相关问题