英文:
Error at binding data - JavaFx and SQLite
问题
I'm working on a small project, trying to bind data from a database to TableView(in main.fxml) and below is my error. Based on the error, it seems to me is that something wrong in the fxml file.
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class javafx.scene.control.TableColumn cannot be cast to class javafx.scene.control.TableColumn$CellDataFeatures (javafx.scene.control.TableColumn and javafx.scene.control.TableColumn$CellDataFeatures are in module javafx.controls of loader 'app')
at javafx.controls/javafx.scene.control.cell.PropertyValueFactory.call(PropertyValueFactory.java:133)
at javafx.controls/javafx.scene.control.skin.TableRowSkin.createCell(TableRowSkin.java:213)
...
package com.example.musicui;
import com.example.musicui.model.Artist;
import com.example.musicui.model.Datasource;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
public class Controller {
@FXML
private TableView<Artist> artistTable;
public void listArtists() {
Task<ObservableList<Artist>> task = new GetAllArtistsTask();
artistTable.itemsProperty().bind(task.valueProperty());
new Thread(task).start(); // *** ERROR HERE ***
}
}
class GetAllArtistsTask extends Task {
@Override
public ObservableList<Artist> call() {
return FXCollections.observableArrayList(Datasource.getInstance().queryArtists(Datasource.ORDER_BY_ASC));
}
}
public class Main extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("main.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 800, 600);
Controller controller = fxmlLoader.getController();
controller.listArtists();
stage.setTitle("Music DataBase");
stage.setScene(scene);
stage.show();
}
// Rest of the code...
}
public class Datasource {
// Rest of the code...
}
The below is my main.fxml
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0"
fx:controller="com.example.musicui.Controller" xmlns:fx="http://javafx.com/fxml/1">
<center>
<TableView fx:id="artistTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn prefWidth="${artistTable.width}" text="Name" >
<cellFactory>
<PropertyValueFactory property="name"/>
</cellFactory>
</TableColumn>
</columns>
<BorderPane.margin>
<Insets right="10.0" />
</BorderPane.margin>
</TableView>
</center>
<right>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="170.00" spacing="20.0" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets right="10.0"/>
</BorderPane.margin>
<Button maxWidth="Infinity" mnemonicParsing="false" text="List Artists"/>
<Button maxWidth="Infinity" mnemonicParsing="false" text="Show Albums (artist)"/>
<Button maxWidth="Infinity" mnemonicParsing="false" text="Update Artist"/>
</VBox>
</right>
<bottom>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<ProgressBar visible="false" prefWidth="200.0" progress="0.0">
<HBox.margin>
<Insets left="50.0"/>
</HBox.margin>
</ProgressBar>
</HBox>
</bottom>
</BorderPane>
In the Controller class, the error occurred with this line of code new Thread(task).start()
. What I can think of is that the problem could be in the fxml file.
Can someone have a look at the code and tell me the problems/or how to fix it?
Thanks.
英文:
I'm working on a small project, trying to bind data from a database to TableView(in main.fxml) and below is my error. Based on the error, it seems to me is that something wrong in the fxml file.
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class javafx.scene.control.TableColumn cannot be cast to class javafx.scene.control.TableColumn$CellDataFeatures (javafx.scene.control.TableColumn and javafx.scene.control.TableColumn$CellDataFeatures are in module javafx.controls of loader 'app')
at javafx.controls/javafx.scene.control.cell.PropertyValueFactory.call(PropertyValueFactory.java:133)
at javafx.controls/javafx.scene.control.skin.TableRowSkin.createCell(TableRowSkin.java:213)
at javafx.controls/javafx.scene.control.skin.TableRowSkin.createCell(TableRowSkin.java:62)
at javafx.controls/javafx.scene.control.skin.TableRowSkinBase.createCellAndCache(TableRowSkinBase.java:740)
at javafx.controls/javafx.scene.control.skin.TableRowSkinBase.recreateCells(TableRowSkinBase.java:734)
at javafx.controls/javafx.scene.control.skin.TableRowSkinBase.<init>(TableRowSkinBase.java:158)
at javafx.controls/javafx.scene.control.skin.TableRowSkin.<init>(TableRowSkin.java:89)
at javafx.controls/javafx.scene.control.TableRow.createDefaultSkin(TableRow.java:213)
at javafx.controls/javafx.scene.control.Control.doProcessCSS(Control.java:897)
at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:146)
at javafx.graphics/javafx.scene.Node.processCSS(Node.java:9456)
at javafx.graphics/javafx.scene.Node.applyCss(Node.java:9543)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1814)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.getCell(VirtualFlow.java:1791)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.getOrCreateCellSize(VirtualFlow.java:2966)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.getOrCreateCellSize(VirtualFlow.java:2949)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.recalculateAndImproveEstimatedSize(VirtualFlow.java:3021)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.recalculateEstimatedSize(VirtualFlow.java:3013)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1052)
at javafx.controls/javafx.scene.control.skin.VirtualFlow$5.invalidated(VirtualFlow.java:885)
at javafx.base/javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:113)
at javafx.base/javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:148)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.setCellCount(VirtualFlow.java:899)
at javafx.controls/javafx.scene.control.skin.TableViewSkinBase.updateItemCount(TableViewSkinBase.java:555)
at javafx.controls/javafx.scene.control.skin.VirtualContainerBase.checkState(VirtualContainerBase.java:184)
at javafx.controls/javafx.scene.control.skin.VirtualContainerBase.layoutChildren(VirtualContainerBase.java:159)
at javafx.controls/javafx.scene.control.skin.TableViewSkinBase.layoutChildren(TableViewSkinBase.java:407)
at javafx.controls/javafx.scene.control.Control.layoutChildren(Control.java:601)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1207)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1214)
at javafx.graphics/javafx.scene.Scene.doLayoutPass(Scene.java:579)
at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2515)
at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:421)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:420)
at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:450)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:575)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:555)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:548)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:353)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class javafx.scene.control.TableColumn cannot be cast to class javafx.scene.control.TableColumn$CellDataFeatures (javafx.scene.control.TableColumn and javafx.scene.control.TableColumn$CellDataFeatures are in module javafx.controls of loader 'app')
at javafx.controls/javafx.scene.control.cell.PropertyValueFactory.call(PropertyValueFactory.java:133)
at javafx.controls/javafx.scene.control.skin.TableRowSkin.createCell(TableRowSkin.java:213)
at javafx.controls/javafx.scene.control.skin.TableRowSkin.createCell(TableRowSkin.java:62)
at javafx.controls/javafx.scene.control.skin.TableRowSkinBase.createCellAndCache(TableRowSkinBase.java:740)
at javafx.controls/javafx.scene.control.skin.TableRowSkinBase.recreateCells(TableRowSkinBase.java:734)
at javafx.controls/javafx.scene.control.skin.TableRowSkinBase.<init>(TableRowSkinBase.java:158)
at javafx.controls/javafx.scene.control.skin.TableRowSkin.<init>(TableRowSkin.java:89)
at javafx.controls/javafx.scene.control.TableRow.createDefaultSkin(TableRow.java:213)
at javafx.controls/javafx.scene.control.Control.doProcessCSS(Control.java:897)
at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:146)
at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:146)
at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:146)
at javafx.graphics/javafx.scene.Parent.doProcessCSS(Parent.java:1400)
at javafx.graphics/javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
at javafx.graphics/com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSSImpl(ControlHelper.java:63)
at javafx.controls/com.sun.javafx.scene.control.ControlHelper.superProcessCSS(ControlHelper.java:55)
at javafx.controls/javafx.scene.control.Control.doProcessCSS(Control.java:886)
at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:146)
at javafx.graphics/javafx.scene.Node.processCSS(Node.java:9456)
at javafx.graphics/javafx.scene.Node.processCSS(Node.java:9449)
at javafx.graphics/javafx.scene.Scene.doCSSPass(Scene.java:572)
at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2510)
at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:421)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:420)
at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:450)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:575)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:555)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:548)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:353)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
package com.example.musicui;
import com.example.musicui.model.Artist;
import com.example.musicui.model.Datasource;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
public class Controller {
@FXML
private TableView<Artist> artistTable;
public void listArtists() {
Task<ObservableList<Artist>> task = new GetAllArtistsTask();
artistTable.itemsProperty().bind(task.valueProperty());
new Thread(task).start(); // *** ERROR HERE ***
}
}
class GetAllArtistsTask extends Task {
@Override
public ObservableList<Artist> call() {
return FXCollections.observableArrayList(Datasource.getInstance().queryArtists(Datasource.ORDER_BY_ASC));
}
}
public class Main extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("main.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 800, 600);
Controller controller = fxmlLoader.getController();
controller.listArtists();
stage.setTitle("Music DataBase");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
@Override
public void init() throws Exception {
super.init();
if(!Datasource.getInstance().open()){
System.out.println("FATAL ERROR: Couldn't connect to database");
Platform.exit();
}
}
@Override
public void stop() throws Exception {
super.stop();
Datasource.getInstance().close();
}
public class Datasource {
public boolean open() {
try {
conn = DriverManager.getConnection(CONNECTION_STRING);
querySongInfoView = conn.prepareStatement(QUERY_VIEW_SONG_INFO_PREP);
insertIntoArtists = conn.prepareStatement(INSERT_ARTIST, Statement.RETURN_GENERATED_KEYS);
insertIntoAlbums = conn.prepareStatement(INSERT_ALBUMS, Statement.RETURN_GENERATED_KEYS);
insertIntoSongs = conn.prepareStatement(INSERT_SONGS);
queryArtist = conn.prepareStatement(QUERY_ARTIST);
queryAlbum = conn.prepareStatement(QUERY_ALBUM);
return true;
} catch (SQLException e) {
System.out.println("Couldn't connect to database: " + e.getMessage());
e.printStackTrace();
return false;
}
}
public void close() {
try {
if (insertIntoArtists != null) {
insertIntoArtists.close();
}
if (insertIntoAlbums != null) {
insertIntoAlbums.close();
}
if (insertIntoSongs != null) {
insertIntoSongs.close();
}
if (querySongInfoView != null) {
querySongInfoView.close();
}
if (queryArtist != null) {
queryArtist.close();
}
if (queryAlbum != null) {
queryAlbum.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
System.out.println("Couldn't close connection: " + e.getMessage());
}
}
//SELECT * FROM TABLE_ARTISTS ORDER BY COLUMN_ARTIST_NAME COLLATE NOCASE DESC;
public List<Artist> queryArtists(int sortOrder) {
StringBuilder sb = new StringBuilder("SELECT * FROM ");
sb.append(TABLE_ARTISTS);
if (sortOrder != ORDER_BY_NONE) {
sb.append(" ORDER BY ");
sb.append(COLUMN_ARTIST_NAME);
sb.append(" COLLATE NOCASE ");
if (sortOrder == ORDER_BY_DESC) {
sb.append("DESC");
} else {
sb.append("ASC");
}
}
try (Statement statement = conn.createStatement();
ResultSet results = statement.executeQuery(sb.toString())) {
List<Artist> artists = new ArrayList<>();
while (results.next()) {
Artist artist = new Artist();
artist.setId(results.getInt(INDEX_ARTIST_ID));
artist.setName(results.getString(INDEX_ARTIST_NAME));
artists.add(artist);
}
return artists;
} catch (SQLException e) {
System.out.println("Query failed: " + e.getMessage());
return null;
}
}
The below is my main.fxml
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0"
fx:controller="com.example.musicui.Controller" xmlns:fx="http://javafx.com/fxml/1">
<center>
<TableView fx:id="artistTable" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn prefWidth="${artistTable.width}" text="Name" >
<cellFactory>
<PropertyValueFactory property="name"/>
</cellFactory>
</TableColumn>
</columns>
<BorderPane.margin>
<Insets right="10.0" />
</BorderPane.margin>
</TableView>
</center>
<right>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="170.00" spacing="20.0" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets right="10.0"/>
</BorderPane.margin>
<Button maxWidth="Infinity" mnemonicParsing="false" text="List Artists"/>
<Button maxWidth="Infinity" mnemonicParsing="false" text="Show Albums (artist)"/>
<Button maxWidth="Infinity" mnemonicParsing="false" text="Update Artist"/>
</VBox>
</right>
<bottom>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<ProgressBar visible="false" prefWidth="200.0" progress="0.0">
<HBox.margin>
<Insets left="50.0"/>
</HBox.margin>
</ProgressBar>
</HBox>
</bottom>
</BorderPane>
In the Controller class, the error occurred with this line of code new Thread(task).start()
. What I can think of is that the problem could be in the fxml file.
Can someone have a look at the code and tell me the problems/or how to fix it?
Thanks.
答案1
得分: 1
在FXML中,PropertyValueFactory
应该在cellValueFactory
元素中定义,而不是在cellFactory
元素中。
无论如何,FXML应该仅用于定义布局,而不用于通用编程。
在代码中,在控制器的initialize
方法中定义工厂,而不是在FXML中。
使用lambda表达式,参见:
- https://stackoverflow.com/questions/72437983/why-should-i-avoid-using-propertyvaluefactory-in-javafx
FAQ
>你能解释一下你所说的“FXML应该只用于定义布局,而不是通用编程”是什么意思吗?
这只是我的个人观点。
FXML中的ML代表标记语言。正如在链接的文章中所指出的,有不同类型的标记语言(表示性、过程性和描述性)。虽然FXML可以包含所有三种类型的元素,但它主要是一种表示性和描述性的标记语言。
对于过程部分以及将UI与模型数据绑定,最好在代码中完成,特别是Java代码。FXML具有在FXML文档中包含脚本(例如JavaScript)的能力。但一般来说,我认为将逻辑放在单独的控制器中更好,因为这样可以更好地将逻辑与标记分开。
此外,FXML具有定义对象、使用绑定表达式和使用依赖于反射的功能,例如PropertyValueFactory,以与模型数据关联。在FXML中定义的所有过程和绑定功能只在运行时起作用,而不是在编译时,而且从好的IDE中获得的智能编辑支持较少。由此产生的运行时错误通常不直观且难以理解,并且在FXML文档和模型之间存在紧密的绑定,这使得在不影响另一个的情况下更改一个变得更加困难。
出于上述所有原因,我认为在FXML中除了定义UI组件和布局外,不应该做太多事情。甚至样式也最好使用CSS来完成。
至于PropertyValueFactory
,目前有588个与之相关的JavaFX标签问题,其中许多是由于PropertyValueFactory
绑定的运行时性质而提出的。当它失败时,人们不知道发生了什么。PropertyValueFactory
只是一个糟糕的遗留设计。它根本不应该被创建。最好的方法是在代码中使用lambda来实现这个功能,在编译器会在应用程序运行之前告诉你是否存在问题,你可以在运行应用程序之前修复它。
英文:
In the FXML, the PropertyValueFactory
should be defined in a cellValueFactory
element, not a cellFactory
element.
Anyway, FXML should only be used for defining the layout, not for general-purpose programming.
Define factories in code, in the controller initialize
method, not in FXML.
Use a lambda, see:
- https://stackoverflow.com/questions/72437983/why-should-i-avoid-using-propertyvaluefactory-in-javafx
FAQ
> could you explain to me what do you mean by this "FXML should only be used for defining layout, not for general purpose programming".
This is just my opinion.
The ML in FXML stands for Markup Language. As noted, in the linked article there are different types of markup languages, (presentational, procedural and descriptive). While FXML can have elements of all three types, it is primarily a presentational and descriptive markup.
For the procedural portions, and the binding of the UI to model data, you are better off doing that in code, specifically Java code. FXML has the ability to include scripts (e.g. JavaScript) in the FXML documents. But generally, Java in a separate controller is better IMO as logic is better separated from the markup.
Also, FXML has the ability to define objects and use binding expressions and to use facilities like the PropertyValueFactory that rely on reflection to tie into model data. All the procedural and binding functions like this that are defined in FXML work only at runtime, not at compile time, plus there is less intelligent editing support that you get from a good IDE. The runtime errors that result are often non-intuitive and hard to understand and you have a tight binding between your FXML document and your model, making it more difficult to change one without impacting the other.
For all of the above reasons, I don't think it is a good idea to do much in FXML beyond defining the UI components and layout. Even styling is better done in CSS.
As far as PropertyValueFactory
goes, there are currently 588 questions on the JavaFX tag that refer to it, many of them are asked because of the runtime nature of the binding of the PropertyValueFactory
. When it fails, people don't know what is going on. PropertyValueFactory
is just a terrible legacy design. It should never have been created. It is better to just implement that functionality in code using a lambda where the compiler will tell you if there is an issue up front and you can fix that before you ever run the application.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论