Simple Billing Application using Java Swing
Let us create a simple billing application using Java Swing and connect it to a MySQL database
Our task is to create a simple billing application using Java Swing and connect it to MySQL database to store its data. So, without wasting time lets get into this tiny project.
Phase 1: Prerequisites & Setup
Step 1: Install Prerequisites
- Java Development Kit (JDK): Ensure you have JDK 8 or later installed. You can download it from Oracle or use an alternative like OpenJDK. Verify by opening a terminal/command prompt and typing
java -version
. - MySQL Server: You need a running MySQL server instance.
- Download and install MySQL Community Server from the official MySQL website ([invalid URL removed]).
- During installation, remember the root password you set, or preferably, create a dedicated user for this application later.
- Ensure the MySQL server service is running.
3. MySQL Client Tool (Recommended): Install a tool to interact with your MySQL database easily. Options include:
- MySQL Workbench: The official graphical tool from MySQL (https://dev.mysql.com/downloads/workbench/).
- DBeaver: A free universal database tool (https://dbeaver.io/).
- SQLyog Community Edition
- Or use the MySQL command-line client that comes with the server installation.
Step 2: Set Up the MySQL Database and User
Connect to MySQL: Open your chosen MySQL client tool (e.g., MySQL Workbench) and connect to your local MySQL server (usually as the root
user initially, using the password you set during installation).
Create Database: Execute the following SQL command to create a new database for the application:
CREATE DATABASE billing_app_db;
Create Dedicated User: It’s best practice to create a separate user for your application instead of using root
. Replace 'your_password'
with a strong password.
CREATE USER 'billing_user'@'localhost' IDENTIFIED BY 'your_password';
Grant Privileges: Give the new user the necessary permissions on the database you created:
GRANT SELECT, INSERT, UPDATE, DELETE ON billing_app_db.* TO 'billing_user'@'localhost';
Apply Changes:
FLUSH PRIVILEGES;
Create Tables: Select the billing_app_db
database in your client tool (e.g., USE billing_app_db;
) and execute the following SQL commands to create the necessary tables:
-- Table structure for bill_items
CREATE TABLE bill_items (
item_id INT AUTO_INCREMENT PRIMARY KEY,
bill_id INT NOT NULL,
item_name VARCHAR(255) NOT NULL,
quantity INT NOT NULL,
price_per_unit DECIMAL(10, 2) NOT NULL, -- Using DECIMAL here too
item_total DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (bill_id) REFERENCES bills (bill_id) ON DELETE CASCADE
);
Note: We’re using DECIMAL(10, 2)
which is generally preferred for financial data over DOUBLE
or FLOAT
to avoid potential precision issues. We'll adjust the Java code slightly.
Step 3: Create Project Structure
Create the following directory structure for your project (e.g., named MySQLBillingProject
):
MySQLBillingProject/
├── src/
│ └── com/
│ └── billingapp/
│ ├── main/
│ ├── gui/
│ ├── model/
│ └── db/
└── lib/
Step 4: Get MySQL Connector/J (JDBC Driver)
- Download: Go to the MySQL Connector/J download page (https://dev.mysql.com/downloads/connector/j/). Select “Platform Independent” and download the ZIP or TAR archive.
- Extract: Extract the downloaded archive. Inside, you’ll find the
mysql-connector-j-VERSION.jar
file (the exact name depends on the version). - Copy: Copy this
.jar
file into thelib
folder you created in your project structure (MySQLBillingProject/lib/
).
(Alternative for Maven/Gradle users): If using Maven, add this to your pom.xml
:
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
If using Gradle, add this to your build.gradle
:
implementation 'com.mysql:mysql-connector-j:8.0.33' // Use the latest appropriate version
Phase 2: Writing the Java Code
Place the following Java files into their respective directories under src/
.
Step 5.1: Model Classes (src/com/billingapp/model/
)
(These are slightly adjusted to use double
consistently internally as before, but the database uses DECIMAL
. JDBC handles the conversion. You could change the Java models to use BigDecimal
for higher precision if desired.)
BillItem.java
(Mostly unchanged, ensure consistency)
package com.billingapp.model;
import java.text.DecimalFormat;
// Consider importing java.math.BigDecimal for precise calculations
public class BillItem {
// Using double internally for simplicity, matching previous examples.
// BigDecimal is recommended for production financial apps.
private String itemName;
private int quantity;
private double pricePerUnit;
private double itemTotal;
private static final DecimalFormat df = new DecimalFormat("#0.00");
// Constructor
public BillItem(String itemName, int quantity, double pricePerUnit) {
if (itemName == null || itemName.trim().isEmpty()) {
throw new IllegalArgumentException("Item name cannot be empty.");
}
if (quantity <= 0) {
throw new IllegalArgumentException("Quantity must be positive.");
}
if (pricePerUnit < 0) {
throw new IllegalArgumentException("Price per unit cannot be negative.");
}
this.itemName = itemName.trim();
this.quantity = quantity;
this.pricePerUnit = pricePerUnit;
this.itemTotal = calculateItemTotal();
}
// Getters
public String getItemName() { return itemName; }
public int getQuantity() { return quantity; }
public double getPricePerUnit() { return pricePerUnit; }
public String getFormattedPricePerUnit() { return df.format(pricePerUnit); }
public double getItemTotal() { return itemTotal; }
public String getFormattedItemTotal() { return df.format(itemTotal); }
// Private helper for calculation
private double calculateItemTotal() {
// For higher precision:
// return BigDecimal.valueOf(this.quantity)
// .multiply(BigDecimal.valueOf(this.pricePerUnit))
// .doubleValue();
return this.quantity * this.pricePerUnit;
}
// Useful for displaying in the JTable easily
public Object[] toTableRow() {
return new Object[]{
itemName,
quantity,
getFormattedPricePerUnit(),
getFormattedItemTotal()
};
}
}
Bill.java
(Unchanged from the SQLite version is fine)
package com.billingapp.model;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// Consider importing java.math.BigDecimal
public class Bill {
private long billId; // Use long for potentially large IDs
private String customerName;
private LocalDateTime billDate;
private double totalAmount; // Or BigDecimal
private final List<BillItem> items;
private static final DecimalFormat df = new DecimalFormat("#0.00");
// Constructor for creating a new bill
public Bill(String customerName) {
this.customerName = (customerName == null || customerName.trim().isEmpty()) ? "N/A" : customerName.trim();
this.billDate = LocalDateTime.now();
this.items = new ArrayList<>();
this.totalAmount = 0.0;
}
// Constructor potentially used when loading from DB
public Bill(long billId, String customerName, LocalDateTime billDate, double totalAmount) {
this.billId = billId;
this.customerName = customerName;
this.billDate = billDate;
this.totalAmount = totalAmount;
this.items = new ArrayList<>(); // Items loaded separately
}
// --- Methods ---
public void addItem(BillItem item) {
if (item != null) {
this.items.add(item);
recalculateTotal();
}
}
public void removeItem(BillItem item) {
if (item != null) {
this.items.remove(item);
recalculateTotal();
}
}
private void recalculateTotal() {
// Using double:
this.totalAmount = 0.0;
for (BillItem item : items) {
this.totalAmount += item.getItemTotal();
}
// Using BigDecimal:
// BigDecimal sum = BigDecimal.ZERO;
// for (BillItem item : items) {
// sum = sum.add(BigDecimal.valueOf(item.getItemTotal()));
// }
// this.totalAmount = sum.doubleValue(); // Or keep as BigDecimal
}
// --- Getters ---
public long getBillId() { return billId; }
public String getCustomerName() { return customerName; }
public LocalDateTime getBillDate() { return billDate; }
public double getTotalAmount() { return totalAmount; } // Or BigDecimal
public String getFormattedTotalAmount() { return df.format(totalAmount); }
public List<BillItem> getItems() { return Collections.unmodifiableList(items); }
// --- Setters ---
public void setBillId(long billId) {
if (this.billId == 0 && billId > 0) {
this.billId = billId;
} else if (this.billId != 0) {
System.err.println("Warning: Attempted to reset billId for Bill " + this.billId);
}
}
}
Step 5.2: Database Helper (src/com/billingapp/db/
)
DatabaseHelper.java
(This is the main file to change)
package com.billingapp.db;
import com.billingapp.model.Bill;
import com.billingapp.model.BillItem;
import java.sql.*;
import java.time.LocalDateTime; // Keep using LocalDateTime
public class DatabaseHelper {
// --- IMPORTANT: Configure these for your MySQL setup ---
private static final String DB_HOST = "localhost"; // Or your MySQL server address
private static final String DB_PORT = "3306"; // Default MySQL port
private static final String DB_NAME = "billing_app_db";
private static final String DB_USER = "billing_user";
private static final String DB_PASSWORD = "your_password"; // CHANGE THIS!
// Construct the JDBC URL for MySQL
private static final String DATABASE_URL = "jdbc:mysql://" + DB_HOST + ":" + DB_PORT + "/" + DB_NAME +
"?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true";
// Add allowPublicKeyRetrieval if needed for newer drivers/MySQL versions
// --- !! SECURITY WARNING !! ---
// Hardcoding credentials like this is NOT recommended for production applications.
// Use environment variables, configuration files, or secure credential management systems.
// This is done here for simplicity in this example ONLY.
// --- Initialization ---
// No need for initializeDatabase() method anymore, as tables are created manually via SQL client.
// However, you might want a method to check the connection.
public static boolean testConnection() {
try (Connection conn = getConnection()) {
System.out.println("Database connection successful!");
return true;
} catch (SQLException e) {
System.err.println("Database connection failed: " + e.getMessage());
e.printStackTrace(); // Print details for debugging
return false;
}
}
public static Connection getConnection() throws SQLException {
// Optional: Explicitly load the driver (usually not needed with modern JDBC)
// try {
// Class.forName("com.mysql.cj.jdbc.Driver");
// } catch (ClassNotFoundException e) {
// throw new SQLException("MySQL JDBC Driver not found.", e);
// }
return DriverManager.getConnection(DATABASE_URL, DB_USER, DB_PASSWORD);
}
// --- Save Operation ---
public static boolean saveBill(Bill bill) {
if (bill == null || bill.getItems().isEmpty()) {
System.err.println("Cannot save null or empty bill.");
return false;
}
String insertBillSql = "INSERT INTO bills(customer_name, bill_date, total_amount) VALUES(?, ?, ?)";
String insertItemSql = "INSERT INTO bill_items(bill_id, item_name, quantity, price_per_unit, item_total) VALUES(?, ?, ?, ?, ?)";
Connection conn = null;
PreparedStatement pstmtBill = null;
PreparedStatement pstmtItem = null;
ResultSet generatedKeys = null;
try {
conn = getConnection();
conn.setAutoCommit(false); // Start transaction
// 1. Insert the Bill record
pstmtBill = conn.prepareStatement(insertBillSql, Statement.RETURN_GENERATED_KEYS);
pstmtBill.setString(1, bill.getCustomerName());
// Convert LocalDateTime to SQL Timestamp for DATETIME column
pstmtBill.setTimestamp(2, Timestamp.valueOf(bill.getBillDate()));
pstmtBill.setDouble(3, bill.getTotalAmount()); // JDBC handles double -> DECIMAL conversion
int affectedRows = pstmtBill.executeUpdate();
if (affectedRows == 0) throw new SQLException("Creating bill failed, no rows affected.");
// 2. Get the generated Bill ID
generatedKeys = pstmtBill.getGeneratedKeys();
long billId;
if (generatedKeys.next()) {
billId = generatedKeys.getLong(1);
bill.setBillId(billId);
} else {
throw new SQLException("Creating bill failed, no ID obtained.");
}
generatedKeys.close();
pstmtBill.close();
// 3. Insert each BillItem record
pstmtItem = conn.prepareStatement(insertItemSql);
for (BillItem item : bill.getItems()) {
pstmtItem.setLong(1, billId);
pstmtItem.setString(2, item.getItemName());
pstmtItem.setInt(3, item.getQuantity());
pstmtItem.setDouble(4, item.getPricePerUnit()); // JDBC handles double -> DECIMAL
pstmtItem.setDouble(5, item.getItemTotal()); // JDBC handles double -> DECIMAL
pstmtItem.addBatch();
}
pstmtItem.executeBatch(); // Execute all item inserts
conn.commit(); // Commit transaction
System.out.println("Bill #" + billId + " saved successfully to MySQL.");
return true;
} catch (SQLException e) {
System.err.println("MySQL Database error saving bill: " + e.getMessage());
e.printStackTrace();
if (conn != null) {
try { conn.rollback(); System.err.println("Transaction rolled back."); }
catch (SQLException ex) { System.err.println("Error during rollback: " + ex.getMessage()); }
}
return false;
} finally {
// Clean up resources
try { if (generatedKeys != null) generatedKeys.close(); } catch (SQLException e) { /* ignored */ }
try { if (pstmtBill != null) pstmtBill.close(); } catch (SQLException e) { /* ignored */ }
try { if (pstmtItem != null) pstmtItem.close(); } catch (SQLException e) { /* ignored */ }
if (conn != null) {
try { conn.setAutoCommit(true); conn.close(); }
catch (SQLException e) { System.err.println("Error closing connection: " + e.getMessage()); }
}
}
}
// --- Optional: Load operations would go here ---
}
Remember to change 'your_password'
in DatabaseHelper.java
to the actual password you set for billing_user
.
Step 5.3: GUI Class (src/com/billingapp/gui/
)
BillingFrame.java
(This class should require NO CHANGES from the SQLite version, as it only interacts with DatabaseHelper
.)
// Paste the exact same code for BillingFrame.java from the previous
// SQLite example here. It does not need modification because the database
// interaction is handled entirely by DatabaseHelper.
// Make sure the package declaration is correct:
package com.billingapp.gui;
import com.billingapp.db.DatabaseHelper;
import com.billingapp.model.Bill;
import com.billingapp.model.BillItem;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.text.ParseException; // Needed for parsing in saveCurrentBill
public class BillingFrame extends JFrame implements ActionListener {
// --- GUI Components ---
private JTextField txtCustomerName;
private JTextField txtItemName;
private JTextField txtQuantity;
private JTextField txtPrice;
private JButton btnAddItem;
private JButton btnSaveBill;
private JButton btnNewBill;
private JTable tblBillItems;
private DefaultTableModel tableModel;
private JLabel lblGrandTotal;
// --- Data ---
private Bill currentBill; // Holds the bill currently being created
private final DecimalFormat df = new DecimalFormat("#0.00");
public BillingFrame() {
setTitle("Simple Billing Application - MySQL Edition"); // Update title
setSize(700, 550);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout(10, 10));
// --- Customer Panel (Top-North) ---
JPanel customerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
customerPanel.add(new JLabel("Customer Name:"));
txtCustomerName = new JTextField(25);
customerPanel.add(txtCustomerName);
// --- Input Panel (Top-Center) ---
JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
inputPanel.setBorder(BorderFactory.createTitledBorder("Add Item"));
inputPanel.add(new JLabel("Item:"));
txtItemName = new JTextField(15);
inputPanel.add(txtItemName);
inputPanel.add(new JLabel("Qty:"));
txtQuantity = new JTextField(4);
inputPanel.add(txtQuantity);
inputPanel.add(new JLabel("Price:"));
txtPrice = new JTextField(6);
inputPanel.add(txtPrice);
btnAddItem = new JButton("Add");
btnAddItem.setToolTipText("Add item to current bill");
btnAddItem.addActionListener(this);
inputPanel.add(btnAddItem);
// Combine Customer and Input panels
JPanel topPanel = new JPanel(new BorderLayout());
topPanel.add(customerPanel, BorderLayout.NORTH);
topPanel.add(inputPanel, BorderLayout.CENTER);
add(topPanel, BorderLayout.NORTH);
// --- Bill Items Table (Center) ---
String[] columnNames = {"Item Name", "Quantity", "Price/Unit", "Total"};
tableModel = new DefaultTableModel(columnNames, 0) {
@Override public boolean isCellEditable(int row, int column) { return false; }
};
tblBillItems = new JTable(tableModel);
tblBillItems.setFillsViewportHeight(true);
tblBillItems.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane scrollPane = new JScrollPane(tblBillItems);
add(scrollPane, BorderLayout.CENTER);
// --- Total and Action Panel (Bottom) ---
JPanel bottomPanel = new JPanel(new BorderLayout(10, 5));
lblGrandTotal = new JLabel("Grand Total: 0.00");
lblGrandTotal.setFont(new Font("Arial", Font.BOLD, 16));
lblGrandTotal.setHorizontalAlignment(SwingConstants.LEFT);
bottomPanel.add(lblGrandTotal, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
btnSaveBill = new JButton("Save Bill");
btnSaveBill.setToolTipText("Save the current bill to the database");
btnSaveBill.addActionListener(this);
buttonPanel.add(btnSaveBill);
btnNewBill = new JButton("New Bill");
btnNewBill.setToolTipText("Clear the form to start a new bill");
btnNewBill.addActionListener(this);
buttonPanel.add(btnNewBill);
bottomPanel.add(buttonPanel, BorderLayout.EAST);
add(bottomPanel, BorderLayout.SOUTH);
// --- Initialize ---
startNewBill();
setLocationRelativeTo(null);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == btnAddItem) addItemToBill();
else if (source == btnSaveBill) saveCurrentBill();
else if (source == btnNewBill) startNewBill();
}
private void startNewBill() {
// Pass customer name field text, might be empty initially
currentBill = new Bill(txtCustomerName.getText());
tableModel.setRowCount(0);
lblGrandTotal.setText("Grand Total: 0.00");
txtCustomerName.setText(""); // Clear customer for next bill
clearInputFields();
txtCustomerName.requestFocusInWindow();
System.out.println("Ready for new bill.");
}
private void addItemToBill() {
// Update bill's customer name IF it changed in the text field since last item added
// This logic is slightly flawed - better to create the final Bill object just before saving.
// Let's keep it simple for now: customer name is primarily set when starting a new bill or saving.
String itemName = txtItemName.getText().trim();
String quantityStr = txtQuantity.getText().trim();
String priceStr = txtPrice.getText().trim();
if (itemName.isEmpty()) { showError("Item Name cannot be empty."); return; }
int quantity; double price;
try {
quantity = Integer.parseInt(quantityStr);
if (quantity <= 0) { showError("Quantity must be positive."); return; }
} catch (NumberFormatException ex) { showError("Invalid Quantity."); return; }
try {
price = Double.parseDouble(priceStr);
if (price < 0) { showError("Price cannot be negative."); return; }
} catch (NumberFormatException ex) { showError("Invalid Price."); return; }
try {
BillItem newItem = new BillItem(itemName, quantity, price);
currentBill.addItem(newItem); // Add to in-memory bill
tableModel.addRow(newItem.toTableRow()); // Add to display table
updateGrandTotal();
clearInputFields();
} catch (IllegalArgumentException ex) { showError(ex.getMessage()); }
}
private void saveCurrentBill() {
// Create the final Bill object to save, using the current customer name text field value
Bill billToSave = new Bill(txtCustomerName.getText());
// Populate the billToSave object from the JTable rows
if (tableModel.getRowCount() == 0) {
showInfo("Cannot save an empty bill.");
return;
}
for (int i = 0; i < tableModel.getRowCount(); i++) {
String name = (String) tableModel.getValueAt(i, 0);
int qty = (int) tableModel.getValueAt(i, 1);
double price;
try {
// Use the locale-aware DecimalFormat parser
price = df.parse((String) tableModel.getValueAt(i, 2)).doubleValue();
billToSave.addItem(new BillItem(name, qty, price)); // Add item to the final Bill object
} catch (ParseException | NumberFormatException | ClassCastException e) { // Catch potential errors
System.err.println("Error parsing price from table row " + i + ": " + tableModel.getValueAt(i, 2));
e.printStackTrace();
showError("Internal Error: Could not parse data from table row " + (i + 1) + ".");
return; // Stop saving
} catch (IllegalArgumentException ex) {
showError("Data error in table row " + (i+1) + ": " + ex.getMessage());
return; // Stop saving
}
}
// Now save the fully constructed billToSave object
boolean success = DatabaseHelper.saveBill(billToSave);
if (success) {
showInfo("Bill #" + billToSave.getBillId() + " saved successfully!"); // Use ID from saved object
startNewBill(); // Clear form for next bill
} else {
showError("Failed to save the bill to the database. Check console logs.");
// Do NOT clear the form here, allow user to retry or fix data
}
}
private void updateGrandTotal() { lblGrandTotal.setText("Grand Total: " + currentBill.getFormattedTotalAmount()); }
private void clearInputFields() { txtItemName.setText(""); txtQuantity.setText(""); txtPrice.setText(""); txtItemName.requestFocusInWindow(); }
private void showError(String message) { JOptionPane.showMessageDialog(this, message, "Error", JOptionPane.ERROR_MESSAGE); }
private void showInfo(String message) { JOptionPane.showMessageDialog(this, message, "Information", JOptionPane.INFORMATION_MESSAGE); }
}
Step 5.4: Main Application Class (src/com/billingapp/main/
)
BillingApp.java
(Only change is removing the initializeDatabase
call)
package com.billingapp.main;
import com.billingapp.db.DatabaseHelper; // Import needed for testConnection
import com.billingapp.gui.BillingFrame;
import javax.swing.*;
public class BillingApp {
public static void main(String[] args) {
// Optional: Set Look and Feel
try {
for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) {
try { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); }
catch (Exception ex) { System.err.println("Failed to set LookAndFeel."); }
}
// --- Test MySQL Connection ---
// It's good practice to check if the DB connection works on startup.
if (!DatabaseHelper.testConnection()) {
JOptionPane.showMessageDialog(null,
"Database connection failed. Please check MySQL server status and DatabaseHelper settings.\nApplication will exit.",
"Database Connection Error", JOptionPane.ERROR_MESSAGE);
System.exit(1); // Exit if DB connection fails
}
// Schedule GUI creation on the Event Dispatch Thread (EDT)
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new BillingFrame(); // Create and show the main window
}
});
}
}
Phase 3: Compile and Run
Step 6: Compile the Project
- Open Terminal/Command Prompt: Navigate to the root directory of your project (
MySQLBillingProject
). - Compile: Use the appropriate command for your OS, making sure the MySQL Connector/J JAR is in the
lib
folder.
Windows:
javac -cp ".;lib/*" -d . src/com/billingapp/main/BillingApp.java
src/com/billingapp/gui/BillingFrame.java
src/com/billingapp/model/Bill.java
src/com/billingapp/model/BillItem.java
src/com/billingapp/db/DatabaseHelper.java
macOS/Linux:
javac -cp ".:lib/*" -d . src/com/billingapp/main/BillingApp.java
src/com/billingapp/gui/BillingFrame.java
src/com/billingapp/model/Bill.java
src/com/billingapp/model/BillItem.java
src/com/billingapp/db/DatabaseHelper.java
- (This compiles all necessary
.java
files and places the.class
files in the correct package structure under your project root).
Step 7: Run the Application
- Stay in the root directory (
MySQLBillingProject
) in your terminal/command prompt. - Run:
Windows:
java -cp ".;lib/*" com.billingapp.main.BillingApp
Output:
macOS/Linux:
java -cp ".:lib/*" com.billingapp.main.BillingApp
Output:
How to Use:
1. The application window should appear.
2. If you get a “Database connection failed” error, double-check:
- Is your MySQL server running?
- Did you correctly create the database (
billing_app_db
) and user (billing_user
)? - Did you grant privileges correctly?
- Did you update the
DB_USER
andDB_PASSWORD
constants inDatabaseHelper.java
? - Is the MySQL Connector/J JAR file in the
lib
folder?
3. Enter a customer name (optional).
4. Enter item details (name, quantity, price) and click “Add”. The item appears in the table, and the total updates.
5. Add more items as needed.
6. Click “Save Bill”. If successful, you’ll get a confirmation message with the Bill ID from the database, and the form will clear for a new bill. Check your bills
and bill_items
tables in MySQL Workbench (or your client) to see the saved data.
7. Click “New Bill” to clear the form manually without saving.
You now have a simple Java Swing billing application connected to a MySQL database! Remember the security warning about hardcoding credentials; for real applications, use a more secure method. We will see more of real time applications in upcoming stories. Follow धनुष.K