LKC913
LKC913

Reputation: 27

cannot add new row to tableview in javafx

I am creating an app as my homework that takes in customer information and register it using derby database and javafx. Once registered, the new customer will appear on a tableview. I've been trying to add rows to tableview but java.lang.reflect.InvocationTargetException keeps popping up and I have no idea what is going on. I want to retrieve data from my derby database and show it in a tableview in my app. When you open the app, there is an option to add new customer. When clicked on the option to add new customer, a new window will pop up that asks for the new customer's information. I have made a separate FXML file for the new customer entry window and a separate controller for it (neither are shown here). After entering all of the required information on the customer entry window, you click on the submit button and the entered information will be inserted into the derby database, then the NewCustomerEntryController class will call the "handleNewSubmit" method in the "RetailSystemController" class (shown below). Please see my code below and the exception that pops up.

This is the main class that overrides the start method of javafx and I have set up my driver and URL connection for derby in this class

package retailsystem;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.DatabaseMetaData;

public class main extends Application {
   private static Connection conn;

   @Override
   public void start(Stage primaryStage) throws Exception {
      Parent root = FXMLLoader.load(getClass().getResource("retailsystem.fxml"));
      primaryStage.setTitle("Retail System Management");
      primaryStage.setScene(new Scene(root, 600, 400));
      setUpDB();
      primaryStage.show();
   }

   private void setUpDB() throws Exception {
      String driver = "org.apache.derby.jdbc.EmbeddedDriver";
      String dbName = "RetailSystemDB";
      String connectionURL = "jdbc:derby:" + dbName + ";create=true";
      String createCustomerString = "CREATE TABLE Customer (FirstName VARCHAR (50) NOT "
              + "NULL, LastName VARCHAR(50) NOT NULL, StreetAddress VARCHAR(50) "
              + "NOT NULL, City VARCHAR(20) NOT NULL, State VARCHAR(12) NOT NULL, "
              + "Zipcode VARCHAR(5), Gender VARCHAR(6))";

      Class.forName(driver);
      conn = DriverManager.getConnection(connectionURL);
      Statement stmt = conn.createStatement();
      DatabaseMetaData dbm = conn.getMetaData();
      ResultSet checkCustomer = dbm.getTables(null, null, "CUSTOMER", null);
      if (!checkCustomer.next()) {
         stmt.executeUpdate(createCustomerString);
      }
      stmt.close();
   }
   public static Connection getConn() {
      return conn;
   }

   public static void main(String[] args) {
      launch(args);
   }

}

The code below is the controller for the new customer entry window:

package retailsystem;

import java.io.File;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.Scanner;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.stage.Stage;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javafx.scene.control.TableView;

public class CustomerInformationController implements Initializable {

   @FXML
   private TextField fEntry;
   @FXML
   private TextField lEntry;
   @FXML
   private TextField aEntry;
   @FXML
   private TextField cEntry;
   @FXML
   private ComboBox<String> sEntry;
   @FXML
   private RadioButton male;
   @FXML
   private RadioButton female;
   @FXML
   private TextField zEntry;
   @FXML
   private Button submitBTN;
   @FXML
   private Button cancelBtn;
   private static person customer;

   ToggleGroup group = new ToggleGroup();

   /**
    * Initializes the controller class.
    */
   @Override
   public void initialize(URL url, ResourceBundle rb) {
      // TODO
      String[] stateList = getStateList();
      for (int i = 0; i < stateList.length; i++) {
         sEntry.getItems().add(stateList[i]);
      }
      sEntry.setValue(stateList[0]);

      male.setToggleGroup(group);
      female.setToggleGroup(group);

   }


   @FXML
   private void handleSubmit(ActionEvent event) throws Exception {
      RadioButton selectedBtn = (RadioButton) group.getSelectedToggle();
      customer = new person(fEntry.getText(), lEntry.getText(),
              aEntry.getText(), cEntry.getText(), sEntry.getValue(), zEntry.getText(),
              selectedBtn.getText());
      Connection conn = main.getConn();
      PreparedStatement psInsert = conn.prepareStatement("INSERT INTO CUSTOMER "
              + "(FirstName, LastName, StreetAddress, City, State, Zipcode, "
              + "Gender) VALUES(?,?,?,?,?,?,?)");
      psInsert.setString(1, customer.getFirstName());
      psInsert.setString(2, customer.getLastName());
      psInsert.setString(3, customer.getAddress());
      psInsert.setString(4, customer.getCity());
      psInsert.setString(5, customer.getState());
      psInsert.setString(6, customer.getZipcode());
      psInsert.setString(7, customer.getGender());
      psInsert.executeUpdate();
      RetailSystemController retailcontroller = new RetailSystemController();
      retailcontroller.handleNewSubmit();
      psInsert.close();
   }

}

