Bekomme ResultSet mittels ObservableList nicht ins TableView

gottfried

Mitglied
Hallo
ich arbeite mit Netbeans8.1 und SceneBuilder8.5.0 unter Linux.
Mir gelingt es leider nicht, den Fehler beim Laden der Daten in eine TableView zu finden.
Deshalb bitte ich hier um Hilfe. Die Variante mit dem dynamischen Erstellen der TableColumns
würde ich nur ungern nutzen, ich bitte deshalb mir möglichst bei dieser Variante zu helfen.
Im Code sind einige Anmerkungen betreffs meiner Versuche und des Fehlers.

Klasse Vorgang für die ObservableList:
Java:
package samplefx.ctrl;

/**
 *
 * @author gottfried
 */
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Vorgang {

    private final StringProperty vnr;            // sollte eigentlich als Integer definiert werden
    private final StringProperty datum;          // als Date -->
    private final StringProperty vanr;
    private final StringProperty vart;
    private final StringProperty vbez;
    private final StringProperty brutto;         // als Double -->
    private final StringProperty zedat;          // als Date   ---> alle auf String geändert, zum Testen

    // Default constructor
    // Constructor with some initial data.
    public Vorgang(String vnr, String datum, String vanr, String vart, String vbez, String brutto, String zedat) {
        this.vnr = new SimpleStringProperty(vnr);
        this.datum = new SimpleStringProperty(datum);
        this.vanr = new SimpleStringProperty(vanr);
        this.vart = new SimpleStringProperty(vart);
        this.vbez = new SimpleStringProperty(vbez);
        this.brutto = new SimpleStringProperty(brutto);
        this.zedat = new SimpleStringProperty(zedat);
    }

    //Getters
    public String getVNR() {
        return vnr.get();
    }
    public String getDatum() {
        return datum.get();
    }
    public String getVANR() {
        return vanr.get();
    }
    public String getVArt() {
        return vart.get();
    }
    public String getVBez() {
        return vbez.get();
    }
    public String getBrutto() {
        return brutto.get();
    }
    public String getZEdat() {
        return zedat.get();
    }
 
    //Setters
    public void setVNR(String value) {
        vnr.set(value);
    }
    public void setDatum(String value) {
        datum.set(value);
    }
    public void setVANR(String value) {
        vanr.set(value);
    }
    public void setVArt(String value) {
        vart.set(value);
    }
    public void setVBez(String value) {
        vbez.set(value);
    }
    public void setBrutto(String value) {
        brutto.set(value);
    }
    public void setZEdat(String value) {
        zedat.set(value);
    }
    
    //Property values
    public StringProperty vnrProperty() {
        return vnr;
    }
    public StringProperty datumProperty() {
        return datum;
    }
    public StringProperty vanrProperty() {
        return vanr;
    }
    public StringProperty vartProperty() {
        return vart;
    }
    public StringProperty vbezProperty() {
        return vbez;
    }
    public StringProperty bruttoProperty() {
        return brutto;
    }
    public StringProperty zedatProperty() {
        return zedat;
    }
}

Laden der Vorgangsliste ins TableView
Code:
public void vorgangDat() {
        String sknr = KNR.getText();
        int knr = Integer.parseInt(sknr);
        ObservableList daten = FXCollections.observableArrayList();
        Connection conn = null;
        try {
            Class.forName("org.postgresql.Driver");
            conn = DriverManager.getConnection(jhpn, uname, pw);
            String sSQL = "SELECT vorgang.vnr, vorgang.datum, vorgang.vanr, vorgang.vart, vorgang.vbez, TO_CHAR(brutto.brutto, 'FM999G990D00') AS brutto,"
                    + "vorgang.zedat FROM vorgang vorgang, brutto brutto WHERE vorgang.knr = " + "'" + knr + "'" + " AND vorgang.vnr = brutto.vnr ORDER BY vnr DESC";
            Statement st = conn.createStatement();
            ResultSet rs = st.executeQuery(sSQL);

            while (rs.next()) {
                daten.add(new Vorgang(rs.getString("vnr"), rs.getString("datum"), rs.getString("vanr"), rs.getString("vart"), rs.getString("vbez"), rs.getString("brutto"), rs.getString("zedat")));
                JOptionPane.showMessageDialog(null, "String(): " + rs.getString("vbez"));  // ---> hier wird der richtige Text angezeigt
            }

            System.out.println("Der Inhalt :" + daten);                                    // ---> im Output: Der Inhalt :[samplefx.ctrl.Vorgang@17e2f267]   
            conn.close();
        } catch (ClassNotFoundException | SQLException e) {
            JOptionPane.showMessageDialog(null, "Fehler in vorgangDat(): " + e);
        }

        VNR.setCellValueFactory(new PropertyValueFactory<>("vnr"));              // ---> worauf diese Zeile: Caused by: java.lang.NullPointerException wirft
        Datum.setCellValueFactory(new PropertyValueFactory<>("datum"));
        VANR.setCellValueFactory(new PropertyValueFactory<>("vanr"));
        Art.setCellValueFactory(new PropertyValueFactory<>("vart"));
        Bezeichnung.setCellValueFactory(new PropertyValueFactory<>("vbez"));
        Bruttobetrag.setCellValueFactory(new PropertyValueFactory<>("brutto"));
        Zahldatum.setCellValueFactory(new PropertyValueFactory<>("zedat"));

        TableVorg.setItems(daten);
    }
