1. El papel de la configuración del DataSource
El DataSource ya existe como bean real, pero aquí empieza la dura verdad: no es telepático. Necesita saber dónde vive la base, cómo llegar a ella y con qué credenciales conectarse. Sin esto la aplicación puede estar escrita perfectamente, pero se parecerá a un coche sin gasolina: el volante gira, las luces parpadean, pero no hay a dónde ir.
La propia cadena de infraestructura ya está clara: driver, DataSource, capa JPA. Ahora necesita la dirección concreta, el puerto y las credenciales, de lo contrario todo el esquema seguirá siendo bonito pero inútil.
En Spring Boot la configuración no es «arreglaré en algún sitio del código una cadena de conexión», sino una disciplina separada. Queremos que el proyecto shop-data-jpa arranque de la misma manera para todos los estudiantes y al mismo tiempo no se convierta en un conjunto de valores hardcodeados que luego da vergüenza mostrarse a uno mismo al cabo de un mes.
Sentido práctico del DataSource
El DataSource es el objeto que proporciona conexiones a la BD (y normalmente lo hace a través de un pool de conexiones, pero el pool lo analizaremos en detalle en la próxima lección). Ahora lo importante es lo simple: para que el DataSource pueda crear una conexión, necesita los parámetros.
En Spring Boot esos parámetros normalmente se definen en la configuración, a través de propiedades del tipo:
- spring.datasource.url
- spring.datasource.username
- spring.datasource.password
Precisamente esas propiedades aprenderemos a definir «de forma adulta».
2. Archivos de ajustes: application.yml y application-dev.yml
Cuando el proyecto solo empieza, tienes muchas ganas de decir: «bueno, de momento lo escribo todo en application.yml, y ya lo ordenaré». Eso es más o menos como apilar todas las cosas en una caja «luego la etiqueto». Normalmente ese «luego» no llega, y la caja se convierte en la legendaria carpeta «Varios», donde vive la mitad de tu vida y un tique de la tienda de 2019.
En Spring Boot es costumbre dividir la configuración en una capa general y una capa de entorno. La capa general — lo que casi no cambia: el nombre de la aplicación, los ajustes generales. La capa de entorno — lo que depende del entorno (dev, test, prod): la dirección de la BD, la contraseña, a veces el logging. Para el proyecto pedagógico esto es especialmente importante: queremos un «arranque dev simple», pero sin fijar malos hábitos.
El application.yml mínimo
En la raíz de los recursos del proyecto (src/main/resources) creamos el application.yml, donde habrá solo los ajustes generales. Al inicio nos basta con el nombre de la aplicación y, en modo pedagógico, la activación del perfil dev.
spring:
application:
name: shop-data-jpa # Nombre de la aplicación (visible, por ejemplo, en los logs)
profiles:
active: dev # Default pedagógico: arrancamos en el perfil dev
Sí, en los proyectos reales spring.profiles.active a menudo no se hace commit en el repositorio, sino que se define a través de las variables de entorno o los parámetros de arranque. Pero para el proyecto pedagógico es un compromiso honesto: menos «magia del arranque», más comprensión de qué exactamente ocurre. Un poco más adelante de todas formas analizaremos cómo activar el perfil a través del entorno.
El archivo de perfil application-dev.yml
Ahora creamos el application-dev.yml y trasladamos ahí todo lo que está vinculado a un entorno concreto, incluyendo los parámetros de conexión a PostgreSQL.
spring:
datasource:
url: jdbc:postgresql://localhost:5432/shop # Dirección de la BD (host:port/database)
username: shop # Login para la conexión
password: shop # Contraseña para la conexión
Es la variante más directa. Funciona. Pero tiene una desventaja: los parámetros están hardcodeados. Para el entorno de desarrollo pedagógico es tolerable, pero queremos aprender a hacer lo mismo a través de las variables de entorno, para poder cambiar fácilmente el host, el puerto y el nombre de la BD sin editar el YAML y sin «commitear la contraseña» como hábito.
3. URL JDBC para PostgreSQL: de qué está compuesta
La URL JDBC al principio parece un conjuro: si te equivocas en un solo carácter — y se convoca el demonio Connection refused. Pero si se descompone la cadena en partes, resulta mucho más tranquilo. En esta lección no intentamos convertirnos en DBA y no nos adentramos en las particularidades de la red; nos basta con aprender a leer la URL con los ojos y entender qué exactamente se define ahí.
Para PostgreSQL la forma básica tiene este aspecto: jdbc:postgresql://host:port/database. Y esta es la clave: host, port, database. Si entiendes estas tres cosas, podrás diagnosticar el 80% de los problemas de conexión sin adivinar en los logs.
Desglosamos la URL en trozos
Aquí la URL «clásica» de nuestro curso:
jdbc:postgresql://localhost:5432/shop
Desglosamos la cadena en partes para que el cerebro no finja que «es una cadena incomprensible única»:
| Parte | Ejemplo | Qué significa |
|---|---|---|
| jdbc:postgresql | jdbc:postgresql | Protocolo JDBC + driver de PostgreSQL |
| host | localhost | Dónde vive la base (desde el punto de vista de la aplicación) |
| port | 5432 | Puerto de PostgreSQL (por defecto 5432) |
| database | shop | Nombre de la base de datos dentro de PostgreSQL |
Matiz importante sobre localhost y Docker
Ahora la aplicación se ejecuta en tu máquina, y PostgreSQL está en el contenedor, pero con el puerto 5432:5432 mapeado. Para la aplicación eso significa simplemente: la base es accesible en localhost:5432. Si alguna vez ejecutas la aplicación también en Docker, localhost se convierte en una trampa, porque dentro del contenedor localhost es él mismo. Pero eso no es nuestra tarea ahora. Por ahora basta con recordar la regla: el host depende de desde dónde te conectas.
4. Login y contraseña: almacenamiento fuera del código
El login y la contraseña son la parte obligatoria de la conexión, sin la cual PostgreSQL justificadamente pregunta: «¿Quién eres?». Al mismo tiempo los desarrolladores principiantes a veces intentan «simplificarse» la vida y escriben la contraseña directamente en el código, al estilo new DriverManager.getConnection("...", "user", "password"). ¿Es cómodo? Sí. ¿Es bueno? No. Y no porque «así está prohibido por el libro de texto», sino porque rompe la reproducibilidad y la seguridad.
En nuestro proyecto pedagógico la contraseña es simple (shop), y nadie va a hackear tu portátil por la base pedagógica. Pero el hábito de «la contraseña en el código» emigra perfectamente a los proyectos de trabajo, y luego empieza lo divertido: la contraseña llega al Git, a los logs, a las capturas de pantalla, al historial de commits y al karma.
Qué hacemos en su lugar
Dejamos el login y la contraseña en la configuración. Y aún mejor — en las variables de entorno, para que el YAML no se convierta en un «diario secreto».
En Spring Boot los parámetros pueden definirse así:
spring:
datasource:
username: ${DB_USER:shop} # Tomamos de la variable DB_USER del entorno, si no existe usamos shop
password: ${DB_PASSWORD:shop} # Tomamos de la variable DB_PASSWORD del entorno, si no existe usamos shop
Aquí ${...:...} es un marcador de posición: «toma el valor del entorno, y si no existe — usa el valor por defecto».
5. Variables de entorno y marcadores de posición
Las variables de entorno a menudo suenan como «algo de DevOps», pero en realidad son una herramienta básica del desarrollador. La idea es simple: el código es el mismo, y los valores — host, puerto, contraseña — pueden cambiarse desde fuera, sin editar archivos y sin arriesgarse a commitear accidentalmente una «contraseña de combate».
En el proyecto pedagógico son especialmente útiles, porque alguien ya tiene el puerto 5432 ocupado, alguien ejecuta varios proyectos del curso simultáneamente, y alguien simplemente le gusta sufrir (sin ánimo de ofender). Queremos que el proyecto pueda adaptarse a esas condiciones con el mínimo esfuerzo.
Ajuste del application-dev.yml a través del entorno
Aquí la variante «pedagógico-productiva» buena del application-dev.yml:
spring:
datasource:
# Construimos la URL a partir de partes para que sea fácil redefinirla por el entorno
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:shop}
username: ${DB_USER:shop} # Login del entorno o el valor por defecto
password: ${DB_PASSWORD:shop} # Contraseña del entorno o el valor por defecto
Observa un detalle importante: no solo usamos las variables de entorno, sino que definimos valores por defecto explícitos. Eso hace la configuración legible. Cuando el estudiante abre el archivo, ve enseguida: «Vale, por defecto es localhost, 5432, shop».
Definición de variables de entorno (Linux/macOS)
Si estás en la terminal en un sistema Unix, tiene este aspecto:
export DB_HOST=localhost # Host donde es accesible la BD para la aplicación
export DB_PORT=5432 # Puerto de PostgreSQL
export DB_NAME=shop # Nombre de la base de datos
export DB_USER=shop # Login
export DB_PASSWORD=shop # Contraseña
Esto puede hacerse en la terminal antes de arrancar la aplicación desde esa misma terminal.
Definición de variables de entorno (Windows PowerShell)
En PowerShell normalmente se hace así:
$env:DB_HOST="localhost" # Host donde es accesible la BD para la aplicación
$env:DB_PORT="5432" # Puerto de PostgreSQL (como cadena — es normal para env)
$env:DB_NAME="shop" # Nombre de la base de datos
$env:DB_USER="shop" # Login
$env:DB_PASSWORD="shop" # Contraseña
Sí, la sintaxis es diferente. Windows le encanta ser «el copo de nieve especial», pero de todas formas lo apreciamos.
6. Perfiles de Spring Boot
Un perfil en Spring es una forma de decirle a la aplicación: «ahora te ejecutas en tal modo, usa tales ajustes». Y sí, la idea es realmente simple. Las complicaciones empiezan cuando todo se mete en un archivo y luego te sorprendes de que los tests van de repente a la base dev, y encima eliminas accidentalmente las tablas y el día se vuelve algo más saturado.
En el proyecto viviremos gradualmente en al menos dos modos: dev y test. Ahora necesitamos el dev: PostgreSQL local en Docker. El test aquí es importante ya como recordatorio de que una sola configuración para todo casi siempre lleva a la confusión y a las idas accidentales a la base equivocada.
Conexión del archivo de perfil
Si el perfil dev está activo, Spring Boot recoge automáticamente el application-dev.yml y lo «superpone» sobre el application.yml. Es decir, archivo general + archivo de perfil = ajustes finales.
Simplificando mucho, y exactamente así lo usamos hoy, la lógica es esta:
application.yml (ajustes generales)
+
application-dev.yml (redefiniciones para dev)
=
configuración final
Definición del perfil activo
La forma más directa pedagógica que ya mostramos arriba — definirlo en el application.yml:
spring:
profiles:
active: dev
Pero también existe la variante más flexible — la variable de entorno:
export SPRING_PROFILES_ACTIVE=dev # Elegimos el perfil sin editar el YAML
Si defines SPRING_PROFILES_ACTIVE, normalmente ya no hace falta hardcodear spring.profiles.active en el YAML. En los equipos reales así se hace a menudo: el perfil se elige «desde fuera».
Prioridades de las fuentes de configuración
En Spring Boot la configuración puede venir de distintas fuentes: archivos YAML, variables de entorno, argumentos de línea de comandos, propiedades del sistema JVM. La tabla completa de prioridades es un tema aparte que fácilmente se convierte en una referencia de 10 pantallas. Pero para el curso necesitamos el modelo mínimo de ingeniería para no perdernos al depurar.
La idea es esta: cuanto más «cerca» está el ajuste del arranque — línea de comandos, entorno —, más alta es su prioridad. Y los archivos de configuración — eso es la capa base.
Esquema simplificado de prioridades
| Fuente | Ejemplo | Prioridad normalmente |
|---|---|---|
| Argumentos del arranque | --spring.datasource.url=... | Muy alta |
| Variables de entorno | SPRING_DATASOURCE_URL=... | Alta |
| application-dev.yml | archivo de perfil | Media |
| application.yml | archivo general | Base |
Esto es una simplificación, pero ya ayuda a responder a la pregunta: «¿Por qué la aplicación se conecta no donde lo definí en el YAML?» La respuesta frecuente: «Porque en tu entorno ya hay una variable definida que ha redefinido el archivo».
Verificación de los ajustes aplicados
Una de las trampas más peligrosas en la configuración es pensar que «si lo escribí en el YAML, entonces se aplicó seguro». La configuración adora las sorpresas: el perfil activo no es ese, la variable de entorno ha redefinido el valor, el IDE ha arrancado con otros parámetros. Por eso es útil saber hacer un pequeño «control de la realidad».
Hoy no hacemos una smoke-prueba completa de la conexión — eso estará en la lección práctica del día —, pero podemos hacer una mini-verificación: imprimir los perfiles activos y la URL que ve Spring.
Mini-verificación a través del Environment
Esto es conveniente verificarlo una vez justo después del arranque de la aplicación, pero no hay que convertir esa impresión en código permanente del punto de entrada. El sentido es puramente diagnóstico: ver los valores finales después de aplicar los perfiles y las redefiniciones del entorno, y luego eliminar ese código temporal.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
@SpringBootApplication
public class ShopDataJpaApplication {
public static void main(String[] args) {
// Levantamos el contexto Spring (después se puede leer la configuración aplicada)
var context = SpringApplication.run(ShopDataJpaApplication.class, args);
// Tomamos el Environment del contexto para ver los valores finales de las propiedades
Environment env = context.getBean(Environment.class);
// Diagnóstico rápido: qué perfil está realmente activo
System.out.println("Active profiles: " + String.join(", ", env.getActiveProfiles())); // Active profiles: dev
// Diagnóstico rápido: qué URL vio realmente Spring (con los perfiles y el env tenidos en cuenta)
System.out.println("DB url: " + env.getProperty("spring.datasource.url")); // DB url: jdbc:postgresql://localhost:5432/shop
}
}
Aquí hay dos puntos importantes. En primer lugar, ves enseguida los perfiles activos y no adivinas. En segundo lugar, ves el valor final de la URL ya con los perfiles y las variables de entorno tenidos en cuenta. Es un diagnóstico muy en tierra que ahorra tiempo.
Miraste el perfil activo y la URL final — después ese código puede eliminarse tranquilamente sin arrastrarlo al esqueleto permanente de la aplicación.
7. Errores típicos al configurar el DataSource
Error n.º 1: hardcode en el código Java «para que funcione más rápido».
El principiante a veces añade la cadena de conexión directamente en el código, porque «así es más rápido de verificar». El problema no es que eso sea absolutamente imposible, sino que rompes la propia idea de Spring Boot: la configuración debe ser externa, manejable y reproducible. Hoy hardcodeaste localhost, y mañana ejecutas la aplicación en otro entorno y olvidas por qué insiste en conectarse a la base antigua.
Error n.º 2: mezclar los configs general y de entorno en un solo archivo sin sistema.
Si en el application.yml mantienes el nombre de la aplicación, los parámetros de la base dev y alguna «corrección temporal», al cabo de un par de días ese archivo se convierte en un pantano. Los archivos de perfil (application-dev.yml) no son por belleza, sino para que los cambios de entorno no se conviertan en el juego de «busca 10 diferencias» entre las ramas y los commits.
Error n.º 3: URL JDBC incorrecta (lo más a menudo el puerto y el nombre de la base).
El clásico — confundir host:port, indicar una base inexistente o olvidarse de que PostgreSQL escucha en 5432. El buen hábito — cada vez pronunciar la URL en palabras: «me conecto a localhost, puerto 5432, base shop». Si no puedes pronunciarlo, probablemente tú mismo no estás seguro de lo que has escrito.
Error n.º 4: el perfil activo no es ese, y se culpa a la base.
Una historia muy frecuente: el estudiante edita el application-dev.yml, y la aplicación se ejecuta sin el perfil dev. Como resultado los ajustes no se cargan, y empieza el «PostgreSQL está roto». Por eso lo primero que conviene verificar ante los problemas son los perfiles activos. Eso casi siempre es más rápido que reinstalar Docker.
Error n.º 5: las variables de entorno se llaman de manera distinta a los marcadores de posición en el YAML.
Escribiste ${DB_PASSWORD:shop}, y exportaste DB_PASS. La aplicación toma honestamente el valor por defecto y se conecta con la contraseña shop, y tú estás seguro de que «pasé la contraseña correcta». Aquí salvan la disciplina de nomenclatura y un pequeño diagnóstico a través del Environment, donde se ve el valor final de la URL y el login.
GO TO FULL VERSION