-1

im trying to populate data from DB(using MySQL) into TableView, the problem i get is, whenever there is a NULL data in some column there will be an error NullPointerException, before using FX im using SWING component and there is no problem with my code, its kinda hard for me to porting it. im using NetBeans IDE and Gluon Scene Builder. the NULL data on DB are used purposedly for some conditional situation to create view in my DB. i think i know my problem are located in populating data in my HandlerComponent.java but i dont know what to do.

this is the code TableController.java

public class TableController implements Initializable {
    Connection CONN;
    Database DB = new Database();
    HandlerComponent COMP_HANDLER;
    @FXML
    private TableView tblView;
    @FXML
    private Button btnLoad;
    @FXML
    private TextField txTable;
    @FXML
    private Label lblKolom1;
    @FXML
    private TextField txKolom1;
    @FXML
    private Label lblKolom2;
    @FXML
    private TextField txKolom2;
    @FXML
    private Button btnTambah;
    @FXML
    private Button btnHapus;
    @FXML
    private Button btnReset;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        try {
            COMP_HANDLER = new HandlerComponent();
            tblView = COMP_HANDLER.iniTable(tblView, "SELECT * FROM items", DB);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }    

    @FXML
    public void loadTable() {
        try {
            tblView.getColumns().clear();
            tblView = COMP_HANDLER.iniTable(tblView, "SELECT * FROM "+txTable.getText(), DB);
            tblView.refresh();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @FXML
    public void getDataTable(javafx.scene.input.MouseEvent e) {
        if(e.getClickCount() == 2 && !e.isConsumed()) {
            System.out.println("double Clicked");
        }
    }
}

Table.fxml

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

<?import java.net.URL?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>

<AnchorPane id="AnchorPane" prefHeight="513.0" prefWidth="600.0" style="-fx-background-color: #123456;" styleClass="mainFxmlClass" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller.TableController">
    <stylesheets>
        <URL value="@designskena.css" />
    </stylesheets>
   <children>
      <VBox prefHeight="497.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <children>
            <FlowPane prefHeight="79.0" prefWidth="600.0">
               <children>
                  <Button fx:id="btnLoad" mnemonicParsing="false" onAction="#loadTable" prefHeight="58.0" prefWidth="92.0" text="Load Data">
                     <FlowPane.margin>
                        <Insets />
                     </FlowPane.margin>
                  </Button>
                  <TextField fx:id="txTable" prefHeight="58.0" prefWidth="431.0" text="Table">
                     <FlowPane.margin>
                        <Insets left="25.0" />
                     </FlowPane.margin>
                  </TextField>
               </children>
               <VBox.margin>
                  <Insets />
               </VBox.margin>
               <padding>
                  <Insets bottom="25.0" left="25.0" right="25.0" top="25.0" />
               </padding></FlowPane>
            <TableView fx:id="tblView" onMouseClicked="#getDataTable" prefHeight="258.0" prefWidth="600.0" style="-fx-background-color: black;" stylesheets="@designskena.css">              
               <VBox.margin>
                  <Insets left="25.0" right="25.0" />
               </VBox.margin>
            </TableView>
            <Pane prefHeight="165.0" prefWidth="600.0">
               <children>
                  <Label fx:id="lblKolom1" layoutX="25.0" layoutY="14.0" prefHeight="17.0" prefWidth="58.0" text="Kolom 1" />
                  <TextField fx:id="txKolom1" layoutX="25.0" layoutY="38.0" />
                  <Label fx:id="lblKolom2" layoutX="206.0" layoutY="14.0" prefHeight="17.0" prefWidth="58.0" text="Kolom 2" />
                  <TextField fx:id="txKolom2" layoutX="206.0" layoutY="38.0" />
                  <Button fx:id="btnTambah" layoutX="25.0" layoutY="86.0" mnemonicParsing="false" text="Tambah" />
                  <Button fx:id="btnHapus" layoutX="99.0" layoutY="86.0" mnemonicParsing="false" text="Hapus" />
                  <Button fx:id="btnReset" layoutX="163.0" layoutY="86.0" mnemonicParsing="false" text="Reset" />
               </children>
               <padding>
                  <Insets bottom="25.0" left="25.0" right="25.0" top="25.0" />
               </padding>
            </Pane>
         </children>
      </VBox>
   </children>
</AnchorPane>

and this is the HandlerComponent.java

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.ResultSet;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.util.Callback;
/**
 * Class untuk membantu konfigurasi setiap COMPONENT JavaFX
 * yang terdapat pada java dengan koneksi Database.
 * @author RHS
 * @version 0.1a
 */
public class HandlerComponent {
    protected Connection H_CONN;
    protected ResultSet H_RESULTSET;
    // Properti yang dibutuhkan untuk Handler Tabel
    public String[] COLUMN_TYPE, COLUMN_NAME;
    public Object[] OBJ_DATA;

    /**
     * <b>Constructor</b> untuk kelas <i>HandlerComponent</i>, akan memanggil metode untuk 
     * koneksi driver database pada kelas database, dengan menyimpan 
     * koneksi pada variable <i><b>H_CONN</b></i>
     * @throws SQLException 
     */
    public HandlerComponent() throws SQLException {
        H_CONN = new Database().inisiasiDB();
    }

    public TableView iniTable(TableView TABLE, String SQL, Database DBClass) {
        ObservableList<ObservableList> DATA = FXCollections.observableArrayList();
        try 
        {
            //H_CONN = new DBClass().inisiasiDB();
            H_RESULTSET = DBClass.getSQL(SQL);
            int count = H_RESULTSET.getMetaData().getColumnCount();
            COLUMN_NAME = new String[count];
            COLUMN_TYPE = new String[count];
            OBJ_DATA = new Object[count];

            /**
             * ======================================
             * Pembuatan Kolom tabel secara dinamis *
             * ======================================
             */
            for(int x=0; x<count; x++) {
                final int j = x;
                // pengecekan nama-kolom pada database untuk digenerate pada 
                // tabel yang dituju serta pengecekan tipedata pada tiap kolom
                // yang kemudian akan disimpan pada variable String array
                // COLUMN_NAME[] dan COLUMN_TYPE
                COLUMN_NAME[x] = H_RESULTSET.getMetaData().getColumnName(x+1);
                COLUMN_TYPE[x] = H_RESULTSET.getMetaData().getColumnTypeName(x+1);
                System.out.print(COLUMN_TYPE[x]);

                // pembuatan kolom tabel berdasarkan COLUMN_NAME
                // yang telah dibuat
                TableColumn col = new TableColumn(COLUMN_NAME[x]);

                // this is where the NullPointerException are triggered
                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());
                    }
                });

                // Menambahkan kolom yang sudah di generate sebelumnya
                // ke dalam tabel tabel tujuan
                TABLE.getColumns().addAll(col);
                System.out.println(" | Column [" + x + "] "+col);
            }

