Crafting a Java Banking System: Lessons in OOP, SQLite, and Console UI Design

Rehan ShafiqRehan Shafiq
10 min read

Crafting a Java Banking System: Lessons in OOP, SQLite, and Console UI Design

Hi there! I’m excited to share my journey of building the Bank Account System, a terminal-based application developed in Java using Object-Oriented Programming (OOP) and SQLite. This project allows users to create accounts, log in, deposit money, withdraw funds, transfer money to others, and check their balance—all through a clean console interface. It features persistent data storage, ASCII art for a polished look, and scripts for easy setup on any platform. In this article, I’ll start with the big picture of how the Bank Account System is structured, then walk you through key features, code snippets, and lessons learned. You can explore the full project on GitHub: https://github.com/Rehancodecraft/Banking-System. Let’s get started!

The Big Picture: Structure and Workflow of the Bank Account System

The Bank Account System is a modular console app built with Java, designed to manage user accounts and transactions efficiently. It uses SQLite for data storage and provides a user-friendly interface with ASCII art. Here’s how it’s organized and how its parts work together.

Project Structure

The project files are arranged for clarity and ease of use:

Banking-System/
├── Bank/                    // Java source files
│   ├── BankAccount.java     // Manages accounts and transactions
│   ├── Database.java        // Handles SQLite operations
│   ├── Main.java            // Entry point and main menu
│   ├── Utility.java         // UI and input handling
├── Bank.db                  // SQLite database file
├── Images/                  // Screenshots for documentation
│   ├── AccountNotFound.png
│   ├── CreateAccount.png
│   └── ... (other images)
├── Libraries/               // External dependencies
│   └── sqlite-jdbc-3.42.0.0.jar  // SQLite JDBC driver
├── out/                     // Compiled Java classes
├── README.md                // Project documentation
├── run.bat                  // Windows script
├── run.sh                   // Linux/macOS script
  • Bank Package: All Java classes are in the Bank folder, organizing the app’s logic.

  • Bank.db: Stores account data persistently using SQLite.

  • Libraries: Contains the SQLite driver for database connectivity.

  • Scripts: run.sh and run.bat make it easy to compile and run the app on different platforms.

How It Works

The Bank Account System connects its components to deliver a smooth experience:

  • Main.java starts the app, showing a menu (via Utility.java) for users to log in or create accounts.

  • BankAccount.java handles core actions like deposits and withdrawals, calling Database.java to save or fetch data.

  • Database.java manages the SQLite database (Bank.db), ensuring accounts and transactions are stored permanently.

  • Utility.java takes care of user inputs and displays, using ASCII art to make menus and receipts look nice.

When a user runs the app (using run.sh or run.bat), Main.java launches the menu, and their choices (e.g., deposit, withdraw) trigger methods in BankAccount.java, which interact with the database and display results via Utility.java.

Harnessing Java and OOP

I chose Java because it runs on any platform and supports Object-Oriented Programming (OOP). OOP let me model real-world concepts—like bank accounts—as objects, making the code easier to manage and expand.

Code Example: The BankAccount Class

The BankAccount class is the heart of the Bank Account System. It stores account details and handles actions like depositing money:

public class BankAccount {
    private String name;
    private String account_no;
    private int acct_type;
    private int balance;
    private String password;

    public void depositAmount(BankAccount account) {
        Utility.UserInterface.depositAmountDisplay();
        int depositAmount = Utility.GetInputWithStyles.getDepositAmountInput();
        Database.depositToDatabase(depositAmount, account.account_no, account.name);
        Utility.UserInterface.depositAmountReceiptDisplay(account.name, account.account_no, depositAmount);
    }
}
  • Encapsulation: Private fields like name and balance keep data safe.

  • Abstraction: Methods like depositAmount() hide complex logic behind simple calls.

  • Modularity: Each action has its own method, making the code reusable.

Lesson Learned

OOP kept my code organized. Thinking of accounts and transactions as objects, and splitting tasks into classes like BankAccount and Database, made the project much easier to handle.

Adding Authentication with Login

When users launch the Bank Account System, they see a welcome screen with options to log in or create an account. I built a login system that checks the user’s name and password against the database for a secure experience.

Code Example: Handling Login in BankAccount

The loginAccount() method manages the login process:

public static BankAccount loginAccount() {
    Utility.UserInterface.loginAccountDisplay();
    String fullName = Utility.GetInputWithStyles.getFullNameInputForLoginAccount();
    String password = Utility.GetInputWithStyles.getPasswordInputForLoginAccount();
    BankAccount account = Database.loginAccount(fullName, password);
    return account;
}
  • User Interface: loginAccountDisplay() shows a menu with ASCII art for a clean look.

  • Input Handling: getFullNameInputForLoginAccount() and getPasswordInputForLoginAccount() safely collect user input.

  • Database Check: Database.loginAccount() verifies the name and password in the database, returning the account if valid or showing an error if not.