The code below is my fxml file

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

<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="retailsystem.RetailSystemController">
   <center>
      <TableView fx:id="tView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
        <columns>
          <TableColumn fx:id="fName" prefWidth="90.0" text="First Name" />
          <TableColumn fx:id="lName" prefWidth="90.0" text="Last Name" />
            <TableColumn fx:id="address" prefWidth="147.0" text="Street Address" />
            <TableColumn fx:id="city" prefWidth="87.0" text="City" />
            <TableColumn fx:id="state" prefWidth="64.0" text="State" />
            <TableColumn fx:id="zip" prefWidth="62.0" text="Zip Code" />
            <TableColumn fx:id="gender" prefWidth="70.0" text="Gender" />
        </columns>
      </TableView>
   </center>
   <top>
      <MenuBar BorderPane.alignment="CENTER">
        <menus>
          <Menu mnemonicParsing="false" text="Customer">
            <items>
              <MenuItem mnemonicParsing="false" onAction="#handleCustomerAddNew" text="Add New" />
                  <MenuItem mnemonicParsing="false" onAction="#handleCustomerListAll" text="List All" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="Employee">
            <items>
              <MenuItem mnemonicParsing="false" onAction="#handleEmployeeAddNew" text="Add New " />
                  <MenuItem mnemonicParsing="false" onAction="#handleEmployeeListAll" text="List All" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="Merchandise">
            <items>
                  <MenuItem mnemonicParsing="false" onAction="#handleMerchandiseAddNew" text="Add New" />
              <MenuItem mnemonicParsing="false" onAction="#handleMerchandiseListAll" text="List All" />
            </items>
          </Menu>
        </menus>
      </MenuBar>
   </top>
</BorderPane>

The code below is the controller class for the above noted fxml file, there are quite a few handlers in this class but I am just focussed on the "handleCustomerAddnew" and "handleNewSubmit" for now as I haven't written much code for the others. The problem happens in the handleNewSubmit method in this class:

package retailsystem;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class RetailSystemController implements Initializable {

   @FXML
   private TableColumn fName;
   @FXML
   private TableColumn lName;
   @FXML
   private TableColumn address;
   @FXML
   private TableColumn city;
   @FXML
   private TableColumn state;
   @FXML
   private TableColumn zip;
   @FXML
   private TableColumn gender;
   @FXML
   private TableView<person> tView;

   public void initialize(URL url, ResourceBundle rb) {
      //ToDo
      fName.setCellValueFactory(new PropertyValueFactory<person, String>("firstName"));
      lName.setCellValueFactory(new PropertyValueFactory<person, String>("lastName"));
      address.setCellValueFactory(new PropertyValueFactory<person, String>("address"));
      city.setCellValueFactory(new PropertyValueFactory<person, String>("city"));
      state.setCellValueFactory(new PropertyValueFactory<person, String>("state"));
      zip.setCellValueFactory(new PropertyValueFactory<person, String>("Zipcode"));
      gender.setCellValueFactory(new PropertyValueFactory<person, String>("gender"));
   }

   @FXML
   private void handleCustomerAddNew(ActionEvent event) {
      Parent root;
      try {
         root = FXMLLoader.load(getClass().getResource("customerinformation.fxml"));
         Stage stage = new Stage();
         stage.setTitle("Customer Information");
         stage.setScene(new Scene(root, 400, 340));
         stage.show();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   public void handleNewSubmit() {
      System.out.println("beginning of handleNewSubmit");
      ArrayList<person> registeredPersons=new ArrayList<person>();
      Connection conn = main.getConn();
      String query = "SELECT * FROM CUSTOMER";
      try{
      PreparedStatement stmt = conn.prepareStatement(query);
      ResultSet resultset = stmt.executeQuery();
      while(resultset.next()){
         System.out.println(resultset.getString("FirstName")+" "+resultset.getString("LastName"));
         registeredPersons.add(new person(resultset.getString("FirstName"),resultset.getString("LastName"), resultset.getString("StreetAddress"),
         resultset.getString("City"),resultset.getString("State"),resultset.getString("Zipcode"),resultset.getString("Gender")));
      }}
      catch (Exception e){
         e.printStackTrace();
      }
      ObservableList<person> items = FXCollections.observableArrayList(registeredPersons);
      System.out.println("before adding to tView");
      tView.setItems(items);
      System.out.println("after adding to tView");
   }

}

Errors:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8885)
    at javafx.controls/javafx.scene.control.Button.fire(Button.java:203)
    at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
    at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3890)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1885)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2618)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:447)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:412)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
    at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
    at javafx.graphics/com.sun.glass.ui.mac.MacView.notifyMouse(MacView.java:127)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:273)
    at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782)
    ... 44 more