Im Prinzip geht es um die Zeile
daten.add(new Vorgang(rs.getString("vnr"), rs.getString("datum"), rs.getString("vanr"), rs.getString("vart"), rs.getString("vbez"), rs.getString("brutto"), rs.getString("zedat")));
die im Output statt der erwarteten FXCollections.observableArrayList() das hier erzeugt:
[samplefx.ctrl.Vorgang@17e2f267]
weshalb das Einfügen dann vermutlich die NullPointerException wirft.
Ich hoffe, jemand von Euch hat eine Idee.
Gottfried
 
[samplefx.ctrl.Vorgang@17e2f267]
weshalb das Einfügen dann vermutlich die NullPointerException wirft.
Nein, die Anzeige ist ok. Wenn Du willst, dass vernünftige Werte ausgegeben werden, musst Du in Klasse Vorgang die Methode toString() überschreiben.

Der Stacktrace der Exception enthält eine Angabe über genau die Zeile, in der die NullPointerException (kurz: NPE) auftritt. Was steht in dieser Zeile?
 
Nein, die Anzeige ist ok. Wenn Du willst, dass vernünftige Werte ausgegeben werden, musst Du in Klasse Vorgang die Methode toString() überschreiben.

Der Stacktrace der Exception enthält eine Angabe über genau die Zeile, in der die NullPointerException (kurz: NPE) auftritt. Was steht in dieser Zeile?
hallo mihe7
die betreffende Zeile hatte ich oben im Code bereits mit
---> worauf diese Zeile: Caused by: java.lang.NullPointerException wirft
gekennzeichnet. Zu deinem Hinweis für Klasse Vorgang, ich muss also
public String getVNR() {
return vnr.get();
}
durch toString() ersetzen?
 
die betreffende Zeile hatte ich oben im Code bereits mit
gekennzeichnet.
Dann ist VNR an der Stelle null.

Zu deinem Hinweis für Klasse Vorgang, ich muss also [...] durch toString() ersetzen?
Nein. Es gibt eine Klasse Object, von der jede Klasse in Java direkt oder indirekt abgeleitet ist. Die Klasse Object besitzt eine Methode toString(), die einen String zurückgibt. Diese Methode wird verwendet, um ein Objekt in lesbarer Form auszugeben. Die Standardimplementierung liefert dann etwas wie "samplefx.ctrl.Vorgang@17e2f267". Um das zu ändern, musst Du den Spaß selbst implementieren.

Beispielsweise könntest Du
Java:
@Override
public String toString() {
    return "VNR: " + getVNR();
}
in Klasse Vorgang einbauen, solltest Du oben statt [samplefx.ctrl.Vorgang@17e2f267] etwas wie [VNR: 123] ausgegeben bekommen. Wie Du den String aufbaust, bleibt natürlich Dir überlassen.
 
hallo mihe7, erstmal Danke für die Hilfe
du schreibst
Wie Du den String aufbaust, bleibt natürlich Dir überlassen
Will ich die die Daten nur ausgeben stimmt das natürlich. Aber wie muss ich den String aufbauen, um die Daten in die Tabelle zu bekommen. Ich habe es mit
@Override
public String toString() {
return getVNR() + ", " + getDatum(); + ...
}
versucht, bekomme aber noch immer die NPE an der gleichen Stelle, siehe oben die markierten Zeile, obwohl die Daten in der Ausgabe nun ok sind. Mann war das in Swing einfach, ich hoffe wir kriegen es noch hin.
 