Here’s the login screen after a successful login:

Logging in with full name and password, followed by a success message

Lesson Learned

Building the login feature taught me how to connect user inputs with database checks. Seeing the “Logged in Successfully” message made the Bank Account System feel like a real banking app!

Integrating SQLite for Persistent Storage

Initially, the Bank Account System stored data in memory, so accounts vanished when the app closed. I added SQLite to save data in a Bank.db file, ensuring accounts and transactions persist across sessions.

Code Example: Setting Up SQLite in Database

The Database class connects to SQLite and sets up a table for accounts:

public class Database {
    static Connection connection;
    public static void connectToDatabase() {
        try {
            final String DB_FILE = "Bank.db";
            final String URL = "jdbc:sqlite:" + DB_FILE;
            File dbFile = new File(DB_FILE);
            boolean isNew = !dbFile.exists();
            connection = DriverManager.getConnection(URL);
            connection.setAutoCommit(false);
            if (isNew) {
                initializeDatabase();
            }
        } catch (Exception e) {
            System.out.println("Error in database Connection");
            e.printStackTrace();
        }
    }

    private static void initializeDatabase() {
        try {
            String createTableSQL = "CREATE TABLE IF NOT EXISTS Accounts (" +
                    "Account_No VARCHAR(20) PRIMARY KEY, " +
                    "AccountHolder_Name VARCHAR(50), " +
                    "Account_Password VARCHAR(20), " +
                    "Account_Type INT, " +
                    "Balance INT);";
            PreparedStatement createStmt = connection.prepareStatement(createTableSQL);
            createStmt.executeUpdate();
            connection.commit();
        } catch (Exception e) {
            System.out.println("Error initializing database.");
        }
    }
}
  • Connecting to SQLite: connectToDatabase() links Java to Bank.db, creating the file if it doesn’t exist.

  • Creating a Table: initializeDatabase() sets up the Accounts table to store account details.

  • Safe Updates: connection.commit() ensures changes are saved reliably.

Account Creation in Action

Users can select “Create Account,” enter their name, password, and account type (savings or current), and the Bank Account System saves the data with a unique account number.

Here’s the account creation process:

Creating a new account with a generated account number

Challenges and Solutions

  • Learning SQL: I learned to use PreparedStatement for safe data insertion.

  • Error Handling: Try-catch blocks catch issues like connection failures.

  • Saving Data: SQLite ensured accounts persisted, making the app more realistic.

Lesson Learned

Using SQLite taught me how to store data permanently. Seeing accounts reappear after restarting the Bank Account System felt like a big win!

Handling User Input with Validation

To make the Bank Account System user-friendly, I focused on handling inputs properly, preventing crashes from invalid entries like letters instead of numbers.

Code Example: Validating Inputs in Utility

The Utility.GetInputWithStyles class validates inputs, such as deposit amounts:

public static int getDepositAmountInput() {
    while (true) {
        System.out.print(green + "                                              Enter Deposit amount: " + reset);
        String userInput = input.nextLine().trim();
        if (userInput.matches("\\d+")) {
            int amount = Integer.parseInt(userInput);
            if (amount > 0) {
                return amount;
            } else {
                UserInterface.invalidDepositAmountDisplay();
            }
        } else {
            UserInterface.invalidDepositAmountDisplay();
        }
    }
}
  • Validation Loop: A while loop ensures the input is a positive number using regex (\\d+).

  • Error Messages: invalidDepositAmountDisplay() alerts users to invalid inputs.

  • User-Friendly Prompts: Green-colored prompts guide users clearly.

Handling Errors Like Insufficient Balance

The withdraw() method checks the balance before allowing withdrawals, showing an error if funds are insufficient.

Here’s what happens when a user tries to withdraw too much:

Error message when trying to withdraw more than the available balance

Challenges

  • Invalid Inputs: Regex ensured inputs were numbers, avoiding crashes.

  • Edge Cases: I added checks for negative amounts and insufficient funds.

Lesson Learned

Input validation made the Bank Account System reliable. It was satisfying to see the app catch errors and guide users to correct them.

Designing the Console UI with ASCII Art

I wanted the Bank Account System to look appealing despite being a console app, so I added ASCII art for menus and receipts, along with colors for visual clarity.

Code Example: Creating a Receipt in Utility

The depositAmountReceiptDisplay() method formats a deposit receipt:

