JavaFX 动态 TableView 不显示数据库中的数据

JavaFX dynamic TableView is not showing data from the database

我使用 Scene Builder 创建了一个 TableView,如下所示 -

这是FXML代码-

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>


<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="508.0" prefWidth="483.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="imran.jfx.application.Controller">
   <children>
      <TableView fx:id="tableview" layoutX="19.0" layoutY="94.0" prefHeight="402.0" prefWidth="443.0">
        <columns>
          <TableColumn fx:id="id" prefWidth="75.0" text="ID" />
          <TableColumn fx:id="english" prefWidth="170.0" text="English" />
            <TableColumn fx:id="bangla" prefWidth="197.0" text="Bangla" />
        </columns>
      </TableView>
      <Button fx:id="showTableview" layoutX="188.0" layoutY="52.0" mnemonicParsing="false" onAction="#showTableData" text="Show Tables" />
   </children>
</Pane>

我尝试连接数据库并尝试将数据库中的数据显示到表视图中。我试图将这些东西实现到 Controller Class 中,如下所示-

package imran.jfx.application;

import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ResourceBundle;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.util.Callback;

public class Controller implements Initializable{

    private ObservableList<ObservableList> data;

    @FXML
    private TableView<?> tableview;

    @FXML
    private TableColumn<?, ?> id;

    @FXML
    private TableColumn<?, ?> english;

    @FXML
    private TableColumn<?, ?> bangla;

    @FXML
    private Button showTableview;

    ResultSet result;
    PreparedStatement doQuery;
    Connection conn;
    String query;

    @Override
    public void initialize(URL location, ResourceBundle resources) 
    {
        try 
        {
            Class.forName("org.sqlite.JDBC");
        } 
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        String url="jdbc:sqlite::resource:database/bn_words.db";
        try 
        {
            conn = DriverManager.getConnection(url);
        } 
        catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void buildData()
    {
          data = FXCollections.observableArrayList();

          try{
            Class.forName("org.sqlite.JDBC");
            String url="jdbc:sqlite::resource:database/bn_words.db";
            conn = DriverManager.getConnection(url);

            String SQL = "SELECT _id,en_word,bn_word from words";

            ResultSet rs = conn.createStatement().executeQuery(SQL);


            for(int i=0 ; i<rs.getMetaData().getColumnCount(); i++)
            {
                final int j = i;                
                TableColumn col = new TableColumn(rs.getMetaData().getColumnName(i+1));
                col.setCellValueFactory(new Callback<CellDataFeatures<ObservableList,String>,ObservableValue<String>>(){                    
                    public ObservableValue<String> call(CellDataFeatures<ObservableList, String> param) {                                                                                              
                        return new SimpleStringProperty(param.getValue().get(j).toString());                        
                    }                    
                });

                tableview.getColumns().addAll(col); 
                System.out.println("Column ["+i+"] ");
            }

            while(rs.next()){
                ObservableList<String> row = FXCollections.observableArrayList();
                for(int i=1 ; i<=rs.getMetaData().getColumnCount(); i++){
                    row.add(rs.getString(i));
                }
                System.out.println("Row [1] added "+row );
                data.add(row);

            }
            tableview.setItems(data);
          }catch(Exception e){
              e.printStackTrace();
              System.out.println("Error on Building Data");             
          }
      }

    @FXML
    void showTableData(ActionEvent event) {
        tableview = new TableView();
        buildData();
    }
}

问题是,tableview.setItems(data); 行显示错误。我不知道这个错误的原因。如果我将 private TableView<?> tableview; 行编辑为 private TableView tableview;,那么它不会显示 tableview.setItems(data); 行的任何错误。然后应用程序运行良好。但是,当我单击“显示表”按钮时,它不会将数据显示到 TableView 中,而是将数据显示到控制台中。

还有很多警告如下-

我应该忽略这些错误吗?我该如何解决我的问题?

不,不要忽略警告。这些警告是因为您正在对 "generic" 的类型使用 原始类型 ,即参数化。如果这样做,您最终可能会遇到编译器未捕获到的类型不匹配。使用正确声明的类型并使它们正确将使您的程序更加健壮。

例如

TableView,是一个泛型类型:声明为TableView<T>。这里 T 是一个 类型参数 :您可以(松散地说)将 T 视为代表 类型的变量table 的每一行。当您创建一个实际的 TableView 时,您必须指定该特定 TableView 将包含的类型。在您的示例中,每一行都由 ObservableList 表示。现在,ObservableList 也是一个泛型类型:它被声明为 ObservableList<E>,其中 E 表示列表中每个元素的类型。在您的情况下,在 buildData() 方法中,您为每一行创建一个 ObservableList<String> 。因此,您的 table 类型是 ObservableList<String>,因此您应该替换

@FXML
private TableView<?> tableView ;

@FXML
private TableView<ObservableList<String>> tableView ;

属于TableView<T>items表示为ObservableList<T>。因为,对于你的 table,TObservableList<String>,你需要你的项目是 ObservableList<ObservableList<String>>(读作 "an observable list of observable lists of strings")。所以你应该更换

private ObservableList<ObservableList> data;

private ObservableList<ObservableList<String>> data ;

这些更改应该会消除 tableView.setItems(data); 中的编译错误。

您还应该修复 TableColumn 的声明。 TableColumn 定义为 TableColumn<S,T>。这里 S 是 table 的类型(即 table 中代表一行的每个项目的类型:它与 TableView 中的 T 相同定义),T 是列中单元格中每个值的类型。在所有列中,单元格中的每个值都是 String,因此在 buildData() 中,您应该具有:

TableColumn<ObservableList<String>, String> col = new TableColumn<>(rs.getMetaData().getColumnName(i+1));

(请注意,因为这些是您要添加到 table 的列,所以不清楚为什么您还在 FXML 中定义了列;这对于您提出的问题有点离题虽然问。)

当您使用通用列表作为 table 的数据类型时,所有这些都会变得相当复杂。由于在此您知道数据库中有多少列,以及这些列代表什么,我非常强烈鼓励table 通过定义一个 class 来表示每一行中的数据来更具体。我不知道你的应用程序是什么,所以我会(可能不正确)猜测它正在做一些翻译。所以我会做

public class TranslatedWord {

