El ecosistema MyBatis requiere la escritura manual de instrucciones SQL para cada operación de persistencia. Para reducir la fricción en etapas iniciales, se integra un motor de generación inversa capaz de producir automáticamente interfaces de acceso, mapas XML y objetos de dominio a partir del esquema relational. Este flujo elimina la repetición de código boilerplate y acelera la entrega de módulos CRUD básicos. Sin embargo, posee restricciones operativas claras: la ejecución reiterada sin limpieza previa tiende a duplicar o concatenar definiciones, lo que obliga a reiniciar el directorio de salida cuando el modelo de datos evoluciona. Asimismo, no abstrae relacioens complejas ni procedimientos almacenados, elementos que requieren implementación manual.
Descriptor de Configuración
La herramienta opera mediente un archivo XML declarativo donde se definen las convenciones de generación. Los nodos principales gestionan el enlace al sistema gestor, la transformación de tipos JDBC a Java, la ruta de ensamblado de modelos, la ubicación de los mappings y el mapeo específico de tablas. El atributo targetRuntime determina el nivel de complejidad generado: MyBatis3 habilita constructores de criterios avanzados, mientras que MyBatis3Simple limita la salida a operaciones estándar.
<?xml version="1.0" encoding="UTF-8"?>
<generatorConfiguration>
<context id="productionTables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3307/inventario_db" userId="admin_sistemas"
password="SecurePass_2024">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<javaModelGenerator targetPackage="com.datamodels.inventory"
targetProject="./src/main/java">
<property name="enableSubPackages" value="false"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<sqlMapGenerator targetPackage="com.mappers.inventory"
targetProject="./src/main/resources">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.dao.inventory"
targetProject="./src/main/java">
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<table catalog="inventario_db" tableName="producto_logico" domainObjectName="ProductoEntity"/>
<table catalog="inventario_db" tableName="categoria_asignada" domainObjectName="CategoriaEntity"/>
</context>
</generatorConfiguration>
Ejecutor del Proceso de Generación
La invocación se realiza mediante una aplicación Java independiente que parsea el descriptor, aplica la política de sobrescritura y dispara el motor de construcción. Es recomendable encapsular la gestión de excepciones y registrar advertencias pendientes.
package com.datagenerator;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class DataSchemaBuilder {
private static final String CONFIG_PATH = "./generator-config.xml";
public void construirArtefactos() throws Exception {
List<String> avisosPrevia = new ArrayList<>();
boolean forzarActualizacion = true;
File descriptor = new File(CONFIG_PATH);
ConfigurationParser analizador = new ConfigurationParser(avisosPrevia);
Configuration configuracionSistema = analizador.parseConfiguration(descriptor);
DefaultShellCallback gestorArchivos = new DefaultShellCallback(forzarActualizacion);
MyBatisGenerator compilador = new MyBatisGenerator(configuracionSistema, gestorArchivos, avisosPrevia);
compilador.generate(null);
if (!avisosPrevia.isEmpty()) {
System.err.println("Avisos durante la generación: " + avisosPrevia);
}
}
public static void main(String[] argumentos) {
try {
new DataSchemaBuilder().construirArtefactos();
System.out.println("Capa de persistencia generada exitosamente.");
} catch (Exception fallo) {
fallo.printStackTrace();
System.exit(1);
}
}
}
Integración y Consultas Dinámicas
Una vez construidos los artefactos, se inyectan en el contexto de sesión y se utilizan patrones de criterios (Example / Criteria) para armar filtros complejos sin escribir SQL explícito. La siguiente demostración muestra cómo estructurar lecturas filtradas, combinaciones lógicas, ordenamiento y agregaciones.
@Test
void validarOperacionesBasicasYCondicionales() {
ProductosMapper repositorio = session.getMapper(ProductosMapper.class);
// 1. Recuperación por clave primaria
ProductoEntity registroBase = repositorio.selectByPrimaryKey(105);
Assertions.assertNotNull(registroBase);
System.out.printf("ID: %d | Nombre: %s\\n", registroBase.getId(), registroBase.getNombre());
// 2. Filtro único: edad superior a umbral definido
ProductosExample filtroUnico = new ProductosExample();
filtroUnico.createCriteria().andEdadMayorQue(18);
List<ProductoEntity>">listaFiltrada = repositorio.selectByExample(filtroUnico);
listaFiltrada.forEach(p -> System.out.println("Producto habilitado: " + p.getNombre()));
// 3. Combinación lógica AND
ProductosExample filtroAnd = new ProductosExample();
Condiciones criterio = filtroAnd.createCriteria();
criterio.andEdadMayorQue(20);
criterio.andNombreLike("%tech%");
List<ProductoEntity>">resultadosConjuntos = repositorio.selectByExample(filtroAnd);
resultadosConjuntos.forEach(System.out::println);
// 4. Combinación lógica OR (criterios independientes)
ProductosExample filtroOr = new ProductosExample();
Condiciones clausulaUno = filtroOr.createCriteria();
clausulaUno.andEdadMenorIgual(25);
Condiciones clausulaDos = filtroOr.or();
clausulaDos.andNombreLike("%vintage%");
List<ProductoEntity>">unionResultados = repositorio.selectByExample(filtroOr);
unionResultados.forEach(p -> System.out.println("Unión aplicada: " + p.getNombre()));
// 5. Ordenamiento multicolumna
ProductosExample consultaOrdenada = new ProductosExample();
consultaOrdenada.createCriteria().andPrecioMayorQue(50);
consultaOrdenada.setOrderByClause("precio DESC, stock ASC");
List<ProductoEntity>">ordenados = repositorio.selectByExample(consultaOrdenada);
ordenados.forEach(System.out::println);
// 6. Agregación condicional
ProductosExample conteoSelectivo = new ProductosExample();
conteoSelectivo.createCriteria().andEdadMayorQue(30).andNombreLike("%pro%");
long totalCumplimiento = repositorio.countByExample(conteoSelectivo);
System.out.println("Total registros cumplen condición: " + totalCumplimiento);
}