Skip to content
Java jdbc 7 min read

Steps to Connect a Database

Connecting to a database in Java follows a clear, repeatable sequence. Once you know the five steps — load the driver, open a connection, create a statement, execute SQL, close resources — you can talk to any relational database with confidence.

The Five Steps at a Glance

Every JDBC program, no matter how simple or complex, walks through the same pipeline:

StepWhat happens
1. Register the driverTell the JVM which Driver class handles your database URL
2. Open a connectionAsk DriverManager for a live Connection
3. Create a statementWrap your SQL in a Statement or PreparedStatement
4. Execute SQL & process resultsRun the query and iterate over the ResultSet
5. Close resourcesRelease the connection, statement, and result set

Let’s walk through each step in detail.


Step 1 — Register the Driver

A JDBC driver is a vendor-supplied JAR (e.g. mysql-connector-j-8.x.x.jar) that translates JDBC calls into the wire protocol your database speaks. Before JDBC 4.0 (Java 6) you had to load the driver class explicitly:

// Old style — required before JDBC 4.0 / Java 6
Class.forName("com.mysql.cj.jdbc.Driver");

Since JDBC 4.0, drivers register themselves automatically via java.util.ServiceLoader. As long as the driver JAR is on the classpath, you do not need Class.forName() at all. Modern code skips this step entirely.

Tip: If you use Maven or Gradle, just add the driver as a dependency and JDBC auto-discovery does the rest. See JDBC Drivers for how driver types work under the hood.


Step 2 — Open a Connection

Call DriverManager.getConnection() with three things: the JDBC URL, a username, and a password.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectDemo {
    public static void main(String[] args) throws SQLException {
        String url  = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
        String user = "root";
        String pass = "secret";

        Connection conn = DriverManager.getConnection(url, user, pass);
        System.out.println("Connected! Catalog: " + conn.getCatalog());
        conn.close();
    }
}

Output:

Connected! Catalog: mydb

The URL format is always jdbc:<subprotocol>://<host>:<port>/<database>[?options]. Every database vendor defines its own subprotocol:

DatabaseExample URL
MySQLjdbc:mysql://localhost:3306/mydb
PostgreSQLjdbc:postgresql://localhost:5432/mydb
Oraclejdbc:oracle:thin:@localhost:1521:mydb
SQLitejdbc:sqlite:/path/to/file.db
H2 (in-memory)jdbc:h2:mem:testdb

Warning: Never hard-code credentials in source code. Load them from environment variables or a properties file instead. Anyone who can read your .java or .class file will see them.


Step 3 — Create a Statement

With a live Connection, you create a Statement to carry your SQL to the database.

import java.sql.Statement;

Statement stmt = conn.createStatement();

For anything that accepts user-supplied data, use PreparedStatement instead — it prevents SQL injection and often performs better because the database can cache the query plan:

import java.sql.PreparedStatement;

PreparedStatement ps = conn.prepareStatement(
    "SELECT id, name FROM users WHERE age > ?");
ps.setInt(1, 18);

Note: Statement is fine for one-off administrative queries where no user input is involved. For everything else, always prefer PreparedStatement. See Statement for a full comparison.


Step 4 — Execute SQL and Process Results

Statement offers three execution methods depending on what you need:

MethodUse when…Returns
executeQuery(sql)SELECT statementsResultSet
executeUpdate(sql)INSERT / UPDATE / DELETE / DDLint (rows affected)
execute(sql)Unknown at compile timeboolean

Reading rows with ResultSet

import java.sql.ResultSet;

ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM users");

while (rs.next()) {
    int    id   = rs.getInt("id");
    String name = rs.getString("name");
    int    age  = rs.getInt("age");
    System.out.printf("%d | %-15s | %d%n", id, name, age);
}

Output:

1 | Alice           | 25
2 | Bob             | 30
3 | Carol           | 22

rs.next() advances the internal cursor one row forward and returns false when no more rows remain. You read column values either by name (rs.getString("name")) or by 1-based index (rs.getString(2)). Column names are generally clearer and safer when the query might change.

Writing data

int rowsAffected = stmt.executeUpdate(
    "INSERT INTO users (name, age) VALUES ('Dave', 28)");
System.out.println("Inserted " + rowsAffected + " row(s).");

Output:

Inserted 1 row(s).

Step 5 — Close Resources

Open database connections hold server-side resources (memory, file handles, network sockets). Always close ResultSet, Statement, and Connection when you are done — in that order, innermost first.

The cleanest approach is try-with-resources, which closes everything automatically even if an exception is thrown:

import java.sql.*;

