Conexión JDBC en Java
Abre y gestiona conexiones a bases de datos en Java con la interfaz Connection — abrir, cerrar y configurar.
Una Connection es una sesión activa con la base de datos. Es el objeto que obtienes de DriverManager (o de un DataSource), y es la fábrica de todo lo demás: sentencias, transacciones, puntos de guardado, metadatos. Una conexión también es un recurso escaso y costoso: cada una abierta ocupa un socket y una sesión del lado del servidor, por lo que la regla cardinal es abrir tarde, cerrar con prontitud.
Este capítulo cubre cómo construir una URL de conexión, las tres formas de abrir una Connection, por qué try-with-resources es innegociable, los parámetros de sesión que puedes ajustar, y los errores que encontrarás cuando algo está mal configurado. Se asume que ya has cargado un driver — consulta Drivers JDBC y la Introducción a JDBC para tener el panorama completo.
La URL de conexión
Todo lo que DriverManager necesita para encontrar y alcanzar una base de datos está codificado en la URL:
jdbc:<subprotocol>://<host>:<port>/<database>?<key=value&...>Por ejemplo jdbc:postgresql://db.internal:5432/shop?ssl=true. El prefijo jdbc: es obligatorio; el subprotocolo selecciona el driver; el resto es específico del proveedor, pero convencionalmente incluye host, puerto, base de datos y una cadena de consulta con opciones de ajuste. Algunas URLs reales que conviene reconocer:
| Base de datos | URL de ejemplo |
|---|---|
| PostgreSQL | jdbc:postgresql://localhost:5432/shop |
| MySQL | jdbc:mysql://localhost:3306/shop?useSSL=true |
| H2 (en memoria) | jdbc:h2:mem:testdb |
| SQLite (archivo) | jdbc:sqlite:/data/shop.db |
El subprotocolo (postgresql, mysql, h2, sqlite) es la forma en que DriverManager decide qué driver registrado debe manejar la URL, por lo que acertarlo es lo que le indica a JDBC qué base de datos se quiere usar.
Tres formas de abrir una conexión
// 1. URL with credentials as arguments
Connection a = DriverManager.getConnection(url, "app", "secret");
// 2. URL with a Properties bag (user, password, plus driver-specific keys)
Properties props = new Properties();
props.setProperty("user", "app");
props.setProperty("password", "secret");
props.setProperty("connectTimeout", "10");
Connection b = DriverManager.getConnection(url, props);
// 3. From a pooled DataSource (preferred for applications)
Connection c = dataSource.getConnection();DriverManager vs. DataSource
DriverManager.getConnection(...) abre una conexión física completamente nueva cada vez, y la destruye cuando la cierras. Ese proceso de enlace — búsqueda DNS, TCP, TLS, autenticación — cuesta decenas de milisegundos, lo cual está bien para un script pero es ruinoso para un servidor que maneja muchas peticiones.
Un DataSource respaldado por un pool de conexiones (HikariCP, Apache DBCP, o el que proporcione tu servidor de aplicaciones) mantiene un conjunto de conexiones físicas abiertas y las reparte. Llamar a getConnection() toma una prestada; llamar a close() la devuelve al pool en lugar de cerrarla realmente. Para cualquier aplicación de larga ejecución, prefiere un DataSource con pool; recurre a DriverManager solo en herramientas pequeñas, pruebas y ejemplos.
Ciérrala siempre — usa try-with-resources
Connection, Statement y ResultSet implementan AutoCloseable. Declararlos en la cabecera de un try-with-resources garantiza que se cierren en orden inverso incluso si se lanza una excepción — el hábito más importante en JDBC:
try (Connection conn = DriverManager.getConnection(url, "app", "secret")) {
// use conn...
} // conn.close() runs here automatically, even on exceptionPerder conexiones (olvidar cerrarlas) agota el pool y eventualmente bloquea toda la aplicación — una interrupción de producción clásica. Ten en cuenta que abrir una Connection lanza una SQLException comprobada, por lo que la llamada siempre vive dentro de un try (o de un método que declara throws SQLException).
Una vez que tienes una conexión, la usas para crear Statements y PreparedStatements — y estos deben estar dentro de la misma cabecera try-with-resources para que se cierren antes que la conexión:
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {
ps.setInt(1, 42);
// ...read the ResultSet...
} // ps closes first, then conn — reverse declaration orderConfigurar una conexión
Una vez abierta, una conexión lleva consigo parámetros de nivel de sesión:
| Método | Qué hace |
|---|---|
setAutoCommit(false) | Inicia una transacción manual; luego debes llamar a commit() o rollback() tú mismo. Consulta Transacciones JDBC. |
setTransactionIsolation(...) | Selecciona un nivel de aislamiento (p. ej. TRANSACTION_READ_COMMITTED). |
setReadOnly(true) | Una pista de que la conexión no realiza escrituras; algunos drivers la optimizan. |
setSchema(...) / setCatalog(...) | Selecciona el espacio de nombres contra el que se ejecutan las consultas. |
isValid(timeout) | Devuelve true si la conexión sigue viva; los pools lo usan para descartar las muertas. |
Estos parámetros son por sesión, por lo que se restablecen cuando una conexión real se cierra — pero con un pool, una conexión prestada puede seguir llevando la configuración que dejó el usuario anterior. Por eso los pools restablecen autoCommit y el aislamiento al devolver la conexión, y por eso debes establecer lo que necesitas en lugar de asumir valores predeterminados.
Errores comunes de conexión
Cuando una conexión falla casi siempre obtienes una SQLException; el mensaje te indica qué capa falló:
No suitable driver found for ...— la clase del driver nunca se cargó, o el subprotocolo de la URL está mal escrito y ningún driver registrado lo reclama. Corrige la dependencia o la URL (consulta Drivers JDBC).Connection refused— nada está escuchando en ese host/puerto: la base de datos está caída, o el host/puerto en la URL es incorrecto.- Autenticación /
password authentication failed—useropasswordincorrectos, o el usuario carece de permisos para esa base de datos. - Tiempo de espera de conexión — el host es inalcanzable (cortafuegos, red incorrecta). Establece
connectTimeoutpara que la llamada falle rápido en lugar de quedarse colgada.
Un ejemplo completo: la anatomía de una URL de conexión
Este programa descompone una URL JDBC realista en sus componentes y construye el contenedor Properties que pasarías junto a ella — las dos entradas que necesita getConnection — sin requerir una base de datos activa.
Lo que se puede extraer de la ejecución:
- La URL no es opaca — es un dato estructurado.
getConnectionanaliza exactamente estas piezas: el subprotocolo elige el driver, y el host/puerto/base de datos le indican a ese driver dónde conectarse. Leer una URL en voz alta es la forma más rápida de depurar errores de "base de datos incorrecta". - La cadena de consulta (
?ssl=true&applicationName=reports) lleva opciones específicas del driver. Los mismos parámetros pueden viajar en la URL o en el contenedorProperties— ambos llegan al driver, y los mezclas a tu gusto. - Las credenciales pertenecen a
Properties(o a la configuración deDataSource), no codificadas en la cadena de URL que registras en logs. El ejemplo enmascara la contraseña en la salida exactamente por esa razón — nunca registres credenciales en logs. connectTimeoutes una propiedad real del driver de PostgreSQL. El ajuste fino vive en estos pares clave/valor, que es por qué rara vez subclasificas nada: la configuración, no el código, da forma a una conexión.- Esto se ejecutó sin base de datos porque construir las entradas para
getConnectiones trabajo puro con cadenas de texto. La parte costosa — el socket y la sesión del servidor — solo ocurre en la propia llamada agetConnection, que es por qué la diferías y la cierras rápido.