What is HSQLDB?

One of the easiest ways to learn Java Database Connectivity (JDBC) is with HyperSQL Database, better known as HSQLDB. It is small, written entirely in Java and can run directly inside your application, so there is no separate database server to install for this tutorial.

This updated example uses Java 25, modern JDBC practices and HSQLDB 2.7.4. HSQLDB 2.7.4 supports JDBC 4.3 and modern Java runtimes, which makes it a good lightweight database for JDBC development, testing and demonstrations.

Java 25 JDBC tutorial prerequisites

You only need a recent JDK, Maven and an IDE or text editor. IntelliJ IDEA, Eclipse, VS Code and the command line all work fine.

We will:

  1. Create a Java 25 Maven project.
  2. Add HSQLDB 2.7.4 as a dependency.
  3. Connect to a file-based HSQLDB database with JDBC.
  4. Create a PLAYER table from Java.
  5. Insert a row with a PreparedStatement.
  6. Query the table with a ResultSet.
  7. Use try-with-resources so JDBC objects are closed automatically.

HSQLDB Maven dependency for Java 25

Add HSQLDB to your Maven POM. The compiler release is set to Java 25 so Maven compiles the project against the Java 25 API.

<properties>
  <maven.compiler.release>25</maven.compiler.release>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
  <dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>2.7.4</version>
  </dependency>
</dependencies>

There is no need to call Class.forName() to manually load the HSQLDB driver. Modern JDBC drivers register themselves through Java's service-provider mechanism, and DriverManager discovers the driver automatically.

Create an HSQLDB database with JDBC

For this example, we will use an embedded, file-based database stored under the Maven project's target directory:

var url = "jdbc:hsqldb:file:target/myDB";
var user = "SA";
var password = "";

The jdbc:hsqldb:file: URL tells HSQLDB to use a persistent database on the local file system. The database files are created automatically when JDBC establishes the connection.

For a throwaway test database, you could instead use an in-memory URL such as jdbc:hsqldb:mem:testdb.

Open the JDBC connection

A modern JDBC connection can be opened directly with DriverManager.getConnection(). Because Connection implements AutoCloseable, put it in a try-with-resources statement.

try (var connection = DriverManager.getConnection(url, user, password)) {
    // JDBC work goes here.
}

When execution leaves the block, Java closes the JDBC connection automatically, even if an exception occurs.

Create the HSQLDB PLAYER table

We can create the table programmatically instead of requiring the HSQLDB GUI before the Java example can run. This makes the tutorial self-contained.

CREATE TABLE IF NOT EXISTS PLAYER (
    ID BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    LOGINNAME VARCHAR(255) NOT NULL,
    PASSWORD VARCHAR(255) NOT NULL
)

Execute the SQL with a JDBC Statement:

var createTable = """
    CREATE TABLE IF NOT EXISTS PLAYER (
        ID BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
        LOGINNAME VARCHAR(255) NOT NULL,
        PASSWORD VARCHAR(255) NOT NULL
    )
    """;

try (var statement = connection.createStatement()) {
    statement.executeUpdate(createTable);
}

The Java text block keeps the SQL readable and avoids a long string full of concatenation and escape characters.

Insert data with a PreparedStatement

Older JDBC examples often build an INSERT statement by concatenating values into a string. Don't do that with application data. A PreparedStatement is cleaner and protects the query from SQL injection problems caused by untrusted input.

var insertSql = """
    INSERT INTO PLAYER (LOGINNAME, PASSWORD)
    VALUES (?, ?)
    """;

try (var statement = connection.prepareStatement(insertSql)) {
    statement.setString(1, "McKenzie");
    statement.setString(2, "password");

    var rowsInserted = statement.executeUpdate();
    IO.println("Rows inserted: " + rowsInserted);
}

The identity column is generated by HSQLDB, so the application does not need to manually supply an ID.

Query HSQLDB with JDBC

Now query the table and process the returned ResultSet. Java 25's IO.println() keeps the console output concise.

var selectSql = """
    SELECT ID, LOGINNAME
    FROM PLAYER
    ORDER BY ID
    """;

try (var statement = connection.prepareStatement(selectSql);
     var results = statement.executeQuery()) {

    while (results.next()) {
        var id = results.getLong("ID");
        var loginName = results.getString("LOGINNAME");

        IO.println(id + ": " + loginName);
    }
}

Complete Java 25 JDBC and HSQLDB example

Put everything together and the entire JDBC application remains quite small:

package com.mcnz.jdbc.hsql;

import java.sql.DriverManager;

void main() throws Exception {

    var url = "jdbc:hsqldb:file:target/myDB";
    var user = "SA";
    var password = "";

    var createTable = """
        CREATE TABLE IF NOT EXISTS PLAYER (
            ID BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
            LOGINNAME VARCHAR(255) NOT NULL,
            PASSWORD VARCHAR(255) NOT NULL
        )
        """;

    var insertSql = """
        INSERT INTO PLAYER (LOGINNAME, PASSWORD)
        VALUES (?, ?)
        """;

    var selectSql = """
        SELECT ID, LOGINNAME
        FROM PLAYER
        ORDER BY ID
        """;

    try (var connection = DriverManager.getConnection(url, user, password)) {

        try (var statement = connection.createStatement()) {
            statement.executeUpdate(createTable);
        }

        try (var statement = connection.prepareStatement(insertSql)) {
            statement.setString(1, "McKenzie");
            statement.setString(2, "password");

            var rowsInserted = statement.executeUpdate();
            IO.println("Rows inserted: " + rowsInserted);
        }

        try (var statement = connection.prepareStatement(selectSql);
             var results = statement.executeQuery()) {

            while (results.next()) {
                var id = results.getLong("ID");
                var loginName = results.getString("LOGINNAME");

                IO.println(id + ": " + loginName);
            }
        }
    }
}

This version demonstrates the JDBC practices worth carrying into modern Java applications: automatic driver discovery, try-with-resources, prepared statements, SQL text blocks, local variable type inference and Java 25's concise IO.println() output.

Do you still need DatabaseManagerSwing?

No. The Java application above creates the database and table itself, so the HSQLDB GUI is optional. However, DatabaseManagerSwing is still useful when you want to inspect tables and run SQL interactively.

If you open the database with the HSQLDB database manager, use the same JDBC URL as the application:

jdbc:hsqldb:file:target/myDB

Make sure the Java application has released the embedded database before opening the same file database from another process.

And that's the modern Java 25 version of the JDBC and HSQLDB example. HSQLDB handles the lightweight database, JDBC provides the standard Java database API, and modern Java syntax keeps the application surprisingly compact.


Cameron McKenzie

Cameron McKenzie is an AWS Certified AI Practitioner, Machine Learning Engineer, Solutions Architect and author of many popular books in the software development and Cloud Computing space. His growing YouTube channel training devs in Java, Spring, AI and ML has well over 30,000 subscribers.

Learn Apache Maven fast!