Issue
I am experimenting with having different FXML files with different controllers. The AddBalances.fxml has fields to input text and then add to the database when the post button is pressed. It is then ideally supposed to refresh the Tableview to reflect the updated list from the database. This is supposed to be done by the sample.refresh() method. Currently, it updates the database but does not reload the data to refresh.
NB the sample.java controller (where I have nested other controllers), can access methods from the nested controllers just fine. i.e the bottomLabelController.checkConnection() and tableViewController.loadFinanceData() work as expected.
NB When I had one FXML file and one controller the TableView refreshed well.
I hope that is enough info.
Here is the error (caused by controller.refresh() in the AddBalancesController.
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:238)
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:8879)
at javafx.controls/javafx.scene.control.Button.fire(Button.java:200)
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:218)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
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:3851)
at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1200(Scene.java:3579)
at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1849)
at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2588)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:390)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
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.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:830)
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:567)
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:567)
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:1784)
... 47 more
Caused by: java.lang.NullPointerException
at BudgetApp/sample.AddBalancesController.addBalances(AddBalancesController.java:65)
... 58 more
sample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="1280.0"
xmlns="http://javafx.com/javafx/11.0.1"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sample.Controller">
<top>
<fx:include source="menuBar.fxml"/>
</top>
<left>
<VBox prefHeight="375.0" prefWidth="225.0" BorderPane.alignment="CENTER">
<fx:include source="ViewBalances.fxml" fx:id="viewBalances"/>
<fx:include source="AddBalances.fxml" fx:id="addBalances"/>
<BorderPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</BorderPane.margin>
</VBox>
</left>
<center>
<fx:include source="TableView.fxml" fx:id="tableView"/>
</center>
<bottom>
<fx:include source="BottomLabel.fxml" fx:id="bottomLabel"/>
</bottom>
</BorderPane>
Controller.java (Controller for sample.fxml)
package sample;
import datasource.DbConnect;
import datasource.DbModel;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
private DbConnect dbConnect;
DbModel dbModel = new DbModel();
@FXML
private BottomLabelController bottomLabelController;
@FXML
private TableViewController tableViewController;
@FXML
private AddBalancesController addBalancesController;
public void initialize(URL url, ResourceBundle rb){
bottomLabelController.checkConnection();
tableViewController.loadFinanceData();
}
public void refresh(){
tableViewController.loadFinanceData();
}
}
AddBalances.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox prefHeight="403.0" prefWidth="225.0" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="sample.AddBalancesController">
<children>
<Label prefHeight="17.0" prefWidth="220.0" text="Enter Balances">
<VBox.margin>
<Insets left="10.0" right="10.0" top="10.0"/>
</VBox.margin>
</Label>
<DatePicker fx:id="datepicker" prefWidth="220.0" promptText="Date">
<VBox.margin>
<Insets left="5.0" right="5.0" top="5.0"/>
</VBox.margin>
</DatePicker>
<TextField fx:id="cashfield" promptText="Cash">
<VBox.margin>
<Insets left="5.0" right="5.0" top="5.0"/>
</VBox.margin>
</TextField>
<TextField fx:id="mpesafield" layoutX="10.0" layoutY="37.0" promptText="Mpesa">
<VBox.margin>
<Insets left="5.0" right="5.0" top="5.0"/>
</VBox.margin>
</TextField>
<TextField fx:id="bankfield" layoutX="10.0" layoutY="62.0" promptText="Bank">
<VBox.margin>
<Insets left="5.0" right="5.0" top="5.0"/>
</VBox.margin>
</TextField>
<TextField fx:id="stimafield" layoutX="10.0" layoutY="112.0" promptText="Stima Sacco">
<VBox.margin>
<Insets left="5.0" right="5.0" top="5.0"/>
</VBox.margin>
</TextField>
<TextField fx:id="field" layoutX="10.0" layoutY="137.0" promptText="Loan">
<VBox.margin>
<Insets left="5.0" right="5.0" top="5.0"/>
</VBox.margin>
</TextField>
<TextField fx:id="advancefield" layoutX="10.0" layoutY="162.0" promptText="Advance">
<VBox.margin>
<Insets left="5.0" right="5.0" top="5.0"/>
</VBox.margin>
</TextField>
<Button fx:id="postbutton" mnemonicParsing="false" onAction="#addBalances" prefHeight="25.0" prefWidth="229.0"
text="Post">
<VBox.margin>
<Insets left="5.0" right="5.0" top="5.0"/>
</VBox.margin>
</Button>
</children>
</VBox>
AddBalancesController.java (Controller for Addbalances.fxml)
package sample;
import datasource.DbConnect;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TextField;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class AddBalancesController {
@FXML
private Controller controller;
@FXML
private TableViewController tableViewController;
@FXML
private DatePicker datepicker;
@FXML
private TextField cashfield;
@FXML
private TextField mpesafield;
@FXML
private TextField bankfield;
@FXML
private TextField stimafield;
@FXML
private TextField loanfield;
@FXML
private TextField advancefield;
@FXML
private Button postbutton;
@FXML
public void addBalances() {
String sqlinsert = "INSERT INTO data(date, cash, mpesa, bank, stimasacco, loan, advance) VALUES (?, ?, ?, ?, ?, ?, ?)";
try {
Connection connection = DbConnect.getConnection();
PreparedStatement insertPreparedStatement = connection.prepareStatement(sqlinsert);
insertPreparedStatement.setString(1, this.datepicker.getEditor().getText());
insertPreparedStatement.setString(2, this.cashfield.getText());
insertPreparedStatement.setString(3, this.mpesafield.getText());
insertPreparedStatement.setString(4, this.bankfield.getText());
insertPreparedStatement.setString(5, this.stimafield.getText());
insertPreparedStatement.setString(6, this.loanfield.getText());
insertPreparedStatement.setString(7, this.advancefield.getText());
insertPreparedStatement.execute();
this.datepicker.setValue(null);
this.cashfield.setText("");
this.mpesafield.setText("");
this.bankfield.setText("");
this.stimafield.setText("");
this.loanfield.setText("");
this.advancefield.setText("");
controller.refresh();
connection.close();
} catch (SQLException e) {
System.out.println("addBalances() " + e.getMessage());
}
}
}
TableView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.BorderPane?>
<TableView fx:id="financeDataTableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER"
xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="sample.TableViewController">
<columns>
<TableColumn fx:id="date" prefWidth="75.0" text="Date"/>
<TableColumn fx:id="cashinwallet" prefWidth="75.0" text="Cash in Wallet"/>
<TableColumn fx:id="mpesa" prefWidth="75.0" text="Mpesa"/>
<TableColumn fx:id="bank" prefWidth="75.0" text="Bank"/>
<TableColumn fx:id="stimasacco" prefWidth="75.0" text="Stima Sacco"/>
<TableColumn fx:id="loan" prefWidth="93.0" text="Loan"/>
<TableColumn fx:id="advance" prefWidth="93.0" text="Advance"/>
</columns>
</TableView>
TableViewController.java (Controller for Tableview.fxml)
package sample;
import datasource.DbConnect;
import datasource.FinanceData;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
public class TableViewController {
@FXML
private TableColumn<FinanceData, String> date;
@FXML
private TableColumn <FinanceData, String> cashinwallet;
@FXML
private TableColumn <FinanceData, String> mpesa;
@FXML
private TableColumn <FinanceData, String> bank;
@FXML
private TableColumn <FinanceData, String> stimasacco;
@FXML
private TableColumn <FinanceData, String> loan;
@FXML
private TableColumn <FinanceData, String> advance;
@FXML
private TableView<FinanceData> financeDataTableView;
private ObservableList<FinanceData> financeDataObservableList;
@FXML
public void loadFinanceData(){
try {
Connection connection = DbConnect.getConnection();
financeDataObservableList = FXCollections.observableArrayList();
String sqlselect = "SELECT * FROM data";
ResultSet resultSet = connection.createStatement().executeQuery(sqlselect);
while (resultSet.next()) {
financeDataObservableList.add(new FinanceData(resultSet.getString(1),
resultSet.getString(2), resultSet.getString(3),
resultSet.getString(4), resultSet.getString(5),
resultSet.getString(6), resultSet.getString(7)));
}
connection.close();
} catch (SQLException | ParseException e) {
System.out.println("loadFinanceData(): " + e.getMessage());
}
try {
date.setCellValueFactory(new PropertyValueFactory<>("datefield"));
cashinwallet.setCellValueFactory(new PropertyValueFactory<>("cashfield"));
mpesa.setCellValueFactory(new PropertyValueFactory<>("mpesafield"));
bank.setCellValueFactory(new PropertyValueFactory<>("bankfield"));
stimasacco.setCellValueFactory(new PropertyValueFactory<>("stimafield"));
loan.setCellValueFactory(new PropertyValueFactory<>("loanfield"));
advance.setCellValueFactory(new PropertyValueFactory<>("advancefield"));
financeDataTableView.setItems(null);
financeDataTableView.setItems(financeDataObservableList);
} catch (Exception e) {
System.out.println("setcellvalue: " + e.getMessage());
}
}
}````
Solution
As you wrote in your question...
Here is the error (caused by controller.refresh() in the AddBalancesController.
controller.refresh()
is line 65 in file AddBalancesController.java
and, according to the stack trace you posted, this line is throwing NullPointerException
which means that controller
is null.
controller
is a member of class AddBalancesController
and is annotated with @FXML
. Here are the relevant code lines from class AddBalancesController
:
@FXML
private Controller controller;
However AddBalances.fxml
is a nested FXML of sample.fxml
whose "controller" is an instance of class Controller
. Here are the relevant excerpts from file sample.fxml
:
fx:controller="sample.Controller">
.
.
<fx:include source="AddBalances.fxml" fx:id="addBalances"/>
And indeed addBalances
is a member of class Controller
...
@FXML
private AddBalancesController addBalancesController;
Looks to me like you think that the FXML loader will also initialize member controller
in class AddBalancesController
and that is where you are mistaken. In other words, you need to initialize member controller
in class AddBalancesController
, otherwise the member is null and hence the NullPointerException
.
Probably the simplest way to initialize member controller
in class AddBalancesController
would be to add a method to class AddBalancesController
, e.g.
public void setSampleController(Controller controller) {
this.controller = controller;
}
Then in class Controller
, call this method - probably in method initialize()
of that class, as in:
public void initialize(URL url, ResourceBundle rb){
bottomLabelController.checkConnection();
tableViewController.loadFinanceData();
addBalancesController.setSampleController(this); // I added this line.
}
Answered By - Abra