            /** 
             * ============================
             * Penambahan Data pada tabel *
             * ============================
             */
            while(H_RESULTSET.next()) {
                // pembuatan tipe data ObservableList untuk digunakan 
                // dalam menampung data yang akan dimasukan dalam tabel perbarisnya
                ObservableList<Object> row = FXCollections.observableArrayList();
                for (int x = 1; x <= count; x++) {
                    // pengecekan data pada setiap kolom yang ada di database
                    // pada baris yang sama untuk disesuaikan tipedata yg nantinya
                    // akan disimpan dalam tipedata Object
                    switch(COLUMN_TYPE[x-1]) {
                        case "INT" :
                            OBJ_DATA[x-1] = H_RESULTSET.getInt(x);
                            break;
                        case "DOUBLE" :
                            OBJ_DATA[x-1] = H_RESULTSET.getDouble(x);
                            break;
                        case "FLOAT" :
                            OBJ_DATA[x-1] = H_RESULTSET.getFloat(x);
                            break;
                        case "VARCHAR" :
                            OBJ_DATA[x-1] = H_RESULTSET.getString(x);
                            break;
                        case "CHAR" :
                            OBJ_DATA[x-1] = H_RESULTSET.getString(x);
                            break;
                        case "TEXT" :
                            OBJ_DATA[x-1] = H_RESULTSET.getString(x);
                            break;
                        case "DATE" :
                            OBJ_DATA[x-1] = H_RESULTSET.getString(x);
                            break;                        
                        default :
                            OBJ_DATA[x-1] = "Unkown Datatype (HandlerComponent.Java)";
                            break;
                    }
                    //
                    row.add(OBJ_DATA[x-1]);
                }
                System.out.println("Row [1] added " + row);
                DATA.add(row);
            }

            TABLE.setItems(DATA);
        } catch (SQLException ex) {
            ex.printStackTrace();
            System.out.println("Error on Building Data");
        }
        return TABLE;
    }
}

this is Database.java

import com.mysql.jdbc.Driver;
import java.sql.Connection; import java.sql.DriverManager;
import java.sql.ResultSet; import java.sql.PreparedStatement;
import java.sql.Statement; import java.sql.SQLException;
import javax.swing.JOptionPane;

