Connecting to MySQL
Connecting Java to MySQL is one of the most practical things you can learn with JDBC. In just a few steps — add the driver JAR, craft a connection URL, and call DriverManager.getConnection() — you have a live link from your Java code to a running MySQL server.
Prerequisites
Before writing any Java code, make sure you have:
- A running MySQL server (local install, Docker, or a cloud instance)
- A database, a table, and a user account with the right privileges
- Java 11+ and Maven or Gradle (or a plain JAR on the classpath)
If you want a zero-install sandbox, spin up MySQL in Docker:
docker run --name mysql-demo -e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=mydb -p 3306:3306 -d mysql:8
Step 1 — Add the MySQL JDBC Driver
MySQL’s official JDBC driver is MySQL Connector/J. It is a pure Type 4 driver (pure Java, no native libraries required — see JDBC Drivers for background on driver types).
Maven
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>
Gradle
implementation 'com.mysql:mysql-connector-j:8.3.0'
Plain JAR (no build tool)
Download mysql-connector-j-8.x.x.jar from MySQL Downloads and add it to your classpath:
javac -cp mysql-connector-j-8.3.0.jar MyApp.java
java -cp .:mysql-connector-j-8.3.0.jar MyApp
Note: The group ID changed from
mysql:mysql-connector-javatocom.mysql:mysql-connector-jin version 8.0.31. Use the new artifact ID for any dependency added from 2022 onwards.
Step 2 — Understand the JDBC URL Format
The JDBC URL tells DriverManager which driver to use, which host to connect to, and which database to open. For MySQL it looks like this:
jdbc:mysql://<host>:<port>/<database>?<key>=<value>&<key>=<value>
| Part | Example | Meaning |
|---|---|---|
jdbc:mysql:// | jdbc:mysql:// | Selects the MySQL driver |
<host> | localhost | MySQL server hostname or IP |
<port> | 3306 | MySQL port (default 3306) |
<database> | mydb | Database (schema) to use |
| Query params | useSSL=false | Driver behaviour flags |
Common query parameters you will encounter:
| Parameter | Default | When to use |
|---|---|---|
useSSL=false | true (8.x) | Disable SSL in local dev to silence warnings |
allowPublicKeyRetrieval=true | false | Required by some auth plugins in dev |
serverTimezone=UTC | JVM TZ | Prevents timezone mismatch errors |
characterEncoding=UTF-8 | — | Enforces UTF-8 for text columns |
autoReconnect=true | false | Reconnects after a dropped connection |
A realistic development URL:
jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
Warning: Never disable SSL in production. Use
useSSL=trueand pointtrustCertificateKeyStoreUrlat your CA certificate.
Step 3 — Open a Connection
Since JDBC 4.0 (Java 6), you do not need to call Class.forName(). The driver registers itself automatically via Java’s ServiceLoader mechanism as long as the JAR is on the classpath.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class MySQLConnect {
private static final String URL =
"jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "secret";
public static void main(String[] args) {
try (Connection conn = DriverManager.getConnection(URL, USER, PASS)) {
System.out.println("Connected to: " + conn.getCatalog());
System.out.println("Driver: " + conn.getMetaData().getDriverName());
System.out.println("MySQL version: " +
conn.getMetaData().getDatabaseProductVersion());
} catch (SQLException e) {
System.err.println("Connection failed: " + e.getMessage());
}
}
}
Output:
Connected to: mydb
Driver: MySQL Connector/J
MySQL version: 8.0.36
Tip: Always wrap
Connection,Statement, andResultSetin try-with-resources. They all implementAutoCloseable, so Java closes them for you even if an exception is thrown — preventing connection leaks.
Step 4 — Create a Table and Insert Data
Let us create a products table and populate it so we have data to query.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class SetupDatabase {
private static final String URL =
"jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
public static void main(String[] args) throws SQLException {
try (Connection conn = DriverManager.getConnection(URL, "root", "secret");
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
)
""");
stmt.executeUpdate(
"INSERT INTO products (name, price) VALUES ('Keyboard', 79.99)");
stmt.executeUpdate(
"INSERT INTO products (name, price) VALUES ('Mouse', 39.99)");
stmt.executeUpdate(
"INSERT INTO products (name, price) VALUES ('Monitor', 349.00)");
System.out.println("Table created and seeded.");
}
}
}
Output:
Table created and seeded.
Note: Text blocks (
"""...""") require Java 15+. For older Java, use a plainStringwith\nescapes or concatenation.
Step 5 — Query Data Safely with PreparedStatement
Use PreparedStatement for any query that includes user-supplied values. It prevents SQL injection by sending the SQL template and the parameters separately — the database never combines them into a raw string.
import java.sql.*;
public class QueryProducts {
private static final String URL =
"jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
public static void main(String[] args) throws SQLException {
String search = "M"; // find products whose name starts with 'M'
String sql = "SELECT id, name, price FROM products WHERE name LIKE ? ORDER BY price";
try (Connection conn = DriverManager.getConnection(URL, "root", "secret");
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, search + "%"); // bind the parameter safely
try (ResultSet rs = ps.executeQuery()) {
System.out.printf("%-5s %-20s %10s%n", "ID", "Name", "Price");
System.out.println("-".repeat(38));
while (rs.next()) {
System.out.printf("%-5d %-20s %10.2f%n",
rs.getInt("id"),
rs.getString("name"),
rs.getDouble("price"));
}
}
}
}
}
Output:
ID Name Price
--------------------------------------
2 Mouse 39.99
3 Monitor 349.00
Step 6 — Insert with Generated Keys
When you insert a row with an AUTO_INCREMENT primary key, you can retrieve the generated ID immediately:
String sql = "INSERT INTO products (name, price) VALUES (?, ?)";
try (Connection conn = DriverManager.getConnection(URL, "root", "secret");
PreparedStatement ps = conn.prepareStatement(
sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, "Webcam");
ps.setDouble(2, 89.99);
ps.executeUpdate();
try (ResultSet keys = ps.getGeneratedKeys()) {
if (keys.next()) {
System.out.println("New product ID: " + keys.getLong(1));
}
}
}
Output:
New product ID: 4
Step 7 — Use Transactions for Multi-Step Operations
By default, JDBC runs in auto-commit mode — every statement commits immediately. For operations that must succeed or fail together, disable auto-commit and manage the transaction yourself. See Transaction Management for the full story.
try (Connection conn = DriverManager.getConnection(URL, "root", "secret")) {
conn.setAutoCommit(false); // begin transaction
try (PreparedStatement ps1 = conn.prepareStatement(
"UPDATE accounts SET balance = balance - ? WHERE id = ?");
PreparedStatement ps2 = conn.prepareStatement(
"UPDATE accounts SET balance = balance + ? WHERE id = ?")) {
ps1.setBigDecimal(1, new java.math.BigDecimal("100.00"));
ps1.setInt(2, 1);
ps1.executeUpdate();
ps2.setBigDecimal(1, new java.math.BigDecimal("100.00"));
ps2.setInt(2, 2);
ps2.executeUpdate();
conn.commit(); // both updates succeed — commit
System.out.println("Transfer complete.");
} catch (SQLException e) {
conn.rollback(); // one update failed — undo everything
throw e;
}
}
Common Errors and Fixes
| Error message | Likely cause | Fix |
|---|---|---|
Communications link failure | MySQL not running or wrong host/port | Check server status and URL |
Access denied for user 'root'@'localhost' | Wrong username or password | Verify credentials with mysql -u root -p |
Unknown database 'mydb' | Database does not exist | Run CREATE DATABASE mydb; in MySQL |
Public Key Retrieval is not allowed | caching_sha2_password auth plugin | Add allowPublicKeyRetrieval=true to URL |
The server time zone value is unrecognized | JVM/MySQL TZ mismatch | Add serverTimezone=UTC to URL |
No suitable driver found | Driver JAR not on classpath | Verify the dependency or JAR path |
Under the Hood
When DriverManager.getConnection(url, user, pass) is called:
- ServiceLoader scan — At JVM startup the
DriverManagerclass triggers aServiceLoaderscan of all JARs on the classpath forMETA-INF/services/java.sql.Driver. MySQL Connector/J registerscom.mysql.cj.jdbc.Driverthis way. - URL routing —
DriverManagercallsdriver.acceptsURL(url)on each registered driver. The MySQL driver returnstrueonly for URLs beginning withjdbc:mysql://(and its variants likejdbc:mysql+srv://for DNS SRV). - TCP handshake — The driver opens a TCP socket to
host:port, performs the MySQL protocol handshake (capability negotiation, authentication — SHA-256 or caching_sha2_password in MySQL 8), and selects the target database. - Connection object — A
ConnectionImplis returned, wrapping the socket, a send/receive buffer, and the session state (current database, charset, transaction isolation). - Statement execution — When you call
ps.executeQuery(), the driver serialises the prepared statement handle and bound parameters into the MySQL binary protocol packet, writes it to the socket buffer, and blocks for a response.
Every ResultSet row is network data decoded on demand. The default fetch size is 0 (fetch all rows at once). For large result sets call ps.setFetchSize(Integer.MIN_VALUE) to stream rows one at a time and avoid out-of-memory errors.
Tip: In any real application, replace
DriverManager.getConnection()with a HikariCP connection pool. HikariCP keeps a pool of pre-authenticated TCP connections and hands them out in microseconds, compared to the 5–50 ms cost of opening a fresh socket per request.
<!-- HikariCP Maven dependency -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.1.0</version>
</dependency>
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC");
config.setUsername("root");
config.setPassword("secret");
config.setMaximumPoolSize(10);
HikariDataSource ds = new HikariDataSource(config);
try (Connection conn = ds.getConnection()) {
// use conn exactly as before
}
Related Topics
- JDBC Overview — understand the full JDBC picture before diving into MySQL specifics
- JDBC Drivers — learn the four driver types and why MySQL Connector/J is a Type 4 driver
- Steps to Connect a Database — a generic, step-by-step walkthrough of the JDBC connection process
- PreparedStatement — the safest and most performant way to execute parameterised SQL
- Transaction Management — commit, rollback, savepoints, and isolation levels
- ResultSet — navigate and read rows returned by your queries