JavaFX中用JFXTreeTableView加载MySQL数据遇类型参数错误求助
我来帮你解决这个JFXTreeTableView的类型边界问题,先回顾下你之前用TableView加载MySQL数据的实现代码,再一步步给出适配你原有逻辑的解决方案:
原有TableView实现代码
数据库连接方法
static public ResultSet AllElement() { try { cnx = connecterDB(); st = cnx.createStatement(); rst = st.executeQuery("SELECT * FROM element"); } catch (Exception ex) { ex.printStackTrace(); } return rst; }
控制器声明
@FXML private TableView<Object> TableElement; @FXML private TableColumn clmID, clmELement, clmPrix, clmQuantite; ObservableList<Object> data;
填充表格方法
public void tableElement() { try { data = FXCollections.observableArrayList(); int r = -1; ResultSet rst = Tools.ConnexionJM.AllElement(); while (rst.next()) { ObservableList<String> row = FXCollections.observableArrayList(); for (int i = 1; i <= 4; i++) { row.add("" + rst.getString(i)); } data.add(row); } TableElement.setItems(data); } catch (SQLException ex) { Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex); } }
列配置方法
public void fillTableElement() { clmID.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>() { public ObservableValue<String> call(TableColumn.CellDataFeatures<ObservableList, String> param) { return new SimpleStringProperty(param.getValue().get(0).toString()); } }); clmELement.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>() { public ObservableValue<String> call(TableColumn.CellDataFeatures<ObservableList, String> param) { return new SimpleStringProperty(param.getValue().get(1).toString()); } }); clmPrix.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>() { public ObservableValue<String> call(TableColumn.CellDataFeatures<ObservableList, String> param) { return new SimpleStringProperty(param.getValue().get(2).toString()); } }); clmQuantite.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>() { public ObservableValue<String> call(TableColumn.CellDataFeatures<ObservableList, String> param) { return new SimpleStringProperty(param.getValue().get(3).toString()); } }); tableElement(); }
你遇到的问题
当尝试声明JFXTreeTableView时:
private JFXTreeTableView<Object> TableViewAP;
收到错误提示:
type argument Object is not within bounds of type-variable S
问题根源
JFoenix的JFXTreeTableView对泛型参数有强制要求:泛型类型必须继承自RecursiveTreeObject<S>。这个类是JFoenix为树形表格设计的基础数据模型,用来管理节点的父子关系,而Object类显然不满足这个继承要求,所以触发了类型边界错误。
适配你原有逻辑的解决方案
咱们一步步修改代码,既满足JFXTreeTableView的要求,又沿用你原来的MySQL数据加载逻辑:
1. 创建对应数据表的实体类(继承RecursiveTreeObject)
为element表创建一个实体类,让它继承RecursiveTreeObject,这样就能符合JFXTreeTableView的泛型要求。这里对应你的表字段,定义属性和对应的Property:
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; public class Element extends RecursiveTreeObject<Element> { private final StringProperty id; private final StringProperty elementName; private final StringProperty prix; private final StringProperty quantite; public Element(String id, String elementName, String prix, String quantite) { this.id = new SimpleStringProperty(id); this.elementName = new SimpleStringProperty(elementName); this.prix = new SimpleStringProperty(prix); this.quantite = new SimpleStringProperty(quantite); } // Getter方法和Property暴露方法 public String getId() { return id.get(); } public StringProperty idProperty() { return id; } public String getElementName() { return elementName.get(); } public StringProperty elementNameProperty() { return elementName; } public String getPrix() { return prix.get(); } public StringProperty prixProperty() { return prix; } public String getQuantite() { return quantite.get(); } public StringProperty quantiteProperty() { return quantite; } }
2. 修正JFXTreeTableView的控制器声明
现在可以用Element类作为泛型参数,正确声明树形表格:
import com.jfoenix.controls.JFXTreeTableColumn; import com.jfoenix.controls.JFXTreeTableView; import javafx.collections.ObservableList; import javafx.fxml.FXML; @FXML private JFXTreeTableView<Element> TableViewAP; @FXML private JFXTreeTableColumn<Element, String> clmTreeID, clmTreeElement, clmTreePrix, clmTreeQuantite; private ObservableList<Element> treeData;
3. 修改数据填充方法适配树形表格
JFXTreeTableView需要用RecursiveTreeItem包装数据,即使你的数据是平级的(没有父子关系),也需要通过它来构建树形结构:
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeItem; import javafx.collections.FXCollections; import javafx.scene.control.TreeItem; import java.sql.SQLException; import java.util.logging.Level; import java.util.logging.Logger; public void fillTreeTableData() { try { treeData = FXCollections.observableArrayList(); ResultSet rst = Tools.ConnexionJM.AllElement(); while (rst.next()) { // 从ResultSet中取出数据,创建Element对象 Element element = new Element( rst.getString(1), rst.getString(2), rst.getString(3), rst.getString(4) ); treeData.add(element); } // 创建根节点,用RecursiveTreeItem包装数据 TreeItem<Element> root = new RecursiveTreeItem<>(treeData, RecursiveTreeObject::getChildren); TableViewAP.setRoot(root); TableViewAP.setShowRoot(false); // 不需要显示根节点的话可以设置为false } catch (SQLException ex) { Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex); } }
4. 配置树形表格的列
JFXTreeTableColumn的配置和普通TableColumn类似,但需要绑定到Element类的Property上,代码更简洁:
public void configureTreeTableColumns() { // 绑定各列到Element的对应Property clmTreeID.setCellValueFactory(cellData -> cellData.getValue().getValue().idProperty()); clmTreeElement.setCellValueFactory(cellData -> cellData.getValue().getValue().elementNameProperty()); clmTreePrix.setCellValueFactory(cellData -> cellData.getValue().getValue().prixProperty()); clmTreeQuantite.setCellValueFactory(cellData -> cellData.getValue().getValue().quantiteProperty()); // 填充数据 fillTreeTableData(); }
5. 初始化时调用配置方法
在控制器的initialize方法中调用列配置方法,确保界面加载时自动填充数据:
@Override public void initialize(URL url, ResourceBundle resourceBundle) { configureTreeTableColumns(); }
额外提示
如果之后需要实现真正的树形结构(比如有父子级的分类数据),只需要在Element类中添加ObservableList<Element> children属性,并实现getChildren()方法返回这个列表,RecursiveTreeItem会自动识别并构建树形结构。
内容的提问来源于stack exchange,提问作者Samer

