Files and persistence
IA tooling, not an exam topic. These are JavaFX and standard-library patterns to adapt for your IA. The text file-handling here is also the B2.5 file-processing material you meet in the syllabus, so it doubles as revision. Your IA must be your own work.
An app that forgets everything when it closes is rarely enough for an IA. This page lets the user choose a file, saves and loads plain text, and shows the step up to a small database when a list outgrows a text file.
Let the user choose a file
What this does: opens the system’s file dialog so the user picks where to read from or save to, and hands you back the chosen File.
When you would use it in your IA: a Save As or Open menu item; letting the client choose where their data or report goes rather than fixing one hidden path.
The pattern:
Imports:
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
Open dialog:
@FXML
private void handleOpen(ActionEvent event) {
FileChooser chooser = new FileChooser();
chooser.setTitle("Open a file");
chooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter("Text files", "*.txt"));
Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
File file = chooser.showOpenDialog(stage);
if (file == null) {
return; // the user pressed Cancel
}
loadFrom(file); // your own action
}
For a save dialog, the only change is the method you call:
File file = chooser.showSaveDialog(stage);
Make it your own:
- Change the title and the extension filter to suit your data (for example
"*.csv"for a CSV export). - Replace
loadFrom(file)or the save call with your own read or write.
Watch out for:
- Both dialogs return
nullwhen the user cancels, so always check fornullbefore using the file. showSaveDialogreturns the chosen path but does not create the file. You still write to it yourself with the next recipe.- The dialog needs the window it belongs to. Getting the
Stagefrom the event source, as above, is the standard way to supply it.
Mix with: Save and load text, Export your data to a CSV file, Let the user pick an image and show it.
Save and load text
What this does: writes text to a file and reads it back, each wrapped in try/catch so a missing or unreadable file does not crash the app.
When you would use it in your IA: saving the app’s state between runs, keeping a simple log, or storing a list of records as lines of text. This is the B2.5 file-processing skill applied to a real app.
The pattern:
Imports:
import java.io.File;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
Writing:
private void saveText(File file, String content) {
try (PrintWriter out = new PrintWriter(file)) {
out.println(content);
} catch (IOException e) {
// show the user that saving failed
}
}
Reading:
private String loadText(File file) {
StringBuilder builder = new StringBuilder();
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
String line;
while ((line = in.readLine()) != null) {
builder.append(line).append("\n");
}
} catch (IOException e) {
// show the user that loading failed
}
return builder.toString();
}
Make it your own:
- Pass in the
Filefrom the file-chooser recipe, or a fixednew File("data.txt")for a hidden save file. - To save a list, loop over it and call
out.println(...)once per line. To load it back, read line by line and rebuild each object.
Watch out for:
- The
try (...)form here opens the file and closes it for you when the block ends, even if an error happens. Always close files; this form does it without a separateclose()line. readLine()returnsnullat the end of the file, which is how thewhileloop knows to stop.- Reading a file that does not exist throws an
IOException. Thecatchhandles it, so decide there whether to start with empty data or warn the user.
Mix with: Let the user choose a file, Fill a table from your data, Step up to a database when a list outgrows a file.
Step up to a database when a list outgrows a file
What this does: stores your data in a small SQLite database file instead of a text file, using the SQL you already know. SQLite is a single file on disk with no server to run, so it is the low-friction way to move up from text.
When you would use it in your IA: when the data is more than a simple list. Several related tables, searching and sorting that would be clumsy to do by hand on text, or records you need to update in place. For a plain list that just needs saving, the text-file recipe above is less setup.
The one setup step: add the sqlite-jdbc driver jar to your BlueJ project libraries (in BlueJ: Tools, then Preferences, then Libraries, then add the jar; or drop it in a +libs folder in the project). Default JavaFX does not include a database driver, so this jar is the single third-party dependency in this whole cookbook.
The pattern:
Imports:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
Connect and create a table:
private void setUpDatabase() {
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:app.db")) {
Statement stmt = conn.createStatement();
stmt.execute("CREATE TABLE IF NOT EXISTS item (name TEXT, quantity INTEGER)");
} catch (SQLException e) {
// show the user that the database could not be opened
}
}
Insert a row from user input, using a PreparedStatement:
private void addItem(String name, int quantity) {
String sql = "INSERT INTO item (name, quantity) VALUES (?, ?)";
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:app.db");
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, name); // fills the first ?
ps.setInt(2, quantity); // fills the second ?
ps.executeUpdate();
} catch (SQLException e) {
// handle the error
}
}
Read rows back:
private void readItems() {
String sql = "SELECT name, quantity FROM item";
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:app.db");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
String name = rs.getString("name");
int quantity = rs.getInt("quantity");
// build an Item from name and quantity and add it to your list
}
} catch (SQLException e) {
// handle the error
}
}
Make it your own:
- Change
app.dbto your own database file name. It is created automatically the first time you connect. - Change the table name, columns, and SQL to match your data. The SQL is the same
CREATE,INSERT, andSELECTyou already know. - Read each row in the
while (rs.next())loop and turn it into one of your model objects.
Watch out for:
- Use a
PreparedStatementwith?placeholders for any value that came from the user, as shown for the insert. It keeps the query from breaking on odd characters like apostrophes, and it is the safer habit to build early. - Every database call can throw
SQLException, so keep them insidetry/catch. - This is the only recipe that needs a third-party jar. If the data really is just a list, the text-file recipe avoids that setup entirely.
Mix with: Save and load text, Fill a table from your data, Filter the table as the user types.