public class FullExample {
    public static void main(String[] args) throws SQLException {
        String url  = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
        String user = "root";
        String pass = "secret";

        try (Connection conn = DriverManager.getConnection(url, user, pass);
             Statement  stmt = conn.createStatement();
             ResultSet  rs   = stmt.executeQuery("SELECT id, name FROM users")) {

            while (rs.next()) {
                System.out.println(rs.getInt("id") + " — " + rs.getString("name"));
            }
        }
        // conn, stmt, rs are all closed automatically here
    }
}

Warning: Forgetting to close a Connection is one of the most common JDBC bugs. Over time, unclosed connections exhaust the database’s connection limit and crash your application. Try-with-resources makes this impossible to forget.


Putting It All Together — A Complete Working Example

Here is a self-contained program that creates a table, inserts rows, queries them, and cleans up — all five steps in one place:

import java.sql.*;

public class JdbcStepsDemo {
    static final String URL  = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
    static final String USER = "root";
    static final String PASS = "secret";

    public static void main(String[] args) throws SQLException {
        // Steps 2–5 handled via try-with-resources
        try (Connection conn = DriverManager.getConnection(URL, USER, PASS)) {

            // DDL — create table if it doesn't exist
            try (Statement stmt = conn.createStatement()) {
                stmt.executeUpdate(
                    "CREATE TABLE IF NOT EXISTS products (" +
                    "  id   INT AUTO_INCREMENT PRIMARY KEY," +
                    "  name VARCHAR(100) NOT NULL," +
                    "  price DECIMAL(10,2) NOT NULL" +
                    ")");
            }

            // INSERT with PreparedStatement (Step 3 + 4)
            String insertSql = "INSERT INTO products (name, price) VALUES (?, ?)";
            try (PreparedStatement ps = conn.prepareStatement(insertSql)) {
                ps.setString(1, "Widget");
                ps.setDouble(2, 9.99);
                ps.executeUpdate();

                ps.setString(1, "Gadget");
                ps.setDouble(2, 24.95);
                ps.executeUpdate();
            }

            // SELECT and print results (Step 4)
            try (Statement stmt = conn.createStatement();
                 ResultSet rs   = stmt.executeQuery(
                     "SELECT id, name, price FROM products ORDER BY id")) {

                System.out.printf("%-5s %-15s %s%n", "ID", "Name", "Price");
                System.out.println("-".repeat(30));
                while (rs.next()) {
                    System.out.printf("%-5d %-15s %.2f%n",
                        rs.getInt("id"),
                        rs.getString("name"),
                        rs.getDouble("price"));
                }
            }
        }
    }
}

Output:

ID    Name            Price
------------------------------
1     Widget          9.99
2     Gadget          24.95

Under the Hood

When DriverManager.getConnection() returns, a real TCP socket (or local pipe on some databases) is open between your JVM and the database server. That socket stays alive until you call conn.close().

Auto-commit is enabled by default, meaning each executeUpdate() is immediately committed as its own transaction. If you need multi-statement atomicity, call conn.setAutoCommit(false) before your statements and then conn.commit() or conn.rollback(). See Transaction Management for details.

Connection pooling — In real applications, opening a new TCP connection for every request is too slow (typically 50–200 ms). A connection pool (HikariCP is the industry standard) maintains a warm pool of pre-opened connections and lends them out on demand. From your code’s perspective you still call getConnection() and close the connection when done; the pool just recycles the underlying socket rather than closing it.

Fetch size — By default, some drivers fetch all rows from the server into client memory at once. For large result sets, call stmt.setFetchSize(50) to stream rows in batches, keeping memory usage flat.


Common Errors and Fixes

Error messageLikely causeFix
No suitable driver found for jdbc:...Driver JAR not on classpathAdd the driver dependency to Maven/Gradle
Communications link failureWrong host/port or database not runningCheck the URL and that the DB server is up
Access denied for user 'root'@'localhost'Wrong credentialsDouble-check username and password
Unknown database 'mydb'Database does not existCreate it: CREATE DATABASE mydb;
Too many connectionsConnections not closedUse try-with-resources; add a connection pool

  • JDBC Drivers — Understand the four driver types and how JDBC auto-discovery works before you connect.
  • DriverManager — Deep dive into how DriverManager.getConnection() finds the right driver and when to switch to DataSource.
  • PreparedStatement — The right way to pass parameters to SQL — safer and faster than plain Statement.
  • Transaction Management — Control commit, rollback, and savepoints to keep multi-step operations atomic.
  • Connection Interface — Everything a Connection object can do beyond just running queries.
  • Connecting to MySQL — Step-by-step guide to setting up a real MySQL database and running your first query against it.
Last updated June 13, 2026
Was this helpful?