Build a Library Book Manager with JDBC
Connect Java to MySQL and build a Swing GUI app with full CRUD operations.
Introduction
30 Second Summary
Think about how a library keeps track of thousands of books. Every time someone borrows, returns, or adds a new title, that information needs to be stored somewhere reliable and searchable.
In this project, you will build a Library Book Manager desktop application that connects Java to a MySQL database. You will design the interface visually using NetBeans GUI Builder and write code that adds, searches, and deletes book records.
What You'll Build
A Library Book Manager desktop application with a visual form for entering book details (title, author, ISBN, year) and a table that displays all books from a MySQL database.
By the end of this project, you'll have:
- A MySQL database with a books table for storing book records.
- A Swing JFrame form designed entirely with NetBeans drag-and-drop (no hand-written layout code).
- JDBC code that performs INSERT, SELECT, and DELETE operations using PreparedStatement.
- Secret Mission: Add an Update feature so you can select a book from the table, edit its details, and save changes back to the database.
Are there any prerequisites?
This project assumes you have core Java knowledge (classes, methods, try-catch). You also need Apache NetBeans, a JDK, and MySQL Server already installed and running on your machine.
Set Up the Database and Project
Your Library Book Manager needs a place to store book records permanently. That place is a MySQL database with a table designed to hold titles, authors, ISBNs, and publication years.
In this step, you will create the database, set up a NetBeans project, and connect the JDBC driver so your Java code can talk to MySQL.
In this step, get ready to:
- Create a MySQL database and books table.
- Set up a NetBeans Java project.
- Add the MySQL JDBC driver to your project's classpath.
Create the MySQL database and books table
Before writing any Java code, you need a database ready to receive book records. You will create a database called library_db and a books table inside it.
- Press the Windows key (Windows) or Cmd+Space (macOS) to open your search bar.
- Type MySQL and open the MySQL Command Line Client.
- Enter your MySQL root password when prompted.
What is the MySQL Command Line Client?
The MySQL Command Line Client is a text-based tool where you type SQL commands directly to your MySQL Server. Think of it as a conversation window between you and your database.
- Create the database and table by running these SQL commands:
CREATE DATABASE library_db;
USE library_db;
CREATE TABLE books (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200) NOT NULL,
author VARCHAR(100) NOT NULL,
isbn VARCHAR(20),
year_published INT
);
What does this SQL do?
- CREATE DATABASE library_db creates a new database to hold all your library data.
- USE library_db tells MySQL that all following commands apply to this database.
- CREATE TABLE books defines a table with five columns. The id column auto-increments so each book gets a unique number automatically.
- NOT NULL on title and author means every book must have these two fields filled in.
- Verify the table was created by running:
SHOW TABLES;
You should see books listed in the output.
Seeing 'Access denied' or can't connect?
- Double-check the root password you entered. It is the password you set when you first installed MySQL Server.
- Make sure MySQL Server is running. On Windows, search for Services in the Start menu and confirm MySQL shows a status of Running.
Help me troubleshoot my MySQL connection
Create a new Java Application project in NetBeans
Now you need a Java project to write your application code in. You will create a project called LibrarySystem using the Java with Ant project type, which includes the GUI Builder.
- In NetBeans, click File in the menu bar.
- Click New Project.
- In the Categories list, select Java with Ant.
- In the Projects list, select Java Application.
- Click Next.
- In the Project Name field, type LibrarySystem.
- Uncheck Create Main Class.
- Click Finish.
Why uncheck Create Main Class?
You will create a JFrame Form in the next step instead of a plain Java class. The JFrame Form has its own main method built in, so you do not need a separate one.
NetBeans creates the project and shows it in the Projects panel on the left.
Don't see Java with Ant in the list?
Make sure you have the Java SE features activated in NetBeans. Go to Tools > Plugins > Installed and confirm Java SE is listed and active.
Help me find the Java with Ant project type in NetBeans
Add the MySQL Connector/J JAR to the project
Java cannot talk to MySQL on its own. It needs a driver called MySQL Connector/J that translates JDBC calls into MySQL network messages. You already have the mysql-connector-j-9.7.0.jar file from the ZIP you extracted earlier.
- In the Projects panel, right-click your project name (LibrarySystem).
- Click Properties.
- In the left panel, click Libraries.
- Make sure the Classpath tab is selected.
- Click the + button (Add JAR/Folder).
- Navigate to the folder where you extracted the ZIP and select mysql-connector-j-9.7.0.jar.
- Click Open.
- Click OK to close the Properties dialog.
Expand the Libraries folder under your project in the Projects panel. You should see mysql-connector-j-9.7.0.jar listed there.
What is MySQL Connector/J?
MySQL Connector/J is the official JDBC driver for MySQL. JDBC drivers let Java use a standard API (Connection, Statement, ResultSet) to talk to any database. The driver handles the translation to MySQL's specific protocol.
JAR not showing in the Libraries folder?
- Make sure you selected the file ending in .jar, not the folder or the ZIP itself.
- Confirm you clicked OK on the Properties dialog to save the change. Clicking the X to close discards it.
Help me add the MySQL Connector JAR to my NetBeans project
Your database is ready and your project has everything it needs to connect to MySQL. Next up, you will design the GUI with drag-and-drop components in NetBeans GUI Builder.
Design the GUI with NetBeans GUI Builder
Your database is ready and waiting for data. Now you need a way for users to interact with it.
In this step, you will design the entire user interface using the NetBeans GUI Builder. This tool lets you drag and drop Swing components onto a form without writing any layout code by hand.
In this step, get ready to:
- Create a JFrame Form for your application window.
- Add labels, text fields, and buttons for book input.
- Add a JTable to display book records from the database.
Create a JFrame Form
A JFrame is the main window container for a Swing desktop application. NetBeans provides a JFrame Form template that opens directly in the visual GUI Builder.
- In the Projects panel on the left, expand your LibrarySystem project.
- Right-click the librarysystem source package.
- Select New > JFrame Form.
- In the Class Name field, type LibraryManager.
- Click Finish.
The GUI Builder (Design view) opens with an empty form. You will see the Palette panel on the right side with Swing components you can drag onto the form.
What is the GUI Builder?
The GUI Builder (also called Matisse) is NetBeans' visual form editor. It generates all the layout code for you inside a protected initComponents() method. You never need to write layout code manually.
The Design Area is where you arrange components visually. The Source button above the design area lets you view the generated Java code at any time.
Don't see the Palette panel?
If the Palette is not visible on the right side, open it from the menu: Window > IDE Tools > Palette. You can also try pressing Ctrl+Shift+8.
Help me find the Palette panel in NetBeans
Drag and drop the input components
Each book has four fields: title, author, ISBN, and year published. You will add labels, text fields, buttons, and a search input to your form.
Add the labels
- In the Palette panel, find Swing Controls.
- Drag a JLabel onto the form.
- Double-click the label text on the form and change it to Title:.
- Drag three more JLabels onto the form, placing them below the first.
- Set their text to Author:, ISBN:, and Year: respectively.
- Drag one more JLabel and set its text to Search:.
Add the text fields
- Drag a JTextField from the Palette and place it next to the Title: label.
- Right-click the JTextField you just placed.
- Select Change Variable Name from the context menu.
- Type txtTitle and click OK.
- Drag a second JTextField next to the Author: label.
- Right-click it, select Change Variable Name, and type txtAuthor.
- Drag a third JTextField next to the ISBN: label.
- Right-click it, select Change Variable Name, and type txtISBN.
- Drag a fourth JTextField next to the Year: label.
- Right-click it, select Change Variable Name, and type txtYear.
- Drag one more JTextField near the Search: label.
- Right-click it, select Change Variable Name, and type txtSearch.
Why rename the variables?
NetBeans gives components default names like jTextField1 and jTextField2. These names appear in the generated Java code.
Renaming them to descriptive names like txtTitle makes your code readable when you write database logic in the next step.
Add the buttons
- Drag a JButton from the Palette onto the form below the text fields.
- Right-click the button and select Edit Text.
- Change the text to Add Book.
- Right-click the same button and select Change Variable Name.
- Type btnAdd and click OK.
- Drag a second JButton onto the form.
- Set its text to Search and its variable name to btnSearch.
- Drag a third JButton onto the form.
- Set its text to Delete and its variable name to btnDelete.
- Drag a fourth JButton onto the form.
- Set its text to Clear and its variable name to btnClear.
Editing button text quickly
You can also edit a button's display text by clicking it once, pausing, then clicking again. The text becomes editable inline without opening a context menu.
Add the JTable for displaying books
The JTable component displays data in rows and columns. It is the perfect fit for showing book records from your database.
- In the Palette panel under Swing Controls, find Table (JTable).
- Drag it onto the lower portion of the form, below your buttons.
- Resize it to fill most of the lower area of the form.
- Right-click the JTable and select Change Variable Name.
- Type tblBooks and click OK.
What is the JScrollPane?
When you drag a JTable onto the form, NetBeans automatically wraps it inside a JScrollPane. This means if your table has more rows than can fit on screen, scrollbars appear automatically. You do not need to add the scroll pane yourself.
Your form design is complete. You should see labels on the left, text fields on the right, buttons in the middle area, and the table taking up the bottom section.
- Click the Source tab above the design area to switch to Source view.
- Scroll to the bottom of the file.
- Confirm you see variable declarations for txtTitle, txtAuthor, txtISBN, txtYear, txtSearch, btnAdd, btnSearch, btnDelete, btnClear, and tblBooks at the bottom of the file.
Missing a variable declaration?
If a variable name is missing or still shows the default name (like jTextField1), switch back to Design view. Right-click the component and select Change Variable Name to correct it.
Help me fix my variable names in NetBeans
✔️ Awesome, I've got everything!
Great. Make sure you have saved the file by pressing Ctrl+S.
ⓧ I'd like to double check the full code
Your Source view should show these auto-generated variable declarations at the bottom of the file. The exact layout code inside initComponents() will differ based on where you placed your components, but the variables below must exist.
package librarysystem;
public class LibraryManager extends javax.swing.JFrame {
public LibraryManager() {
initComponents();
}
// NOTE: initComponents() is auto-generated by NetBeans GUI Builder
// Variables declaration - auto-generated by GUI Builder
private javax.swing.JButton btnAdd;
private javax.swing.JButton btnClear;
private javax.swing.JButton btnDelete;
private javax.swing.JButton btnSearch;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JLabel lblAuthor;
private javax.swing.JLabel lblISBN;
private javax.swing.JLabel lblSearch;
private javax.swing.JLabel lblTitle;
private javax.swing.JLabel lblYear;
private javax.swing.JTable tblBooks;
private javax.swing.JTextField txtAuthor;
private javax.swing.JTextField txtISBN;
private javax.swing.JTextField txtSearch;
private javax.swing.JTextField txtTitle;
private javax.swing.JTextField txtYear;
// End of variables declaration
}
Your entire GUI is designed without writing a single line of layout code. Next up, you will connect this form to your MySQL database and make the buttons actually do something.
Connect to MySQL and Add Books
Your GUI form is designed and ready to go. Now it's time to make it actually do something by connecting it to the MySQL database you set up earlier.
JDBC is Java's standard API for talking to databases. In this step, you'll write Java methods that open a connection to your library_db database, read book records, and insert new ones.
In this step, get ready to:
- Write a database connection method using JDBC.
- Add methods to load books from MySQL and insert new records.
- Wire button click events to your new methods.
Write the database connection method and set up the table model
Before your app can read or write books, it needs a way to connect to MySQL. You'll set up the imports, a reusable connection method, and configure the table to display the right columns.
- In NetBeans, make sure you're in the Source view (click the Source tab above the editor area).
- Add these imports at the very top of your file, directly below the package librarysystem; line:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.swing.JOptionPane;
import javax.swing.table.DefaultTableModel;
What are these imports for?
- Connection, DriverManager, PreparedStatement, ResultSet, Statement, and SQLException are the core JDBC classes for database communication.
- JOptionPane provides pop-up dialog boxes for messages and errors.
- DefaultTableModel lets you control what columns and rows appear in the JTable.
- Find the existing constructor in your file. It currently looks like this:
public LibraryManager() {
initComponents();
}
- Replace the entire constructor with this updated version that sets up the table columns and loads books on startup:
public LibraryManager() {
initComponents();
DefaultTableModel model = new DefaultTableModel(
new String[]{"ID", "Title", "Author", "ISBN", "Year"}, 0
);
tblBooks.setModel(model);
loadBooks();
}
- Directly after the closing } of the constructor, add the getConnection() method:
private Connection getConnection() throws SQLException {
String url = "jdbc:mysql://localhost:3306/library_db";
String user = "root";
String password = "your-mysql-password-here";
return DriverManager.getConnection(url, user, password);
}
What does this code do?
- The constructor now creates a DefaultTableModel with five column headers (ID, Title, Author, ISBN, Year) and applies it to tblBooks. The 0 means the table starts with zero rows.
- loadBooks() is called at the end of the constructor so the table fills with data as soon as the app opens.
- getConnection() builds a JDBC URL pointing to your library_db database on localhost:3306 and returns a live Connection object. Every other method in the class will call this helper.
Replace your-mysql-password-here with the actual root password you use to log into MySQL.
You won't see a change yet. The next chunk adds the methods the constructor references.
Write the loadBooks and addBook methods
Your app needs methods that execute SQL statements and feed the results into the GUI. You'll write three methods: one to load all books into the table, one to insert a new book, and a helper to clear the form fields.
- Add the following three methods inside the LibraryManager class, below the getConnection() method you just wrote:
private void loadBooks() {
DefaultTableModel model = (DefaultTableModel) tblBooks.getModel();
model.setRowCount(0);
try (Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM books")) {
while (rs.next()) {
model.addRow(new Object[]{
rs.getInt("id"),
rs.getString("title"),
rs.getString("author"),
rs.getString("isbn"),
rs.getInt("year_published")
});
}
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error loading books: " + ex.getMessage());
}
}
private void addBook() {
String title = txtTitle.getText();
String author = txtAuthor.getText();
String isbn = txtISBN.getText();
String yearText = txtYear.getText();
if (title.isEmpty() || author.isEmpty()) {
JOptionPane.showMessageDialog(this, "Title and Author are required.");
return;
}
String sql = "INSERT INTO books (title, author, isbn, year_published) VALUES (?, ?, ?, ?)";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, title);
pstmt.setString(2, author);
pstmt.setString(3, isbn);
pstmt.setInt(4, yearText.isEmpty() ? 0 : Integer.parseInt(yearText));
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Book added successfully!");
clearFields();
loadBooks();
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error adding book: " + ex.getMessage());
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(this, "Year must be a number.");
}
}
private void clearFields() {
txtTitle.setText("");
txtAuthor.setText("");
txtISBN.setText("");
txtYear.setText("");
txtSearch.setText("");
}
What does this code do?
loadBooks() clears the table with setRowCount(0), then runs a SELECT * FROM books query. It loops through every row in the ResultSet and adds each book as a new row in the table model.
addBook() reads values from the text fields, validates that title and author aren't empty, then uses a PreparedStatement to safely insert the data. The ? placeholders prevent SQL injection by separating the SQL structure from user input.
clearFields() resets all the text fields to empty strings so the form is ready for the next entry.
You still won't see a change if you run the app. The buttons aren't connected to these methods yet.
Wire up the Add and Clear button events
NetBeans generates event handler methods automatically when you double-click a button in the GUI Builder. You just need to add your method call inside the generated handler.
- Switch to Design view (click the Design tab above the editor area).
- Double-click the Add Book button on your form.
NetBeans creates a btnAddActionPerformed method and drops your cursor inside it. This method runs every time the button is clicked.
- Inside the generated btnAddActionPerformed method, type this line:
addBook();
- Switch back to Design view.
- Double-click the Clear button.
- Inside the generated btnClearActionPerformed method, type this line:
clearFields();
What did double-clicking do?
Double-clicking a button in the GUI Builder tells NetBeans to generate an event listener and a handler method automatically. The generated code wires the button's click event to your handler. All you need to do is fill in the body.
- Save your file with Ctrl+S.
- Run the project by pressing F6 (or right-click your project name in the Projects panel and select Run).
- The Library Manager form appears. Type a book title and author into the text fields, then click Add Book.
- A dialog saying "Book added successfully!" appears, and the book shows up in the table below.
Seeing a connection error?
- Make sure your MySQL Server is running. If you closed it, restart it from the Windows Services panel or by running net start mysql in an Administrator command prompt.
- Double-check that the password in your getConnection() method matches the root password you set when installing MySQL.
- Confirm the database exists by opening MySQL Command Line Client and running SHOW DATABASES; to verify library_db is listed.
Why can't my app connect to MySQL?
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
package librarysystem;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.swing.JOptionPane;
import javax.swing.table.DefaultTableModel;
public class LibraryManager extends javax.swing.JFrame {
public LibraryManager() {
initComponents();
DefaultTableModel model = new DefaultTableModel(
new String[]{"ID", "Title", "Author", "ISBN", "Year"}, 0
);
tblBooks.setModel(model);
loadBooks();
}
private Connection getConnection() throws SQLException {
String url = "jdbc:mysql://localhost:3306/library_db";
String user = "root";
String password = "your-mysql-password-here";
return DriverManager.getConnection(url, user, password);
}
private void loadBooks() {
DefaultTableModel model = (DefaultTableModel) tblBooks.getModel();
model.setRowCount(0);
try (Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM books")) {
while (rs.next()) {
model.addRow(new Object[]{
rs.getInt("id"),
rs.getString("title"),
rs.getString("author"),
rs.getString("isbn"),
rs.getInt("year_published")
});
}
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error loading books: " + ex.getMessage());
}
}
private void addBook() {
String title = txtTitle.getText();
String author = txtAuthor.getText();
String isbn = txtISBN.getText();
String yearText = txtYear.getText();
if (title.isEmpty() || author.isEmpty()) {
JOptionPane.showMessageDialog(this, "Title and Author are required.");
return;
}
String sql = "INSERT INTO books (title, author, isbn, year_published) VALUES (?, ?, ?, ?)";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, title);
pstmt.setString(2, author);
pstmt.setString(3, isbn);
pstmt.setInt(4, yearText.isEmpty() ? 0 : Integer.parseInt(yearText));
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Book added successfully!");
clearFields();
loadBooks();
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error adding book: " + ex.getMessage());
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(this, "Year must be a number.");
}
}
private void clearFields() {
txtTitle.setText("");
txtAuthor.setText("");
txtISBN.setText("");
txtYear.setText("");
txtSearch.setText("");
}
// NOTE: initComponents() is auto-generated by NetBeans GUI Builder
private void btnAddActionPerformed(java.awt.event.ActionEvent evt) {
addBook();
}
private void btnClearActionPerformed(java.awt.event.ActionEvent evt) {
clearFields();
}
// Variables declaration - auto-generated by GUI Builder
private javax.swing.JButton btnAdd;
private javax.swing.JButton btnClear;
private javax.swing.JButton btnDelete;
private javax.swing.JButton btnSearch;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JLabel lblAuthor;
private javax.swing.JLabel lblISBN;
private javax.swing.JLabel lblSearch;
private javax.swing.JLabel lblTitle;
private javax.swing.JLabel lblYear;
private javax.swing.JTable tblBooks;
private javax.swing.JTextField txtAuthor;
private javax.swing.JTextField txtISBN;
private javax.swing.JTextField txtSearch;
private javax.swing.JTextField txtTitle;
private javax.swing.JTextField txtYear;
// End of variables declaration
}
Your app can now talk to MySQL and add new books. Next up, you'll implement search and delete so users can find specific books and remove records they no longer need.
Implement Search and Delete
Your Library Book Manager can now add books and display them in the table. But a real library system needs more than just adding records.
In this step, you will add search and delete functionality. Search lets you filter books by title or author using SQL's LIKE keyword, and delete lets you remove a selected row from both the table and the database.
In this step, get ready to:
- Write a search method that filters books by title or author.
- Write a delete method that removes the selected book from the database.
- Wire both buttons to their methods using NetBeans event handlers.
Write the search method
When the learner types a keyword and clicks Search, you want the table to show only books whose title or author contains that keyword. SQL's LIKE operator with % wildcards makes this possible.
- Switch to Source view in LibraryManager.java.
- Add the following searchBooks() method inside the class, below the addBook() method:
private void searchBooks() {
String keyword = txtSearch.getText();
if (keyword.isEmpty()) {
loadBooks();
return;
}
DefaultTableModel model = (DefaultTableModel) tblBooks.getModel();
model.setRowCount(0);
String sql = "SELECT * FROM books WHERE title LIKE ? OR author LIKE ?";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "%" + keyword + "%");
pstmt.setString(2, "%" + keyword + "%");
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
model.addRow(new Object[]{
rs.getInt("id"),
rs.getString("title"),
rs.getString("author"),
rs.getString("isbn"),
rs.getInt("year_published")
});
}
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error searching: " + ex.getMessage());
}
}
What does this code do?
If the search field is empty, the method reloads all books instead of filtering. This means clicking Search with no keyword acts as a "show all" reset.
The LIKE operator combined with % wildcards matches any row where the keyword appears anywhere in the title or author. A PreparedStatement safely inserts the keyword so there is no risk of SQL injection.
- Save LibraryManager.java.
Seeing a compile error?
Make sure the method is placed inside the class body (between the opening { of the class and its final closing }). Also check that you have not accidentally placed it inside another method.
Help me fix a compile error in my searchBooks method.
Write the delete method
Deleting a book requires two pieces of information: which row is selected in the table, and what the database ID of that row is. You will also ask the user to confirm before actually running the DELETE statement.
- Add the following deleteBook() method below searchBooks():
private void deleteBook() {
int selectedRow = tblBooks.getSelectedRow();
if (selectedRow == -1) {
JOptionPane.showMessageDialog(this, "Please select a book to delete.");
return;
}
int id = (int) tblBooks.getValueAt(selectedRow, 0);
int confirm = JOptionPane.showConfirmDialog(this,
"Delete this book?", "Confirm", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
String sql = "DELETE FROM books WHERE id = ?";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Book deleted.");
loadBooks();
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error deleting: " + ex.getMessage());
}
}
}
What does this code do?
The method calls tblBooks.getSelectedRow() to find which row the user clicked. A return value of -1 means nothing is selected, so you show a warning.
Column 0 of the table holds the database ID (the first column you set up in the DefaultTableModel). The method grabs that ID and passes it to a DELETE query with a PreparedStatement. After deleting, it calls loadBooks() to refresh the table.
- Save LibraryManager.java.
Getting a ClassCastException?
If you see an error at the line (int) tblBooks.getValueAt(selectedRow, 0), make sure your loadBooks() method uses rs.getInt("id") (not rs.getString) when adding the ID column to the table model.
Help me debug a ClassCastException in my deleteBook method.
Wire up the Search and Delete button events
The methods are ready but the buttons do not call them yet. You will use the same double-click technique from the previous step to generate event handlers in NetBeans.
- Switch to Design view by clicking the Design tab above the editor area.
- Double-click the Search button on your form.
- NetBeans creates a btnSearchActionPerformed method and takes you to Source view. Inside that method, type:
searchBooks();
- Switch back to Design view.
- Double-click the Delete button on your form.
- Inside the generated btnDeleteActionPerformed method, type:
deleteBook();
Why does double-clicking generate event code?
The NetBeans GUI Builder automatically writes the event-listener boilerplate for you. When you double-click a button, it creates a method named after the button's variable name plus ActionPerformed and registers it as the click handler. You only need to fill in the body.
- Save LibraryManager.java.
- Run the project by pressing F6 (or right-click the project and select Run).
- Add a few books using different titles and authors.
- Type a partial title into the search field and click Search. Only matching books should appear in the table.
- Click a row in the table, then click Delete. Confirm the dialog. The book disappears from the table.
Search button not filtering results?
Make sure you are typing in the search text field (txtSearch), not one of the book input fields. Also confirm that btnSearchActionPerformed calls searchBooks() and not loadBooks().
Help me figure out why my search is not filtering books.
✔️ Awesome, I've got everything!
Great. Double check you have saved your file before moving on.
ⓧ I'd like to double check the full code
Here is the complete LibraryManager.java at this point:
package librarysystem;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.swing.JOptionPane;
import javax.swing.table.DefaultTableModel;
public class LibraryManager extends javax.swing.JFrame {
public LibraryManager() {
initComponents();
DefaultTableModel model = new DefaultTableModel(
new String[]{"ID", "Title", "Author", "ISBN", "Year"}, 0
);
tblBooks.setModel(model);
loadBooks();
}
private Connection getConnection() throws SQLException {
String url = "jdbc:mysql://localhost:3306/library_db";
String user = "root";
String password = "your-mysql-password-here";
return DriverManager.getConnection(url, user, password);
}
private void loadBooks() {
DefaultTableModel model = (DefaultTableModel) tblBooks.getModel();
model.setRowCount(0);
try (Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM books")) {
while (rs.next()) {
model.addRow(new Object[]{
rs.getInt("id"),
rs.getString("title"),
rs.getString("author"),
rs.getString("isbn"),
rs.getInt("year_published")
});
}
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error loading books: " + ex.getMessage());
}
}
private void addBook() {
String title = txtTitle.getText();
String author = txtAuthor.getText();
String isbn = txtISBN.getText();
String yearText = txtYear.getText();
if (title.isEmpty() || author.isEmpty()) {
JOptionPane.showMessageDialog(this, "Title and Author are required.");
return;
}
String sql = "INSERT INTO books (title, author, isbn, year_published) VALUES (?, ?, ?, ?)";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, title);
pstmt.setString(2, author);
pstmt.setString(3, isbn);
pstmt.setInt(4, yearText.isEmpty() ? 0 : Integer.parseInt(yearText));
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Book added successfully!");
clearFields();
loadBooks();
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error adding book: " + ex.getMessage());
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(this, "Year must be a number.");
}
}
private void searchBooks() {
String keyword = txtSearch.getText();
if (keyword.isEmpty()) {
loadBooks();
return;
}
DefaultTableModel model = (DefaultTableModel) tblBooks.getModel();
model.setRowCount(0);
String sql = "SELECT * FROM books WHERE title LIKE ? OR author LIKE ?";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "%" + keyword + "%");
pstmt.setString(2, "%" + keyword + "%");
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
model.addRow(new Object[]{
rs.getInt("id"),
rs.getString("title"),
rs.getString("author"),
rs.getString("isbn"),
rs.getInt("year_published")
});
}
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error searching: " + ex.getMessage());
}
}
private void deleteBook() {
int selectedRow = tblBooks.getSelectedRow();
if (selectedRow == -1) {
JOptionPane.showMessageDialog(this, "Please select a book to delete.");
return;
}
int id = (int) tblBooks.getValueAt(selectedRow, 0);
int confirm = JOptionPane.showConfirmDialog(this,
"Delete this book?", "Confirm", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
String sql = "DELETE FROM books WHERE id = ?";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Book deleted.");
loadBooks();
} catch (SQLException ex) {
JOptionPane.showMessageDialog(this, "Error deleting: " + ex.getMessage());
}
}
}
private void clearFields() {
txtTitle.setText("");
txtAuthor.setText("");
txtISBN.setText("");
txtYear.setText("");
txtSearch.setText("");
}
// NOTE: initComponents() is auto-generated by NetBeans GUI Builder
private void btnAddActionPerformed(java.awt.event.ActionEvent evt) {
addBook();
}
private void btnSearchActionPerformed(java.awt.event.ActionEvent evt) {
searchBooks();
}
private void btnDeleteActionPerformed(java.awt.event.ActionEvent evt) {
deleteBook();
}
private void btnClearActionPerformed(java.awt.event.ActionEvent evt) {
clearFields();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new LibraryManager().setVisible(true);
}
});
}
// Variables declaration - auto-generated by GUI Builder
private javax.swing.JButton btnAdd;
private javax.swing.JButton btnClear;
private javax.swing.JButton btnDelete;
private javax.swing.JButton btnSearch;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JLabel lblAuthor;
private javax.swing.JLabel lblISBN;
private javax.swing.JLabel lblSearch;
private javax.swing.JLabel lblTitle;
private javax.swing.JLabel lblYear;
private javax.swing.JTable tblBooks;
private javax.swing.JTextField txtAuthor;
private javax.swing.JTextField txtISBN;
private javax.swing.JTextField txtSearch;
private javax.swing.JTextField txtTitle;
private javax.swing.JTextField txtYear;
// End of variables declaration
}
Your Library Book Manager now supports adding, searching, and deleting books. That covers three of the four core database operations. In the Secret Mission, you will tackle the fourth: updating an existing record.
Secret mission
Implement Update Book
Your Library Book Manager can add, search, and delete books. But what happens when a book's title has a typo, or you need to correct an author name? Right now, you'd have to delete the record and re-add it. Add an Update feature so you can select a book from the table, edit its details in the form fields, and save changes back to the database.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- MySQL database library_db with the books table.
- NetBeans project LibrarySystem (local project files).
- MySQL Connector/J 9.7.0 JAR file (downloaded during setup).
Keep everything running
No action needed. Choose this if you are still actively building or want to extend the project further.
- Your library_db database stays on your local MySQL Server and uses minimal disk space.
- The LibrarySystem NetBeans project stays in your NetBeans projects folder.
- The MySQL Connector/J JAR remains wherever you extracted it.
Pause - I'll come back to this later
Stop MySQL Server to free up memory. Your database files remain on disk and will be available when you start the server again.
- Open the Windows Start menu and search for Services.
- Find MySQL80 (or your MySQL service name) in the list.
- Right-click it and select Stop.
- When you are ready to resume, right-click the same service and select Start.
Delete - I don't want to use this again
Remove all project resources and start fresh. This deletes the database, project files, and the connector JAR.
- Drop the MySQL database by opening the MySQL Command Line Client and running:
DROP DATABASE library_db;
- Delete the NetBeans project by right-clicking LibrarySystem in the Projects panel, selecting Delete, checking Also delete sources, and clicking Yes.
- Delete the mysql-connector-j-9.7.0.jar file and its extracted folder from wherever you saved them.
Nice Work!
Nice Work!
You just built a fully functional Library Book Manager desktop application that connects Java to a real MySQL database. Every button on your form triggers live database operations through code you wrote yourself.
Here's what you learned:
- Designed a Swing GUI entirely with NetBeans drag-and-drop and connected it to a MySQL database using JDBC and MySQL Connector/J 9.7.0.
- Wrote SQL INSERT, SELECT (with LIKE for search), and DELETE statements using PreparedStatement to safely pass user input to the database.
- Displayed live database data in a JTable that dynamically refreshes after every add, search, or delete operation.
- Secret Mission: Implemented full Update functionality, completing all four CRUD operations in your application.
Ready to quiz yourself?