Introduction to JDBC
Introduction to JDBC
JDBC — Java Database Connectivity — is the standard Java API for connecting to, querying, and updating relational databases. It ships as part of the JDK (in the java.sql and javax.sql packages), so you need no third-party dependency just to open a connection. Everything from JPA and Hibernate to Spring Data and jOOQ ultimately calls JDBC under the hood.
This tutorial assumes you are comfortable with Java generics, lambdas, streams, and resource management via try-with-resources. We will not revisit those concepts; we will use them freely as tools.
Why JDBC still matters
You might wonder why you should learn a 1997-era API when higher-level frameworks exist. Two reasons:
- Debugging and tuning. Every ORM exception stack trace eventually bottoms out in JDBC. Understanding the API means you can diagnose slow queries, connection-pool exhaustion, and transaction boundary bugs that look mysterious at the framework level.
- Control. Bulk batch inserts, stored-procedure calls, custom type mappings, and vendor-specific features are often cleaner in raw JDBC than wrestling with ORM annotations.
The four-layer model
Understanding JDBC requires understanding its layered architecture:
- Your application code — you call standard
java.sqlinterfaces such asConnection,Statement, andResultSet. - JDBC API — the interfaces and abstract classes that define the contract (defined in
java.sqlandjavax.sql). - JDBC Driver Manager / DataSource — the bridge that loads the correct driver and hands you a
Connection. - JDBC Driver — vendor-supplied JAR that implements the JDBC interfaces and speaks the actual database protocol (e.g. MySQL wire protocol, PostgreSQL's libpq-style binary protocol).
Because your code only ever touches interfaces defined in layer 2, switching from PostgreSQL to MySQL is — in theory — a driver swap plus a connection-string change, with no changes to your application logic.
Driver types
The JDBC specification originally defined four driver types. In practice you will only encounter Type 4 today:
- Type 1 — JDBC-ODBC bridge (removed in Java 8): translated JDBC calls to ODBC. Long deprecated.
- Type 2 — native-API driver: wraps a database's native C library. Requires native code on the machine.
- Type 3 — network-protocol driver: sends calls to a middleware server. Rarely used.
- Type 4 — pure-Java driver: a pure Java JAR that speaks the database's wire protocol directly. This is what
mysql-connector-j,postgresql,h2, and every modern driver are. Zero native dependencies; just add the JAR to the classpath.
org.postgresql:postgresql; for MySQL it is com.mysql.cj:mysql-connector-j; for H2 (in-memory, great for tests) it is com.h2database:h2.
The key JDBC interfaces
The entire API is built around a handful of interfaces you will use in every JDBC program:
java.sql.Connection— represents a single physical connection to the database. It owns transactions. Always closed in a finally block or try-with-resources.java.sql.Statement— executes a static SQL string. Never use this with user-supplied data (SQL injection risk).java.sql.PreparedStatement— pre-compiles a parameterised SQL string. The correct choice for almost all queries.java.sql.CallableStatement— calls stored procedures.java.sql.ResultSet— a cursor over rows returned by a query. Rows are accessed one at a time withnext().javax.sql.DataSource— a factory forConnectionobjects; the production alternative toDriverManager. Connection pools implement this interface.
A minimal end-to-end sketch
Before diving into each piece in detail over the following lessons, here is the smallest possible JDBC program so you can see how the pieces fit together. It uses H2 in in-memory mode — no external database required:
Output: Hello from JDBC
Notice the nested try-with-resources. Connection, Statement, and ResultSet all implement AutoCloseable, so the JVM closes them in reverse order when each block exits — even on exceptions. Forgetting to close these objects is the most common JDBC resource leak.
Statement with user input. The example above uses a hard-coded string, which is safe. As soon as a user's value appears in an SQL string concatenation you have a SQL injection vulnerability. The PreparedStatement lesson covers the correct approach — but keep this rule in mind from the start.
How the driver is loaded
Since Java 6 you do not need to call Class.forName("org.postgresql.Driver") manually. The JDK uses the ServiceLoader mechanism: every JDBC driver JAR declares itself in META-INF/services/java.sql.Driver. When DriverManager.getConnection(url, ...) is called, it iterates registered drivers, offers each one the URL, and the first driver that recognises the URL prefix (e.g. jdbc:postgresql:) accepts the call and returns a Connection.
JDBC URL anatomy
Every JDBC URL follows the pattern jdbc:<subprotocol>:<subname>. The subname is vendor-specific. Examples:
jdbc:h2:mem:testdb— H2 in-memory database named "testdb"jdbc:postgresql://localhost:5432/mydb— PostgreSQL on localhostjdbc:mysql://localhost:3306/mydb?useSSL=false— MySQL with a query parameterjdbc:sqlserver://host:1433;databaseName=mydb— SQL Server
DriverManager vs DataSource
DriverManager.getConnection() opens a new physical TCP connection every time it is called. In a real application that is unacceptably expensive (each open/close costs ~10–100 ms and a file-descriptor). The solution is a connection pool — a DataSource implementation that keeps a pool of already-open connections and hands them out on request, returning them to the pool on close().
Popular pool libraries — HikariCP, c3p0, DBCP — all implement javax.sql.DataSource, so your DAO code only depends on the interface. We will examine pooling in detail later in this tutorial.
DataSource (connection pool) everywhere. Use DriverManager only in quick standalone scripts, tests, or educational examples like this lesson.
Summary
JDBC is Java's vendor-neutral API for relational database access. Its layered model — your code calls interfaces, the driver implements them — means you can switch databases with minimal code changes. The five types of objects you will manipulate are Connection, Statement, PreparedStatement, ResultSet, and DataSource. The driver is loaded automatically via ServiceLoader; no manual class registration is needed. In production always use a pooled DataSource; DriverManager is for simple scripts and tests. The next lesson digs into how to configure and obtain a connection properly.