Conectar Java con MySQL
Java es uno de los lenguajes más utilizados en el desarrollo empresarial, y MySQL es una de las bases de datos más populares del mundo. JDBC (Java Database Connectivity) es la API estándar de Java para conectarse a bases de datos relacionales, y MySQL Connector/J es el driver oficial que implementa esta API para MySQL. En este artículo aprenderás a configurar la dependencia, establecer conexiones, manejar pools de conexiones y estructurar tu código para aplicaciones robustas.
Requisitos previos
Necesitas Java 11 o superior (se recomienda Java 17+), un servidor MySQL en ejecución y un gestor de dependencias como Maven o Gradle. Crea una base de datos de prueba antes de comenzar:
CREATE DATABASE IF NOT EXISTS tienda
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;Instalación
Maven
Agrega la dependencia de MySQL Connector/J en tu pom.xml:
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>Gradle
En tu build.gradle:
dependencies {
implementation 'com.mysql:mysql-connector-j:8.3.0'
}Si no usas un gestor de dependencias, puedes descargar el JAR desde el sitio oficial de MySQL y agregarlo al classpath manualmente.
Codigo completo
Este ejemplo establece una conexion basica con MySQL y verifica que funcione:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConectarMySQL {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/tienda?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8";
String usuario = "root";
String contraseña = "tu_contraseña";
try (Connection conn = DriverManager.getConnection(url, usuario, contraseña)) {
System.out.println("Conexion exitosa a MySQL");
System.out.println("Base de datos: " + conn.getCatalog());
System.out.println("Driver: " + conn.getMetaData().getDriverName());
System.out.println("Version del driver: " + conn.getMetaData().getDriverVersion());
System.out.println("Version de MySQL: " + conn.getMetaData().getDatabaseProductVersion());
} catch (SQLException e) {
System.err.println("Error de conexion: " + e.getMessage());
System.err.println("Codigo SQL: " + e.getSQLState());
System.err.println("Codigo de error: " + e.getErrorCode());
}
}
}Salida esperada:
Conexion exitosa a MySQL
Base de datos: tienda
Driver: MySQL Connector/J
Version del driver: mysql-connector-j-8.3.0
Version de MySQL: 8.0.36
Explicacion paso a paso
La clase DriverManager es el punto de entrada para crear conexiones JDBC. A partir de JDBC 4.0 (Java 6+), el driver se carga automaticamente desde el classpath gracias al mecanismo de Service Provider, por lo que ya no es necesario llamar a Class.forName("com.mysql.cj.jdbc.Driver").
La URL de conexion JDBC para MySQL sigue el formato jdbc:mysql://host:puerto/basedatos?parametros. Los parametros mas importantes son:
// URL con parametros comunes
String url = "jdbc:mysql://localhost:3306/tienda"
+ "?useSSL=false" // Desactivar SSL para desarrollo local
+ "&serverTimezone=UTC" // Zona horaria del servidor
+ "&characterEncoding=UTF-8" // Codificacion de caracteres
+ "&useUnicode=true" // Soporte Unicode
+ "&allowPublicKeyRetrieval=true" // Permitir recuperacion de clave publica
+ "&connectTimeout=5000" // Timeout de conexion en ms
+ "&socketTimeout=30000"; // Timeout de socket en msEl bloque try-with-resources garantiza que la conexion se cierre automaticamente al salir del bloque, incluso si ocurre una excepcion. Esto es fundamental para evitar fugas de conexiones.
Conexion con Properties
Para mayor flexibilidad, puedes pasar las propiedades de conexion como un objeto Properties:
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "tu_contraseña");
props.setProperty("useSSL", "false");
props.setProperty("serverTimezone", "UTC");
props.setProperty("characterEncoding", "UTF-8");
props.setProperty("connectTimeout", "5000");
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/tienda", props)) {
System.out.println("Conectado con Properties");
}Archivo de configuracion externo
En aplicaciones reales, las credenciales nunca deben estar en el codigo fuente. Crea un archivo db.properties:
db.url=jdbc:mysql://localhost:3306/tienda?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
db.user=root
db.password=tu_contraseñaY cargalo desde Java:
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class ConfigDB {
private static final Properties props = new Properties();
static {
try (FileInputStream fis = new FileInputStream("db.properties")) {
props.load(fis);
} catch (IOException e) {
throw new RuntimeException("No se pudo cargar db.properties", e);
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(
props.getProperty("db.url"),
props.getProperty("db.user"),
props.getProperty("db.password")
);
}
}Pool de conexiones con HikariCP
En aplicaciones con multiples hilos o servidores web, crear una conexion por cada operacion es ineficiente. Un pool de conexiones mantiene conexiones abiertas y las reutiliza. HikariCP es el pool de conexiones mas rapido y confiable para Java.
Agrega la dependencia de HikariCP:
<!-- Maven -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.1.0</version>
</dependency>Configura y usa el pool:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class PoolConexiones {
private static final HikariDataSource dataSource;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/tienda?useSSL=false&serverTimezone=UTC");
config.setUsername("root");
config.setPassword("tu_contraseña");
// Configuracion del pool
config.setMaximumPoolSize(10); // Maximo de conexiones simultaneas
config.setMinimumIdle(5); // Minimo de conexiones inactivas
config.setIdleTimeout(300000); // Tiempo maximo inactivo: 5 minutos
config.setConnectionTimeout(10000); // Timeout para obtener conexion: 10 segundos
config.setMaxLifetime(1800000); // Vida maxima de conexion: 30 minutos
// Propiedades de MySQL
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.addDataSourceProperty("useServerPrepStmts", "true");
dataSource = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void shutdown() {
dataSource.close();
}
}Con HikariCP, cada llamada a getConnection() obtiene una conexion del pool en lugar de crear una nueva, lo que reduce drasticamente la latencia. Al cerrar la conexion con try-with-resources, esta se devuelve al pool en lugar de destruirse.
Caso practico
Veamos una clase de acceso a datos completa que encapsula la gestion de conexiones y puede usarse en cualquier aplicacion Java:
import java.sql.*;
import java.util.Properties;
import java.io.FileInputStream;
import java.io.IOException;
public class BaseDatos {
private static BaseDatos instancia;
private final String url;
private final String usuario;
private final String contraseña;
private BaseDatos() {
Properties props = new Properties();
try (FileInputStream fis = new FileInputStream("db.properties")) {
props.load(fis);
} catch (IOException e) {
throw new RuntimeException("Error cargando configuracion: " + e.getMessage());
}
this.url = props.getProperty("db.url");
this.usuario = props.getProperty("db.user");
this.contraseña = props.getProperty("db.password");
// Verificar conexion al inicializar
try (Connection conn = getConnection()) {
System.out.println("Base de datos inicializada: " + conn.getCatalog());
} catch (SQLException e) {
throw new RuntimeException("No se pudo conectar a la base de datos: " + e.getMessage());
}
}
public static synchronized BaseDatos getInstancia() {
if (instancia == null) {
instancia = new BaseDatos();
}
return instancia;
}
public Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, usuario, contraseña);
}
public void ejecutarConsulta(String sql, ConsumidorResultSet consumidor, Object... params)
throws SQLException {
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
consumidor.aceptar(rs);
}
}
}
}
public int ejecutarActualizacion(String sql, Object... params) throws SQLException {
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
return pstmt.executeUpdate();
}
}
@FunctionalInterface
public interface ConsumidorResultSet {
void aceptar(ResultSet rs) throws SQLException;
}
}Uso de la clase:
public class Main {
public static void main(String[] args) {
BaseDatos db = BaseDatos.getInstancia();
try {
// Consulta
db.ejecutarConsulta(
"SELECT nombre, precio FROM productos WHERE categoria = ?",
rs -> System.out.printf(" %s - $%.2f%n", rs.getString("nombre"), rs.getDouble("precio")),
"Perifericos"
);
// Actualizacion
int filas = db.ejecutarActualizacion(
"UPDATE productos SET stock = stock + ? WHERE id = ?", 10, 1
);
System.out.println("Filas actualizadas: " + filas);
} catch (SQLException e) {
System.err.println("Error: " + e.getMessage());
}
}
}Manejo de errores
Los errores de conexion mas comunes y como identificarlos:
public class ManejadorErrores {
public static void diagnosticar(SQLException e) {
switch (e.getErrorCode()) {
case 1045:
System.err.println("Credenciales incorrectas. Verifica usuario y contraseña");
break;
case 1049:
System.err.println("La base de datos no existe. Creala antes de conectar");
break;
case 1044:
System.err.println("Acceso denegado a la base de datos");
break;
case 0:
if (e.getMessage().contains("Communications link failure")) {
System.err.println("No se pudo conectar al servidor MySQL. Verifica que esta corriendo");
} else if (e.getMessage().contains("Connection refused")) {
System.err.println("Conexion rechazada. Verifica host y puerto");
} else {
System.err.println("Error de conexion: " + e.getMessage());
}
break;
default:
System.err.println("Error MySQL [" + e.getErrorCode() + "]: " + e.getMessage());
System.err.println("Estado SQL: " + e.getSQLState());
}
}
}Ahora que tienes una conexion configurada, en el siguiente articulo aprenderas a ejecutar consultas SELECT para recuperar datos de MySQL desde Java.
Escrito por Eduardo Lázaro