Die NPE und die „komische“ Ausgabe und die Ausgabe in der Tabelle später sind unabhängig voneinander.

Den Grund für die NPE hat @mihe7 ja Schon genannt, warum das null ist kann man anhand des Codes hier nicht sagen, dafür sieht man zu wenig.
Wenn das behoben ist, sollten die Daten in der Tabelle richtig dargestellt werden.
toString musst du dafür nicht überschreiben, es soll ja nicht das ganze Objekt in String-Form gebracht werden, sondern die einzelnen Werte (die schon Strings sind) in der Tabelle dargestellt werden.
 
hallo, ich bin leider noch nicht am Ziel

nachdem ich die Klasse Vorgang neu erstellt habe wird TableView aktiviert und ist scrollbar,
bleibt aber leer. Die Null-Pointer Exception erscheint nicht mehr. Der Output sagt:
Note: /home/gottfried/DLDB/src/samplefx/ctrl/FXML1Controller.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
javac -Xlint:unchecked FXML1Controller.java
Code:
FXML1Controller.java:81: error: cannot find symbol
    private TableView<Vorgang> TableVorgang;
                      ^
  symbol:   class Vorgang
  location: class FXML1Controller
FXML1Controller.java:111: error: cannot find symbol
    private final ObservableList<Vorgang> daten;
                                 ^
Vorgang.java und FXML1Controller.java sind in der gleichen Package.

Woran kann das liegen? Soll ich die Vorgang.java nochmal posten?
Ich wäre sehr dankbar für einen Tipp.
 
hallo mihe7
javac -cp . -Xlint:unchecked FXML1Controller.java
bringt das Gleiche plus
where E is a type-variable:
E extends Object declared in interface ObservableList
FXML1Controller.java:207: error: cannot find symbol
daten.add(new Vorgang(rs.getString("vnr"), rs.getString("datum"), rs.getString("vanr"), rs.getString("vart"), rs.getString("vbez"), rs.getString("brutto"), rs.getString("zedat")));
markiert ^ ist auch wieder Vorgang ???
 
hallo mihe7, auf deine Frage:
Die Package-Anweisung ist in beiden Klassen wirklich
package samplefx.ctrl;
eindeutig: Ja

javac -cp ../.. -Xlint:unchecked FXML1Controller.java:
Code:
FXML1Controller.java:135: warning: [unchecked] unchecked call to addAll(E...) as a member of the raw type ObservableList
                ComboBoxKunden.getItems().addAll(rs.getString("bem"));
                                                ^
  where E is a type-variable:
    E extends Object declared in interface ObservableList
1 warning

