En proyectos reales, es común separar la infraestructura en distintos entornos: desarrollo, pruebas y producción. Cada uno requiere configuraciones independientes de fuentes de datos —por ejemplo, evitar que el entorno de desarrollo acceda accidentalmente a bases de producción, lo cual podría comprometer la integridad de los datos.
MyBatis permite definir múltiples perfiles de entorno dentro de su archivo de configuración principal (mybatis-config.xml). Esto se logra mediante el elemento <environments>, que agrupa varias instancias de <environment>, cada una identificada por un atributo id. El atributo default especifica cuál se usará si no se indica explícitamente otro.
Configuración básica con dos entornos
A continuación se muestra una configuración que define dos entornos: dev y prod. Ambos utliizan el administrador de transacciones JDBC y un pool de conexiones (POOLED), pero apuntan a bases de datos distintas mediante sus respectivos URLs:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/myapp_dev?serverTimezone=UTC&useSSL=false"/>
<property name="username" value="dev_user"/>
<property name="password" value="dev_pass123"/>
</dataSource>
</environment>
<environment id="prod">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://prod-db.internal:3306/myapp_prod?serverTimezone=UTC&useSSL=true"/>
<property name="username" value="prod_user"/>
<property name="password" value="prod_secret456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
Extracción de propiedades comunes
Cuando varios entornos comparten valores repetidos (como el nombre del controlador o credenciales genéricas), conviene centralizarlos usando el bloque <properties>. Esto mejora la mantenibilidad y reduce errores de copia/pegado.
La siguiente variante reemplaza los valores duplicados por referencias tipo ${clave}:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<properties>
<property name="jdbc_driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbc_user" value="app_user"/>
<property name="jdbc_password" value="secure_password"/>
</properties>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc_driver}"/>
<property name="url" value="jdbc:mysql://localhost:3306/app_dev"/>
<property name="username" value="${jdbc_user}"/>
<property name="password" value="${jdbc_password}"/>
</dataSource>
</environment>
<environment id="prod">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc_driver}"/>
<property name="url" value="jdbc:mysql://db-prod:3306/app_prod"/>
<property name="username" value="${jdbc_user}"/>
<property name="password" value="${jdbc_password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/ProductMapper.xml"/>
</mappers>
</configuration>
Inicialización programática con entorno específico
El flujo de creación de sesión en MyBatis implica tres pasos clave: construir un SqlSessionFactoryBuilder, usarlo para generar un SqlSessionFactory, y finalmente obtener un SqlSession. La selección del entorno ocurre al llamar al método build(), pasando como segundo parámetro el id del entorno deseado.
Este fragmento inicializa una fábrica conectada al entorno de producción:
try (InputStream configStream = Resources.getResourceAsStream("mybatis-config.xml")) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
.build(configStream, "prod");
try (SqlSession session = factory.openSession()) {
List<Product> products = session.selectList("ProductMapper.listAll");
products.forEach(System.out::println);
}
}
Para probar ambas configuraciones, se puede iterar sobre distintos archivos XML y forzar el uso del perfil "prod" en todos los casos:
@Test
void verifyEnvironmentIsolation() {
List<String> configs = List.of(
"configs/mybatis-dev.xml",
"configs/mybatis-prod.xml"
);
for (String cfg : configs) {
try (InputStream is = Resources.getResourceAsStream(cfg)) {
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
.build(is, "prod");
try (SqlSession s = factory.openSession()) {
var result = s.selectList("ProductMapper.countActive");
System.out.println("Active items in prod: " + result);
}
} catch (IOException e) {
throw new IllegalStateException("Failed to load config: " + cfg, e);
}
}
}