public class Database {
    private static Connection config;
    private Connection CONFIG;
    private static Driver DRIVER_MANAGER;
    private static String PARAMETER_CONN;
    public static String DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASS, STMT, LOG;
    public ResultSet RES;
    public Statement STM;
    public PreparedStatement PREP;

    public Database() {
        Database.setDB_HOST("localhost");
        Database.setDB_NAME("rab");
        Database.setDB_USER("root");
        Database.setDB_PASS("");
        Database.setDB_PORT("3306");
    }

    public Database(String db_name, String user, String pass) {
        Database.DB_NAME = db_name; Database.DB_USER = user;
        Database.DB_PASS = pass;
    }

    public static void setDB_HOST(String DB_HOST) {
        Database.DB_HOST = DB_HOST;
    }

    public static void setDB_PORT(String DB_PORT) {
        Database.DB_PORT = DB_PORT;
    }

    public static void setDB_NAME(String DB_NAME) {
        Database.DB_NAME = DB_NAME;
    }

    public static void setDB_USER(String DB_USER) {
        Database.DB_USER = DB_USER;
    }

    public static void setDB_PASS(String DB_PASS) {
        Database.DB_PASS = DB_PASS;
    }

    public static void setConfig(Connection config) {
        Database.config = config;
    }

    public void setConfigs(Connection config) {
        CONFIG = config;
    }

    public static void setSTMT(String STMT) {
        Database.STMT = STMT;
    }

    public Connection getConfig() {
        return config;
    }

    protected String getLog() {
        return LOG;
    }
    protected void setLog(String Description)  {
        Database.LOG = ("\nQuery Performed :"+Description+" =========================================\n"
                + Database.STMT
                + "\n========================================================\n");
    }
    protected void setLog(String Description, String SQL)  {
        Database.LOG = ("\nQuery Performed :"+Description+" =========================================\n"
                + SQL
                + "\n========================================================\n");
    }
    /**
     * Query Select untuk SQL 
     * dengan syarat harus memberikan query pada property STMT
     * e.g. "<i>SELECT * FROM <b>namaTable</b></i>"<br> atau "<i>SELECT * FROM <b>namaTable</b> WHERE <b>kolom</b> = "<u>kondisi</u>"</i>"
     * @return ResultSet executeQuery();
     * @throws SQLException 
     * @see setSTMT();
     */
    public ResultSet getSQL() throws SQLException {
        checkConnection();
        STM = Database.config.createStatement();
        LOG = Database.STMT;
        RES = STM.executeQuery(Database.STMT);
        setLog("Getting Data");
        System.out.println(getLog());
        return RES;
    }

    /**
     * Query Select untuk SQL dengan Custom Parameter
     * @param Query String SQL
     * e.g. "<i>SELECT * FROM <b>namaTable</b></i>"<br> atau "<i>SELECT * FROM <b>namaTable</b> WHERE <b>kolom</b> = "<u>kondisi</u>"</i>"
     * @return ResultSet executeQuery();
     * @throws SQLException 
     */ 
    public ResultSet getSQL(String Query) throws SQLException {
        //checkConnection();
        STM = Database.config.createStatement();;
        setLog("Getting Data", Query);
        System.out.println(getLog());
        return (RES = STM.executeQuery(Query));
    }

    public int insertSQL(Object[] DATA, String TABLE, String PRIMARY_KEY) throws SQLException {
        String[] SQL = Database.STMT.split("\\s");
        int result = 0;
        // pengecualian table cek
        if( (searchIndexDB(DATA[0].toString(),PRIMARY_KEY,TABLE) == -1)
            || (SQL[0].equalsIgnoreCase("UPDATE"))
            || (TABLE.equalsIgnoreCase("orderdetail"))
            || (TABLE.equalsIgnoreCase("recipe"))) {
            //System.out.println("Data ["+DATA[0]+"] belum ada di DB");
            PREP = Database.config.prepareStatement(STMT);
            String typedata = "";
            int pointer = 0;
            if(SQL[0].equalsIgnoreCase("UPDATE")){
                setLog("Update Data");
                result = 2;
            }
            else {
                setLog("Insert Data");
                result = 1;
            }
            System.out.println(getLog());
            for (Object temp : DATA) {
                typedata = temp.getClass().getSimpleName();
                System.out.println(temp.getClass().getSimpleName()+" : "+DATA[pointer]);
                switch (typedata) {
                    case "String" :
                        PREP.setString(pointer+1, DATA[pointer].toString());
                        break;
                    case "Integer" :
                        PREP.setInt(pointer+1, Integer.parseInt(DATA[pointer].toString()));
                        break;
                    case "Float" :
                        PREP.setFloat(pointer+1, Float.parseFloat(DATA[pointer].toString()));
                        break;
                    case "Double" :
                        PREP.setDouble(pointer+1,Double.parseDouble(DATA[pointer].toString()));
                        break;
                }
                pointer++;
            }
            PREP.executeUpdate();
            return result;
        }else{  
            System.out.println("Data ["+DATA[0]+"] Sudah ada di DB\n"
                            + "Silahkan Gunakan fitur Update");
            JOptionPane.showMessageDialog(null, "Ooppss!. Data tersebut sudah ada\n"
                    + "Silahkan gunakan Fitur UPDATE","Warning",1);
            return result;
        }
    }

