0

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>

enter image description here

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.

MaTa
  • 1
  • @jewelsea, Thanks for your comments. I just changed it and it works. WOOOHOOOO. For this comment "used to express exuberant delight or approval." I will check out, but could you explain to me what do you mean by this "FXML should only be used for defining layout, not for general purpose programming". – MaTa May 29 '23 at 21:06
  • Changed comment into an answer. – jewelsea May 30 '23 at 15:51

1 Answers1

1

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:


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.

jewelsea
  • 150,031
  • 14
  • 366
  • 406