0

I am trying to make something like finance organiser in JavaFX save into database. I would like to retrieve data from database and store it in tableview after pressing button Incomes. At first there is no problem. Stage is changed and tableview will be filled. When I clicked Back button and again Incomes stage has been changed, NullPointerException has ocured in line db.selectIncome() in MainMenuController. I have no idea why it happens. Can anybody help me?

Main class package app;

import java.io.IOException;

import controller.MainMenuController;
import data.DataBase;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import utils.SQLConnection;
import utils.StageLoader;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(this.getClass().getResource("/fxml/MainMenu.fxml"));
        Parent root = loader.load();

        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();

        MainMenuController mainMenuController = loader.getController();
        mainMenuController.setStage(primaryStage);

        SQLConnection.dataBaseConnection();
        DataBase db = new DataBase();
        StageLoader stageLoader = new StageLoader();
        mainMenuController.setDataBase(db);
        mainMenuController.setStageLoader(stageLoader);
    }

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

MainMenuController

    package controller;

import java.io.IOException;

import data.DataBase;
import javafx.fxml.FXML;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import utils.StageLoader;

public class MainMenuController {

    public static final String INCOME_MENU_FXML = "/fxml/IncomeMenu.fxml";
    private Stage stage;
    private StageLoader stageLoader;
    private DataBase db;

    public void setStage(Stage stage) {
        this.stage = stage;
    }

    public void setStageLoader(StageLoader stageLoader) {
        this.stageLoader = stageLoader;
    }

    public void setDataBase(DataBase db) {
        this.db = db;
    }

    @FXML
    public void goToIncomeMenu() throws IOException {
        stageLoader = new StageLoader();
        stageLoader.loadStage(INCOME_MENU_FXML, stage); 
        IncomeMenuController incomeMenuController = stageLoader.getLoader().getController();
        incomeMenuController.setStage(stage);

        db.selectIncome();

        incomeMenuController.getIncomeAmountColumn().setCellValueFactory(new PropertyValueFactory<>("amount"));;
        incomeMenuController.getIncomeCategoryColumn().setCellValueFactory(new PropertyValueFactory<>("category"));
        incomeMenuController.getIncomeDateColumn().setCellValueFactory(new PropertyValueFactory<>("stringDate"));
        incomeMenuController.getIncomeTable().setItems(db.getBudget());


        incomeMenuController.setStageLoader(stageLoader);
        incomeMenuController.setDataBase(db);
    }

    @FXML
    public void exitApplication() {
        System.exit(0);
    }

    @FXML
    public void initialize() {

    }
}

IncomeMenuController

    package controller;

import java.io.IOException;

import data.Budget;
import data.DataBase;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import utils.StageLoader;

public class IncomeMenuController {

    public static final String ADD_INCOME_MENU_FXML = "/fxml/AddIncomeMenu.fxml";
    public static final String MAIN_MENU_FXML = "/fxml/MainMenu.fxml";
    private Stage stage;
    private StageLoader stageLoader;
    private Stage addIncomeStage;
    private DataBase db;

    public void setStageLoader(StageLoader stageLoader) {
        this.stageLoader = stageLoader;
    }

    public void setStage(Stage stage) {
        this.stage = stage;
    }

    public void setDataBase(DataBase db) {
        this.db = db;
    }

    public TableView<Budget> getIncomeTable() {
        return incomeTable;
    }

    public TableColumn<Budget, Double> getIncomeAmountColumn() {
        return incomeAmountColumn;
    }

    public TableColumn<Budget, String> getIncomeCategoryColumn() {
        return incomeCategoryColumn;
    }

    public TableColumn<Budget, String> getIncomeDateColumn() {
        return incomeDateColumn;
    }

    @FXML
    TableView<Budget> incomeTable;

    @FXML
    TableColumn<Budget, Double> incomeAmountColumn;

    @FXML
    TableColumn<Budget, String> incomeCategoryColumn;

    @FXML
    TableColumn<Budget, String> incomeDateColumn;

    @FXML
    public void goToAddIncomeMenu() throws IOException {
        addIncomeStage = new Stage();
        stageLoader.loadStage(ADD_INCOME_MENU_FXML, addIncomeStage);
        AddIncomeMenuController addIncomeMenuController = stageLoader.getLoader().getController();
        addIncomeMenuController.setStage(addIncomeStage);
        addIncomeMenuController.setDataBase(db);
    }