    public int deleteSQL(String KEY) throws SQLException{
        checkConnection();
        PREP = Database.config.prepareStatement(STMT);
        PREP.setString(1, KEY);
        setLog("Delete Data");
        System.out.println(getLog());
        return (PREP.executeUpdate());
    }
    public int deleteSQL(String KEY, String SQL) throws SQLException{
        checkConnection();
        setSTMT(SQL);
        setLog("Delete Data");
        System.out.println(getLog());
        PREP = Database.config.prepareStatement(STMT);
        PREP.setString(1, KEY);
        return (PREP.executeUpdate());
    }

    /**
     * Metode untuk mencari adanya data dalam DB
     * e.g. searchIndexDB(<u>textField.getText(), "kolom", "table"</u>) atau<br>
     * searchIndexDB(<u>txID.getText(), "id", "user"</u>)
     * @param SEARCH data yang ingin dicari
     * @param FIELD kolom field yang dicari
     * @param TABLE table yang dicari
     * @return mengembalikan tipedata Integer, bernilai -1 jika tidak ditemukan,<br>dan akan mengembalikan nilai index jika ditemukan dalam DB;
     * @throws SQLException 
     */ 
    public int searchIndexDB(String SEARCH, String FIELD, String TABLE) throws SQLException {
        int index = -1, i = 0;
        //Database.setSTMT("SELECT "+FIELD+" FROM "+TABLE+" ORDER BY "+FIELD+" ASC");
        RES = getSQL("SELECT "+FIELD+" FROM "+TABLE+" ORDER BY "+FIELD+" ASC");
        while (RES.next()) {
            if(true == RES.getString(1).equalsIgnoreCase(SEARCH)) {                
                index = i;
                break;
            }
            i++;
        }
        setLog("Search Index");
        System.out.println(getLog());
        return index;
    }

    public static Connection inisiasiDB() throws SQLException {
        if ((Database.DB_NAME == null) || (Database.DB_USER == null) || (Database.DB_PASS == null) || (Database.DB_PORT == null) || (Database.DB_HOST == null)) {
            JOptionPane.showMessageDialog(null,"Database Belum disetting");
             if (Database.DB_NAME == null) {
                 String DBname = JOptionPane.showInputDialog("Nama Database");
                 Database.DB_NAME = DBname;
             }
             if (Database.DB_USER == null) setDB_USER(JOptionPane.showInputDialog("User"));
             if (Database.DB_PASS == null) Database.DB_PASS = JOptionPane.showInputDialog("Password");
             if (Database.DB_PORT == null) Database.DB_PORT = JOptionPane.showInputDialog("Port");
             if (Database.DB_HOST == null) Database.DB_HOST = JOptionPane.showInputDialog("Host");
             Database.PARAMETER_CONN = "jdbc:mysql://"+Database.DB_HOST+":"+Database.DB_PORT+"/"+Database.DB_NAME;
        }
        try {
            Database.DRIVER_MANAGER = new com.mysql.jdbc.Driver();
            Database.PARAMETER_CONN = "jdbc:mysql://"+Database.DB_HOST+":"+Database.DB_PORT+"/"+Database.DB_NAME;
            DriverManager.registerDriver(DRIVER_MANAGER);
            setConfig(DriverManager.getConnection(Database.PARAMETER_CONN,Database.DB_USER, Database.DB_PASS));
            System.out.println("Koneksi Sukses ========================\n DB_NAME : "+DB_NAME);
        } catch (SQLException ex) {
            ex.printStackTrace();
            JOptionPane.showMessageDialog(null,"Driver untuk Koneksi database tidak ada\nSilahkan nyalakan services MySQL pada XAMPP");
        }
        return config;
    }

