Going Deeper (Modern & Professional Java)Pro· 45 min read

JDBC — Connecting Java to a Database

Use JDBC to connect Java to a real database and run safe queries with PreparedStatement, the foundation of data-driven apps.

What you will learn

  • Connect to a database with JDBC
  • Run an insert and a query safely with PreparedStatement
  • Read results from a ResultSet

Letting Java talk to a database

Files are fine for small data, but real applications store information in a database — software built to hold, search and update large amounts of structured data reliably. JDBC (Java Database Connectivity) is the standard built-in way for Java to talk to a database such as MySQL, PostgreSQL or SQLite.

The flow is always the same: connect, send a command (insert, query, update), read any results, then close the connection. We will use a users table with columns id, name and age as our example.

Connecting

You connect with DriverManager.getConnection(...), giving it a connection URL (which database and where), plus a username and password. Like file work, JDBC can throw a checked SQLException, so it lives in try / catch. We again use try-with-resources so the connection closes automatically.

Opening a database connection with JDBC
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/mydb";
        try (Connection conn = DriverManager.getConnection(url, "root", "password")) {
            System.out.println("Connected to the database!");
        } catch (SQLException e) {
            System.out.println("Connection failed: " + e.getMessage());
        }
    }
}

Note: Output: Connected to the database! The URL jdbc:mysql://localhost:3306/mydb says: use JDBC, talk to MySQL, on this computer (localhost) at port 3306, and use the database named mydb. If the details are right and the database is running, the connection opens — and closes itself at the end because it was declared in the try ( ... ) brackets.

Inserting data safely with PreparedStatement

To run SQL, we use a PreparedStatement — a command with placeholders written as ?. We fill those placeholders with real values afterwards. This is the safe way: it stops a dangerous attack called SQL injection, where a user types sneaky SQL into an input to break or read your database. With placeholders, user input can never be mistaken for SQL commands.

Inserting a row with a safe PreparedStatement
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/mydb";
        String sql = "INSERT INTO users (name, age) VALUES (?, ?)";

        try (Connection conn = DriverManager.getConnection(url, "root", "password");
             PreparedStatement stmt = conn.prepareStatement(sql)) {

            stmt.setString(1, "Asha");   // fill the first ?
            stmt.setInt(2, 25);          // fill the second ?
            int rows = stmt.executeUpdate();

            System.out.println("Rows inserted: " + rows);
        } catch (SQLException e) {
            System.out.println("Insert failed: " + e.getMessage());
        }
    }
}

Note: Output: Rows inserted: 1 The ? placeholders were filled by setString(1, "Asha") and setInt(2, 25) — the numbers are the placeholder positions, counting from 1. executeUpdate() runs commands that change data (INSERT, UPDATE, DELETE) and returns how many rows changed: here, 1. Because the values went in through set... calls, not by gluing text into the SQL, the query is safe from SQL injection.

Watch out: Never build SQL by joining strings with user input, like "... VALUES ('" + name + "')". A malicious name could inject extra SQL and damage or steal your data. Always use ? placeholders and the set... methods — it is both safer and cleaner.

Querying and reading results

To read data we use executeQuery(), which returns a ResultSet — a cursor over the rows that match. We call next() to step to each row (it returns false when there are no more), and getter methods like getString and getInt to read each column by name.

Querying rows and reading them from a ResultSet
import java.sql.*;

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/mydb";
        String sql = "SELECT name, age FROM users WHERE age >= ?";

        try (Connection conn = DriverManager.getConnection(url, "root", "password");
             PreparedStatement stmt = conn.prepareStatement(sql)) {

            stmt.setInt(1, 18);                    // adults only
            ResultSet rs = stmt.executeQuery();

            while (rs.next()) {                    // step through each row
                String name = rs.getString("name");
                int age = rs.getInt("age");
                System.out.println(name + " is " + age);
            }
        } catch (SQLException e) {
            System.out.println("Query failed: " + e.getMessage());
        }
    }
}

Note: Output (with two matching rows in the table): Asha is 25 Ravi is 30 The query asked for everyone aged 18 or more. executeQuery() gave back a ResultSet, and the while loop called rs.next() to move to each row in turn, reading the name and age by column name. When no rows are left, next() returns false and the loop ends.

The JDBC steps in order

  1. Connect with DriverManager.getConnection(url, user, password), inside try-with-resources.
  2. Prepare a statement with ? placeholders via conn.prepareStatement(sql).
  3. Fill the placeholders with setString, setInt, and so on (positions count from 1).
  4. Run it: executeUpdate() for INSERT/UPDATE/DELETE, or executeQuery() for SELECT.
  5. Read a SELECT result by looping while (rs.next()) and using getString / getInt per column.
  6. Closing is automatic — try-with-resources closes the statement and connection for you.

Tip: In bigger apps, database code is usually tucked into a DAO (Data Access Object) — a class whose only job is to read and write one kind of data (for example a UserDao with save(user) and findById(id)). It keeps the messy SQL in one place, away from the rest of your program.

Q. Why use a PreparedStatement with ? placeholders instead of building SQL by joining strings?

Answer: Placeholders mean user input is treated strictly as data, never as part of the SQL command, which blocks SQL injection. Joining strings with user input is unsafe and a common security hole.

✍️ Practice

  1. Write the JDBC code to insert one product (name and price) into a products table using a PreparedStatement.
  2. Write a query that selects all products under a given price and prints each name and price from the ResultSet.

🏠 Homework

  1. Sketch (or build, if you have a database handy) a UserDao class with two methods: addUser(name, age) using executeUpdate, and listAdults() that queries and prints everyone aged 18 or older. Explain in 3 to 4 lines why PreparedStatement is safer than string-built SQL.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →