JDBC sits between your application code and a database driver. Four key objects are involved in every database interaction:

ObjectRole
DriverManagerBootstrap class — creates Connection from a JDBC URL
ConnectionSession with the database; wraps a TCP connection
Statement / PreparedStatementSends SQL to the server; PreparedStatement uses bind parameters
ResultSetCursor over query results; advance with next()

The driver is discovered automatically via the ServiceLoader mechanism since JDBC 4.0 — no Class.forName() call needed.

Add the JDBC driver for your database. PostgreSQL is used throughout this article:

<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.7.3</version> </dependency>

For H2 in-memory testing: com.h2database:h2:2.2.224.

DriverManager.getConnection(url, user, password) returns a Connection. Always wrap in try-with-resources so the connection is closed even if an exception occurs:

import java.sql.*; public class JdbcDemo { private static final String URL = "jdbc:postgresql://localhost:5432/mydb"; private static final String USER = "myuser"; private static final String PASS = "secret"; public static void main(String[] args) throws SQLException { // try-with-resources auto-closes the Connection try (Connection conn = DriverManager.getConnection(URL, USER, PASS)) { System.out.println("Connected: " + conn.getMetaData().getDatabaseProductName()); } } } For production code use a connection pool (HikariCP) instead of DriverManager directly. DriverManager creates a new TCP connection on every call.

Statement is fine for SQL with no user-supplied parameters. Use executeQuery() for SELECT, executeUpdate() for INSERT/UPDATE/DELETE:

try (Connection conn = DriverManager.getConnection(URL, USER, PASS); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT id, name, email FROM users")) { while (rs.next()) { long id = rs.getLong("id"); String name = rs.getString("name"); String email = rs.getString("email"); System.out.printf("%-4d %-20s %s%n", id, name, email); } }

Key ResultSet getter methods:

SQL TypeJava Getter
INTEGER / BIGINTgetInt(col) / getLong(col)
VARCHAR / TEXTgetString(col)
NUMERIC / DECIMALgetBigDecimal(col)
BOOLEANgetBoolean(col)
DATE / TIMESTAMPgetDate(col) / getTimestamp(col)
BLOB / BYTEAgetBytes(col) / getBlob(col)

executeUpdate() returns the number of affected rows:

try (Connection conn = DriverManager.getConnection(URL, USER, PASS); Statement stmt = conn.createStatement()) { // INSERT int inserted = stmt.executeUpdate( "INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')"); System.out.println("Rows inserted: " + inserted); // UPDATE int updated = stmt.executeUpdate( "UPDATE users SET email = 'alice2@example.com' WHERE name = 'Alice'"); System.out.println("Rows updated: " + updated); // DELETE int deleted = stmt.executeUpdate( "DELETE FROM users WHERE name = 'Alice'"); System.out.println("Rows deleted: " + deleted); } Never concatenate user input into SQL strings passed to Statement. This opens SQL injection attacks. Use PreparedStatement with bind parameters for any user-supplied value.

Pass Statement.RETURN_GENERATED_KEYS to get the database-assigned primary key after an INSERT:

try (Connection conn = DriverManager.getConnection(URL, USER, PASS); Statement stmt = conn.createStatement()) { stmt.executeUpdate( "INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com')", Statement.RETURN_GENERATED_KEYS); try (ResultSet keys = stmt.getGeneratedKeys()) { if (keys.next()) { long newId = keys.getLong(1); System.out.println("Generated ID: " + newId); } } }

DatabaseMetaData exposes driver version, supported features, and schema information without writing SQL:

try (Connection conn = DriverManager.getConnection(URL, USER, PASS)) { DatabaseMetaData meta = conn.getMetaData(); System.out.println("DB: " + meta.getDatabaseProductName() + " " + meta.getDatabaseProductVersion()); System.out.println("Driver: " + meta.getDriverName() + " " + meta.getDriverVersion()); System.out.println("Max conn:" + meta.getMaxConnections()); // List all tables in the public schema try (ResultSet tables = meta.getTables(null, "public", "%", new String[]{"TABLE"})) { while (tables.next()) { System.out.println(" Table: " + tables.getString("TABLE_NAME")); } } }

Primitive getters like getInt() return 0 when the column is NULL — call wasNull() immediately after to distinguish zero from NULL:

int score = rs.getInt("score"); boolean isNull = rs.wasNull(); // true if column was SQL NULL // Safer: use getObject() for nullable columns Integer scoreBoxed = (Integer) rs.getObject("score"); // null if SQL NULL
TaskAPI
Open connectionDriverManager.getConnection(url, user, pass)
Static SQL (no params)conn.createStatement()
Parameterised SQLconn.prepareStatement(sql)
SELECTstmt.executeQuery(sql)ResultSet
DML (INSERT/UPDATE/DELETE)stmt.executeUpdate(sql) → row count
Any SQLstmt.execute(sql) → boolean (true = ResultSet)
Auto-generated keysstmt.getGeneratedKeys()
DB metadataconn.getMetaData()
Resource cleanuptry-with-resources on Connection, Statement, ResultSet