    public Connection inisiasiDBnoStatic() throws SQLException {
        if ((Database.DB_NAME == null) || (Database.DB_USER == null) || (Database.DB_PASS == null) || (Database.DB_PORT == null) || (Database.DB_HOST == null)) {
            JOptionPane.showMessageDialog(null,"Database Belum disetting");
             if (Database.DB_NAME == null) {
                 String DBname = JOptionPane.showInputDialog("Nama Database");
                 Database.DB_NAME = DBname;
             }
             if (Database.DB_USER == null) setDB_USER(JOptionPane.showInputDialog("User"));
             if (Database.DB_PASS == null) Database.DB_PASS = JOptionPane.showInputDialog("Password");
             if (Database.DB_PORT == null) Database.DB_PORT = JOptionPane.showInputDialog("Port");
             if (Database.DB_HOST == null) Database.DB_HOST = JOptionPane.showInputDialog("Host");
             Database.PARAMETER_CONN = "jdbc:mysql://"+Database.DB_HOST+":"+Database.DB_PORT+"/"+Database.DB_NAME;
        }
        try {
            Database.DRIVER_MANAGER = new com.mysql.jdbc.Driver();
            Database.PARAMETER_CONN = "jdbc:mysql://"+Database.DB_HOST+":"+Database.DB_PORT+"/"+Database.DB_NAME;
            DriverManager.registerDriver(DRIVER_MANAGER);
            setConfigs(DriverManager.getConnection(Database.PARAMETER_CONN,Database.DB_USER, Database.DB_PASS));
            System.out.println("Koneksi Sukses ========================\n DB_NAME : "+DB_NAME);
        } catch (SQLException ex) {
            ex.printStackTrace();
            JOptionPane.showMessageDialog(null,"Driver untuk Koneksi database tidak ada\nSilahkan nyalakan services MySQL pada XAMPP");
        }
        return CONFIG;
    }

    private void checkConnection() throws SQLException{
        if(getConfig() == null) {
            setConfig(inisiasiDB());
        }
    }

    public int lastInventID(){
        int result= -1;
        try {
            checkConnection(); 
            setSTMT("SELECT no FROM inventory ORDER BY no ASC");
            RES = getSQL(); RES.last();
            result = RES.getInt(1);
            result++;
        } catch (SQLException ex){
            ex.printStackTrace();
        }
        return result;
    }

    public String generateOrderID() {
        try {
            checkConnection();
            String temp;
            RES = getSQL("SELECT orderID FROM orderhead ORDER BY orderID");
            LOG = STMT;
            RES.last();
            temp = RES.getString("orderID");
            int increase = Integer.parseInt(temp);
            int digit = String.valueOf(increase).length();
            switch (digit) {
                case 1: {increase++;temp = "000"+increase;}
                    break;
                case 2: {increase++;temp = "00"+increase;}
                    break;
                case 3: {increase++;temp = "0"+increase;}
                    break;
                default: {increase++;temp = ""+increase;}
                    break;
            }
            RES.close();
            return temp;            
        } catch(SQLException ex) {
            ex.printStackTrace();
        }
        return "";
    }
}

Exception Stack

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at Model.HandlerComponent$1.call(HandlerComponent.java:67)
    at Model.HandlerComponent$1.call(HandlerComponent.java:65)
RedHatSource
  • 9
  • 1
  • 7

1 Answers1

1

You just need to check for null before attempting to call toString():

col.setCellValueFactory(new Callback<CellDataFeatures<ObservableList, String>, ObservableValue<String>>() {
    public ObservableValue<String> call(CellDataFeatures<ObservableList, String> param) {
        Object value = param.getValue().get(j) ;
        return new SimpleStringProperty(value == null ? "" : value.toString());
    }
});
James_D
  • 201,275
  • 16
  • 291
  • 322
  • thx for the solution capt, this work like a charm. why this way can work? what about the solution given by serge not working. – RedHatSource Jun 01 '20 at 16:40
  • @RedHatSource The `param` passed to the `Callback` is an instance of [`TableColumn.CellDataFeatures`](https://openjfx.io/javadoc/14/javafx.controls/javafx/scene/control/TableColumn.CellDataFeatures.html) and is never `null`. The value returned by `getValue()` is the actual item in the `TableView`'s items list, i.e. the `ObservableList` you created as `row`, which may contain `null` at index `j` if you put `null` in there from the database. So that's the value you need to check is not null. – James_D Jun 01 '20 at 16:44