irgendwas scheint an der Deklaration von ObservableList nicht zu stimmen, die lautete zunächst:
private ObservableList daten = FXCollections.observableArrayList();
nach Annahme der von Netbeans eingeblendeten Korrektur jetzt:
private final ObservableList<Vorgang> daten;
public FXML1Controller() {
this.daten = FXCollections.observableArrayList();
hilft uns das weiter?
 
1. Der Fehler ist weg.
2. Das andere ist eine Warnung, der ein Fehler zugrunde liegen kann.

Was zeigt denn die ComboBoxKunden genau an? Objekte vom Typ Kunde oder vom Typ String? Und wie ist die ComboBox deklariert? Außerdem brauchst Du in dem Fall kein addAll, hier reicht ein add.
 
hallo, da habe ich was überlesen,
da ich für die ComboBoxKunden gar keine ObservableList definiert habe glaubte ich die Warnung bezöge sich auf die
Deklaration der ObservableList für TableVorgang. In die ComboBox werden die Daten (String) geladen, in die TableVorgang
nicht. Deshalb nochmal die Ausgabe von javac -cp . -Xlint:unchecked FXML1Controller.java:
Code:
  where E is a type-variable:
    E extends Object declared in interface List
FXML1Controller.java:207: error: cannot find symbol
                daten.add(new Vorgang(rs.getString("vnr"), rs.getString("datum"), rs.getString("vanr"), rs.getString("vart"), 
                                ^
rs.getString("vbez"), rs.getString("brutto"), rs.getString("zedat")));
Ich vermute, dass es an der Deklaration der ObservableList Vorgang oder an Klasse Vorgang selbst liegen könnte, dass die Tabelle
zwar aktiviert wird und scrollbar ist, aber leer bleibt. Deshalb hier nochmal den letzten Stand der Klasse Vorgang:
Java:
package samplefx.ctrl;

/**
 *
 * @author gottfried
 */
public class Vorgang {

    private String vnr;    // Unique id
    private String datum;
    private String vanr;
    private String vart;
    private String vbez;
    private String brutto;
    private String zedat;

    public Vorgang(String vnr, String datum, String vanr, String vart, String vbez, String brutto, String zedat) {
        vnr = vnr;
        datum = datum;
        vanr = vanr;
        vart = vart;
        vbez = vbez;
        brutto = brutto;
        zedat = zedat;
    }

    public String getVnr() {
        return vnr;
    }
    public String getDatum() {
        return datum;
    }
    public String getVanr() {
        return vanr;
    }
    public String getVart() {
        return vart;
    }
    public String getVbez() {
        return vbez;
    }
    public String getBrutto() {
        return brutto;
    }
    public String getZedat() {
        return zedat;
    }
}
Mit der Erstellung einer Klasse für ObservableList habe ich noch keine Erfahrung. Deshalb bin ich dankbar für jeden Hinweis.
Gottfried
 
Ich würd für alle columns noch einen CellFactory implementieren der überprüft ob das Attribute null ist, falls ja nichts oder String.Empty() anzeigen

Hier Tutorial zu CellFactory https://code.makery.ch/blog/javafx-8-tableview-cell-renderer/

Code:
VNR.setCellFactory(column -> {
    return new TableCell<Vorgang , String>() {
        @Override
        protected void updateItem(Vorgang item, boolean empty) {
            super.updateItem(item, empty);

            if (item == null || empty) {
                setText(null);
                setStyle("");
            } else {
               String content = item.getVNR() == null ? "" : item.getVNR();
                setText(content);
            }
        }
    };
});
 
Ziemlich sicher weil VNR da null war, mit der übergebenen ValueFactory hat das nichts zu tun.
Ohh sorry, das war etwas schwammig formuliert. Ich meinte wenn der PropertyValueFactory den Wert von VNR holt und setzt, aber null ist, dann würde es doch genauso auf Exception knallen oder?

Deswegen habe ich im CellFactory die Überprüfung gemacht, wenn null ist leerer String ansonsten VNR

Code:
String content = item.getVNR() == null ? "" : item.getVNR();
setText(content);

im PropertyValueFactory sehe ich keine null Behandlung.
 
hallo, ich möchte an folgendes erinnern

nachdem ich die Klasse Vorgang neu erstellt hatte wird TableView aktiviert und ist scrollbar,
bleibt aber leer. Die Null-Pointer Exception erscheint nicht mehr. Der Output sagt:
Note: /home/gottfried/DLDB/src/samplefx/ctrl/FXML1Controller.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Noch etwas ist mir aufgefallen: Der Scroll-Balken vom TableView verhält sich so, als gäbe es Inhalt, also er erscheint
unterschiedlich lang je nach Anzahl der (nicht sichtbaren) Rows.
???
 
hallo, ich möchte an folgendes erinnern

nachdem ich die Klasse Vorgang neu erstellt hatte wird TableView aktiviert und ist scrollbar,
bleibt aber leer. Die Null-Pointer Exception erscheint nicht mehr. Der Output sagt:

Noch etwas ist mir aufgefallen: Der Scroll-Balken vom TableView verhält sich so, als gäbe es Inhalt, also er erscheint
unterschiedlich lang je nach Anzahl der (nicht sichtbaren) Rows.
???
Wird es blau wenn du die Rows markierst?
 
hallo lam_tr
das hatte ich noch nicht probiert, habs eben getestet: Ja, wird es
und ich kann mit den Pfeiltasten in den unsichtbaren Rows runter und rauf navigieren.
was bedeutet das?
 
hallo mrBrown, Sollte getVnr heißen
das ist inzwischen der Fall, deswegen habe ich doch die letzte Version der Vorgang.java nochmal gepostet,
(Post 11:24) wo genau diese Schreibweise realisiert ist. Das dürfte es nicht sein.
Noch eine Idee?
 
Das Problem ist gelöst:
-die zuerst gepostete Klasse Vorgang.java war richtig angelegt, lediglich die Schreibweise der Getters war falsch.
-entsprechend dem Hinweis: müsste eigentlich getVnr heißen hätte ich lediglich das ändern sollen, nichts weiter
-als ich die richtige Schreibweise verwendete verhinderten leider andere Änderungen an der Klasse Vorgang deren Funktion.

Da das Thema inzwischen ziemlich unübersichtlich geworden ist, stelle ich den funktionierenden Code nochmal ein:

1. Klasse Vorgang:
Java:
package samplefx.ctrl;

/**
 *
 * @author gottfried
 */
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Vorgang {

    private final StringProperty vnr;
    private final StringProperty datum;
    private final StringProperty vanr;
    private final StringProperty vart;
    private final StringProperty vbez;
    private final StringProperty brutto;
    private final StringProperty zedat;

    // Default constructor
    // Constructor with some initial data.
    public Vorgang(String vnr, String datum, String vanr, String vart, String vbez, String brutto, String zedat) {
        this.vnr = new SimpleStringProperty(vnr);
        this.datum = new SimpleStringProperty(datum);
        this.vanr = new SimpleStringProperty(vanr);
        this.vart = new SimpleStringProperty(vart);
        this.vbez = new SimpleStringProperty(vbez);
        this.brutto = new SimpleStringProperty(brutto);
        this.zedat = new SimpleStringProperty(zedat);
    }

    //Getters
    public String getVnr() {
        return vnr.get();
    }
    public String getDatum() {
        return datum.get();
    }
    public String getVanr() {
        return vanr.get();
    }
    public String getVart() {
        return vart.get();
    }
    public String getVbez() {
        return vbez.get();
    }
    public String getBrutto() {
        return brutto.get();
    }
    public String getZedat() {
        return zedat.get();
    }

    //Setters
    public void setVnr(String value) {
        vnr.set(value);
    }
    public void setDatum(String value) {
        datum.set(value);
    }
    public void setVanr(String value) {
        vanr.set(value);
    }
    public void setVart(String value) {
        vart.set(value);
    }
    public void setVbez(String value) {
        vbez.set(value);
    }
    public void setBrutto(String value) {
        brutto.set(value);
    }
    public void setZedat(String value) {
        zedat.set(value);
    }

    //Property values
    public StringProperty vnrProperty() {
        return vnr;
    }
    public StringProperty datumProperty() {
        return datum;
    }
    public StringProperty vanrProperty() {
        return vanr;
    }
    public StringProperty vartProperty() {
        return vart;
    }
    public StringProperty vbezProperty() {
        return vbez;
    }
    public StringProperty bruttoProperty() {
        return brutto;
    }
    public StringProperty zedatProperty() {
        return zedat;
    }
}

2. FXML1Controller.java --> vorgangDat() //Fill TableView
Java:
    @FXML
    public void vorgangDat() {
        String sknr = res.getText();
        int knr = Integer.parseInt(sknr);
        Connection conn = null;
        try {
            Class.forName("org.postgresql.Driver");
            conn = DriverManager.getConnection(jhpn, uname, pw);
            String sSQL = "SELECT vorgang.vnr, vorgang.datum, vorgang.vanr, vorgang.vart, vorgang.vbez, TO_CHAR(brutto.brutto, 'FM999G990D00') AS brutto,"
                    + "vorgang.zedat FROM vorgang vorgang, brutto brutto WHERE vorgang.knr = " + "'" + knr + "'" + " AND vorgang.vnr = brutto.vnr ORDER BY vnr DESC";
            Statement st = conn.createStatement();
            ResultSet rs = st.executeQuery(sSQL);

            while (rs.next()) {
                daten.add(new Vorgang(rs.getString("vnr"), rs.getString("datum"), rs.getString("vanr"), rs.getString("vart"), rs.getString("vbez"), rs.getString("brutto"), rs.getString("zedat")));
            }
            conn.close();

        } catch (ClassNotFoundException | SQLException e) {
            JOptionPane.showMessageDialog(null, "Fehler in vorgangDat(): " + e);
        }

        vnr.setCellValueFactory(new PropertyValueFactory<>("vnr"));
        datum.setCellValueFactory(new PropertyValueFactory<>("datum"));
        vanr.setCellValueFactory(new PropertyValueFactory<>("vanr"));
        vart.setCellValueFactory(new PropertyValueFactory<>("vart"));
        vbez.setCellValueFactory(new PropertyValueFactory<>("vbez"));
        brutto.setCellValueFactory(new PropertyValueFactory<>("brutto"));
        zedat.setCellValueFactory(new PropertyValueFactory<>("zedat"));
        TableVorgang.setItems(daten);
    }

Ich danke allen, die mit ihren Hinweisen zur Lösung beigetragen haben.
Viele Grüße und bleibt gesund!
Gottfried
 

Zurück
Oben