是否有必要为每个数据库查询创建类才能使用 TableView?

问题描述 投票:0回答:2

在过去的几天里,我浏览了一些解释“如何使用 TableViews?”的 JavaFX 教程。每个教程都会创建数据模型对象,例如

Person
example。只要您只需要像
select Firstname, Lastname from Person
这样的 sql 语句就可以了。

但是,如果您不想为每个选择查询创建数据模型,因为您出于某种原因将数据与其他对象连接起来,该怎么办?例如,

select * from Person join City join Country
。我最近几天了解到的是,如果您创建一个仅包含
List
(代表行)的表格视图,那么处理格式化甚至评估表格视图都会变得很糟糕。

这真的是 javaFX 的事情吗?还是我错过了什么?

更新

为了更清楚,请参阅这个问题。我在阅读问题的答案和评论后第一次发现这个问题,但我对链接问题的答案不满意。

java sql oop javafx tableview
2个回答
3
投票

你的问题在这里混淆了两个概念:一方面是

javafx.scene.control.TableView
,另一方面是SQL/ORM。让我们忘记 SQL 查询之类的,我认为您主要关心的是
TableView

TableView
必须 填充为
List
。该列表的内部类型必须与TableView的通用类型匹配。因此,例如
TableView<Person>
将填充为
List<Person>

除此之外,表示行的对象的实际类型可以是anything。它不需要甚至必须包含数据本身,或者它很可能是,例如,一个

Map<String, Object>
。在这种情况下,您将通过为每列定义
CellValueFactory
将行的键映射到每列,这将返回所选键的条目值。

然后您可以通过为列定义

String
将此值转换为
Node
和/或
CellFactory

具有 JavaFX 属性的对象可以更轻松地映射,因为存在预制的

PropertyValueFactory
,仅需要属性名称。但它们并不是唯一的出路。

每行使用

Map<String, Object>
的示例:

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class App extends Application {

  private TableView<Map<String, Object>> tableView;
  private TableColumn<Map<String, Object>, String> nameCol;
  private TableColumn<Map<String, Object>, Integer> ageCol;
  
  private Stage stage;

  @Override
  public void start(Stage stage) {
    this.stage = stage;

    //Insert a TableView into scene
    this.tableView = new TableView<>();
    this.nameCol = new TableColumn<>("Name");
    this.nameCol.setPrefWidth(250);
    ageCol = new TableColumn<>("Age");
    tableView.getColumns().add(nameCol);
    tableView.getColumns().add(ageCol);
    
    nameCol.setCellValueFactory(param -> {
      Map<String, Object> v = param.getValue();
      return new SimpleStringProperty(v == null ? null : (String) v.get("name"));
    });
    
    ageCol.setCellValueFactory(param -> {
      Map<String, Object> v = param.getValue();
      return new SimpleObjectProperty<Integer>(v == null ? null : (Integer) v.get("age"));
    });
    ageCol.setCellFactory(param -> {
      return new TableCell<>() {
        @Override
        public void updateItem(Integer item, boolean empty) {
          if (empty || item == null)
            super.setText("");
          else
            super.setText(NumberFormat.getIntegerInstance().format(item));
          //Could also call setGraphic(Node)
        }
      };
    });
    
    final Scene scene = new Scene(new BorderPane(tableView), 640, 480);

    stage.setScene(scene);
    stage.setOnShown(event -> stageReady()); //Continue when stage is rendered
    stage.show();
  }

  private void stageReady() {
    //Generate data
    List<Map<String, Object>> data = new ArrayList<>();
    Map<String, Object> obj1 = new HashMap<>();
    obj1.put("name", "Name of Object 1");
    obj1.put("age", 42);
    data.add(obj1);
    Map<String, Object> obj2 = new HashMap<>();
    obj2.put("name", "OBJECT 2");
    obj2.put("age", 53);
    data.add(obj2);
    //Show data
    tableView.getItems().setAll(data);
  }

  public static void main(String[] args) {
    launch();
  }

}

ageCol.setCellFactory
仅用于演示。如果我没有设置它,那么
Integer
单元格将被渲染,为单元格
Integer.toString()
属性调用
text


3
投票

here所示,数据库查询可用于构造

ObservableList<Map>
属性值对。如将数据映射添加到表中所示,
MapValueFactory
是“
Callback
接口的便捷实现”,就像
PropertyValueFactory
一样,“专为在
TableColumn
内使用而设计。如here 讨论过,便利是有代价的。

受此相关 example 的启发,下面的变体显示了

TableView
,其中包含由
Map<String,​String>
返回的
System.getenv()
中包含的条目。尽管该地图不可修改,但显示了简单的编辑支持。

import java.util.Map;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

/**
 * https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/table-view.htm
 * https://stackoverflow.com/a/18620705/230513
 * https://stackoverflow.com/a/69781121/230513
 * https://stackoverflow.com/a/9134371/230513
 */
public class TableMapSample extends Application {

    private final String columnKey = "Key";
    private final String columnValue = "Value";

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        ObservableList<Map.Entry<String, String>> list
            = FXCollections.observableArrayList(System.getenv().entrySet());
        TableView<Map.Entry<String, String>> table = new TableView<>(list);
        table.setEditable(true);
        table.getSelectionModel().setCellSelectionEnabled(true);

        TableColumn<Map.Entry<String, String>, String> keyColumn = new TableColumn<>(columnKey);
        keyColumn.setCellValueFactory((TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p)
            -> new SimpleStringProperty(p.getValue().getKey()));
        table.getColumns().add(keyColumn);
        keyColumn.setCellFactory(TextFieldTableCell.forTableColumn());

        TableColumn<Map.Entry<String, String>, String> valueColumn = new TableColumn<>(columnValue);
        valueColumn.setCellValueFactory((TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p)
            -> new SimpleStringProperty(p.getValue().getValue()));
        valueColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        valueColumn.setPrefWidth(800);
        table.getColumns().add(valueColumn);

        stage.setTitle("Table Map.Entry<K,V>");
        stage.setScene(new Scene(new StackPane(table)));
        stage.show();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.