public static void depositAmountReceiptDisplay(String Name, String AccountNo, int amount) {
    LocalDateTime now = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
    String formattedDateTime = now.format(formatter);
    final int totalWidth = 50;
    String divider = "-".repeat(totalWidth);
    String formattedName = String.format("%-" + (totalWidth - 12) + "s", Name);
    System.out.println(yellow + "   *" + green + "                             " + divider + reset + yellow + "                              *");
    System.out.println(yellow + "   *" + green + "                                              " + yellow + bold + "Deposit Receipt" + reset + green + "                      " + yellow + "                          *");
    System.out.println(yellow + "   *" + green + "                              Account Holder  : " + reset + "          " + formattedName + yellow + "             *");
    // ... other fields
}
  • ASCII Art: Characters like * and - create borders, designed using patorjk.com/software/taag.

  • Colors: ANSI codes (e.g., green, yellow) highlight key elements.

  • Formatting: String.format aligns receipt fields neatly.

A Deposit Receipt in Action

After depositing money, the Bank Account System displays a receipt with the user’s details, amount, and timestamp.

Here’s a deposit receipt:

Deposit receipt with formatted ASCII borders and timestamp

Challenges

  • Alignment: I adjusted spacing to fit different terminals.

  • Color Support: Tested on Linux and Windows CMD to ensure colors displayed correctly.

Lesson Learned

ASCII art and colors made the Bank Account System engaging. It showed me that even a console app can offer a great user experience with thoughtful design.

Formatting Code for Clarity

As the Bank Account System grew, I prioritized clean, readable code to simplify debugging and future updates.

Code Example: A Clear Menu in BankAccount

The handleAccountServices() method manages the main menu and user choices:

public void handleAccountServices(BankAccount account) {
    Utility.UserInterface.bankServicesDisplay();
    int servicesOption = Utility.GetInputWithStyles.getBankServicesInput();
    while (servicesOption != 6) {
        switch (servicesOption) {
            case 1:
                account.depositAmount(account);
                exitFromBank();
                Utility.UserInterface.bankServicesDisplay();
                servicesOption = Utility.GetInputWithStyles.getBankServicesInput();
                break;
            case 2:
                account.withdraw(account);
                exitFromBank();
                Utility.UserInterface.bankServicesDisplay();
                servicesOption = Utility.GetInputWithStyles.getBankServicesInput();
                break;
            // ... other cases
        }
    }
    Utility.UserInterface.exiting();
}
  • Indentation: Used 4 spaces for readability.

  • Clear Names: Descriptive names like depositAmount() and servicesOption explain their purpose.

  • Modular Code: Each menu option is a separate case, keeping the code organized.

Checking Account Details

The “Check Balance” option displays account details in a neat format, reflecting the clarity of the code structure.

Here’s the account details screen:

Account details displayed after checking balance

Lesson Learned

Clean code saved time during debugging and made the Bank Account System easier to share, as others could quickly understand it.

Adding Features Like Money Transfer

A standout feature in the Bank Account System is money transfer between accounts. Users select “Send Money,” enter the recipient’s account number and amount, and the app processes the transaction.

How It Works

  • Input: Collects the recipient’s account number and amount.

  • Validation: Checks if the recipient exists and if the sender has enough funds.

  • Database Update: Updates both accounts’ balances using sendMoneyToDatabaseToOtherAccount().

If the recipient isn’t found, an error appears (e.g., “Account Not Found”). On success, a receipt shows the transaction details.

Lesson Learned

Money transfer made the Bank Account System feel more like a real bank. It taught me how to safely handle transactions between database records, which was a rewarding challenge.

Creating Cross-Platform Run Scripts

I added scripts to make running the Bank Account System simple for everyone, without needing to set up Java manually.

run.sh (Linux/macOS)

#!/bin/bash
javac -cp Libraries/sqlite-jdbc-3.42.0.0.jar Bank/*.java
java -cp "Libraries/sqlite-jdbc-3.42.0.0.jar;." Bank.Main

run.bat (Windows)

@echo off
javac -cp Libraries\sqlite-jdbc-3.42.0.0.jar Bank\*.java
java -cp "Libraries\sqlite-jdbc-3.42.0.0.jar;." Bank.Main
pause
  • Purpose: These scripts compile and run the app, including the SQLite library.

  • Usage: On Linux/macOS, use chmod +x run.sh then ./run.sh. On Windows, double-click run.bat or run it in CMD.

Lesson Learned

These scripts made the Bank Account System more accessible. I also learned to manage external libraries in the classpath, a new skill for me.

Key Lessons Learned

This project taught me a lot:

  • OOP: Classes and methods made the code scalable.

  • SQLite: Persistent storage made the app realistic.

  • User Input: Validation ensured stability.

  • UI Design: ASCII art and colors enhanced the experience.

  • Code Organization: Clean code made the project shareable.

Conclusion

Building the Bank Account System was a fantastic experience. From login systems to ASCII art receipts, every step brought new lessons. The project is on GitHub (https://github.com/Rehancodecraft/Banking-System), with a README and screenshots. Try it out and share your feedback!

If you’re new to Java or seeking a fun project, I hope this inspires you. You can create amazing things with code and creativity. Happy coding!

10
Subscribe to my newsletter

Read articles from Rehan Shafiq directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rehan Shafiq
Rehan Shafiq