    private final StringProperty id = new SimpleStringProperty();

    public StringProperty idProperty() {
        return id ;
    }
    public final String getId() {
        return idProperty().get();
    }
    public final void setId(String id) {
        idProperty().set(id);
    }

    private final StringProperty english = new SimpleStringProperty();

    public StringProperty englishProperty() {
        return english ;
    }
    public final String getEnglish() {
        return englishProperty().get();
    }
    public final void setEnglish(String english) {
        englishProperty().set(english);
    }

    private final StringProperty bangla = new SimpleStringProperty();

    public StringProperty banglaProperty() {
        return bangla ;
    }
    public final String getBangla() {
        return banglaProperty().get();
    }
    public final void setId(String bangla) {
        banglaProperty().set(bangla);
    }

}

现在你做了一个TableView<TranslatedWord>TableColumn<TranslatedWord, String>,等等。你的data变成了一个ObservableList<TranslatedWord>,你的buildData()方法有这样的代码

        while(rs.next()){
            TranslatedWord word = new TranslatedWord();
            word.setId(rs.getString("id"));
            word.setEnglish(rs.getString("english"));
            word.setBangla(rs.getString("bangla"));
            System.out.println("Row [1] added "+row );
            data.add(row);

        }

一旦您习惯了这种结构,您会发现这种风格更易于维护,因为类型指定了它们所代表的内容。

你在这段代码中还有很多其他错误:正如我之前所说,你在 FXML 中创建了 TableViewTableColumn,但是你在控制器中创建了新的并操纵那些。因此,即使您解决了您实际询问的问题,您也可能会发现除非您也解决了这些问题,否则它不会按您希望的方式工作。但是摆脱编译错误和警告将是一个开始...