    @FXML
    public void goToMainMenu() throws IOException {
        stageLoader.loadStage(MAIN_MENU_FXML, stage);
        MainMenuController mainMenuController = stageLoader.getLoader().getController();
        mainMenuController.setStage(stage);
        mainMenuController.setStageLoader(stageLoader);
    }

    @FXML
    public void load() {

    }

    @FXML
    public void initialize() {

    }
}

DataBase

    package data;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;


import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.DatePicker;

import javafx.scene.control.TextField;

import utils.SQLConnection;

public class DataBase {

    private String query;
    private PreparedStatement prepStmt;
    private ResultSet result;
    private DateTimeFormatter formatter;
    private DateTimeFormatter tableColumnFormatter;
    private Income income;
    private ObservableList<Budget> budget;

    public ObservableList<Budget> getBudget() {
        return budget;
    }

    public DataBase() {
        formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        tableColumnFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
        income = new Income();
        budget = FXCollections.observableArrayList();
        System.out.println("utworzono obiekt bazy danych");
    }

    public void addIncome(TextField amountField, TextField categoryField, DatePicker dateField) {
        try {
            query = "INSERT INTO Incomes(ID, Amount, Category, Date) VALUES (NULL, ?, ?, ?);";
            prepStmt = SQLConnection.getConnection().prepareStatement(query);
            prepStmt.setDouble(1, Double.parseDouble(amountField.getText()));
            prepStmt.setString(2, categoryField.getText());
            prepStmt.setString(3, dateField.getValue().format(formatter));
            prepStmt.execute();

            prepStmt.close();
        } catch (SQLException e) {
            System.err.println("Błąd podczas dodawania do bazy danych");
            e.printStackTrace();
        }
    }

    public void selectIncome() {
        budget.clear();
        try {
            query = "SELECT * FROM Incomes;";
            prepStmt = SQLConnection.getConnection().prepareStatement(query);
            result = prepStmt.executeQuery();
            while(result.next()) {
                income.setAmount(result.getDouble("amount"));
                income.setCategory(result.getString("category"));
                income.setStringDate(result.getString("date"));
                income.setDate(LocalDate.parse(income.getStringDate()));
                income.setStringDate(income.getDate().format(tableColumnFormatter));
                budget.add(new Income(income.getAmount(), income.getCategory(), income.getStringDate()));
            }
            for(Budget i: budget) {
                System.out.println(i);
            }
            prepStmt.close();
            result.close();
        } catch (SQLException e) {
            System.err.println("Błąd podczas pobierania danych z bazy");
        }

    }
}
Arghavan
  • 1,125
  • 1
  • 11
  • 17
ketrab321
  • 541
  • 2
  • 12
  • 22
  • 1
    Post your stack trace. – SedJ601 Jan 23 '17 at 03:13
  • A `NullPointerException` is not a "SQL Exception". See e.g. here: http://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it –  Jan 23 '17 at 07:33

1 Answers1

0

I suppose this code is executed when you click the back button.

@FXML
public void goToMainMenu() throws IOException {
    stageLoader.loadStage(MAIN_MENU_FXML, stage);
    MainMenuController mainMenuController = stageLoader.getLoader().getController();
    mainMenuController.setStage(stage);
    mainMenuController.setStageLoader(stageLoader);
}

Previously you were passing your database object between the controllers, but this time you didn't.

mainMenuController.setDataBase(db);

And since the db hasn't been initialized, trying to do db.selectIncome() throws a NullPointerException.

As a side note, you should avoid creating new Stage objects whenever possible. In your case it's enough to switch the Scene, or change its contents, because you merely change the look of your window - you don't need another one. Here's one of the ways of doing that.

Community
  • 1
  • 1
Dth
  • 1,916
  • 3
  • 23
  • 34
  • Yes that was the problem. I corrected this method and now it works. Thanks for advice :) One more question. What is more common, asigning methods to buttons in SceneBuilder or by setOnAction/ActionEvent? – ketrab321 Jan 23 '17 at 17:45
  • And by avoiding creating new stage you mean the method goToAddIncomeMenu() in IncomeMenuController? Because by setStage method I pass through controllers always the same stage object? Am I right? – ketrab321 Jan 23 '17 at 18:39
  • You're welcome :) I don't use Scene Builder, but as far as I'm concerned it creates FXML files for which you need to bind controllers. This is a preferred way for me, because it's a handy way to decouple views from the logic... but it's just me, I can't tell which way is more popular or better. Both are viable. – Dth Jan 23 '17 at 19:01
  • And yes, you can reuse your current Stage in other controllers. – Dth Jan 23 '17 at 19:10