Caused by: java.lang.NullPointerException
    at retailsystem.RetailSystemController.handleNewSubmit(RetailSystemController.java:127)
    at retailsystem.CustomerInformationController.handleSubmit(CustomerInformationController.java:102)
    ... 55 more

Upvotes: 0

Views: 203

Answers (1)

James_D
James_D

Reputation: 209225

The NullPointerException is occurring on the line

tView.setItems(items);

tViewis a TableView defined in your FXML file; it will be initialized in the controller when the FXML file is loaded. The reason it is null when you call handleNewSubmit() is that you are not calling that method on the controller, but on an arbitrary object you created (of the same class as the controller):

private void handleSubmit(ActionEvent event) throws Exception {
    // ...
    RetailSystemController retailcontroller = new RetailSystemController();
    retailcontroller.handleNewSubmit();
    // ...
}

The best way to solve this is simply to pass the table's items list to the CustomerInformationController when you show the customerinformation.fxml file. That way the CustomerInformationController can update the table data directly. Right now you are trying to load all the data from the database again when you add a new item: that seems pretty unnecessary; all you need to do is add the new item to the list, if the database update is successful.

So you can do:

public class RetailSystemController implements Initializable {

    // ...

   @FXML
   private void handleCustomerAddNew(ActionEvent event) {
      Parent root;
      try {
         FXMLLoader loader = new FXMLLoader(getClass().getResource("customerinformation.fxml"));
         root = loader.load();
         CustomerInformationController ciController = loader.getController();
         ciController.setDataList(tbl.getItems());
         Stage stage = new Stage();
         stage.setTitle("Customer Information");
         stage.setScene(new Scene(root, 400, 340));
         stage.show();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   public void handleNewSubmit() {
       // you probably don't need this method any more
   }

   // ...
}

And then:

public class CustomerInformationController implements Initializable {

    // ...

    private ObservableList<person> dataList ;

    public void setDataList(ObservableList<person> dataList) {
        this.dataList = dataList();
    }

    // ...

    @FXML
    private void handleSubmit(ActionEvent event) throws Exception {
        RadioButton selectedBtn = (RadioButton) group.getSelectedToggle();
        customer = new person(fEntry.getText(), lEntry.getText(),
              aEntry.getText(), cEntry.getText(), sEntry.getValue(), zEntry.getText(),
              selectedBtn.getText());
        Connection conn = main.getConn();
        PreparedStatement psInsert = conn.prepareStatement("INSERT INTO CUSTOMER "
              + "(FirstName, LastName, StreetAddress, City, State, Zipcode, "
              + "Gender) VALUES(?,?,?,?,?,?,?)");
        psInsert.setString(1, customer.getFirstName());
        psInsert.setString(2, customer.getLastName());
        psInsert.setString(3, customer.getAddress());
        psInsert.setString(4, customer.getCity());
        psInsert.setString(5, customer.getState());
        psInsert.setString(6, customer.getZipcode());
        psInsert.setString(7, customer.getGender());
        psInsert.executeUpdate();
        // RetailSystemController retailcontroller = new RetailSystemController();
        // retailcontroller.handleNewSubmit();
        psInsert.close();

        dataList.add(customer) ;
    }   

    // ...
}

Obviously, in a real application, you would move the database code to a separate class, and handle exceptions appropriately, etc.

If you really do need to reload the entire list from the database again, after adding a new entry, then you can do that in the handleSubmit() method, and just use dataList.setAll(...) to replace the existing items, etc. But again, since you know what changes you just made to the database, you can just make the identical change to the table's backing list.

Upvotes: 1

Related Questions