Implementación de SQL Dinámico con MyBatis

Concepto de SQL Dinámico

El SQL dinámico implica la generación de consultas SQL en tiempo de ejecución según condiciones variables. MyBatis proporciona etiquetas como <if>, <choose>, <where> y <foreach> para simplificar la creación de estas consultas.

Etiquetas de SQL Dinámico en MyBatis

Etiqueta <if>

La etiqueta <if> incluye fragmentos de SQL de forma condicional basándose en expresiones evaluadas.

<select id="buscarClientePorFiltros" parameterType="map" resultType="Cliente">
  SELECT * FROM clientes
  WHERE 1=1
  <if test="apellido != null">
    AND apellido = #{apellido}
  </if>
  <if test="ciudadId != null">
    AND ciudad_id = #{ciudadId}
  </if>
</select>

Etiquetas <choose>, <when> y <otherwise>

Estas etiquetas permiten seleccionar una opción entre múltiples condiciones, similar a una estructura switch en Java.

<select id="buscarProductoPorPrioridad" parameterType="map" resultType="Producto">
  SELECT * FROM productos
  WHERE 1=1
  <choose>
    <when test="categoria != null">
      AND categoria = #{categoria}
    </when>
    <when test="precioMax != null">
      AND precio <= #{precioMax}
    </when>
    <otherwise>
      AND estado = 'disponible'
    </otherwise>
  </choose>
</select>

Etiquetas <trim>, <where> y <set>

Estas etiquetas ajustan dinámicamente la sintaxis SQL para eliminar operadores redundantes como AND o comas.

<select id="buscarEmpleadosPorCriterios" parameterType="map" resultType="Empleado">
  SELECT * FROM empleados
  <where>
    <if test="nombreCompleto != null">
      AND nombre_completo LIKE CONCAT('%', #{nombreCompleto}, '%')
    </if>
    <if test="salarioMin != null">
      AND salario >= #{salarioMin}
    </if>
  </where>
</select>

Etiqueta <foreach>

La etiqueta <foreach> itera sobre colecciones para generar cláusulas IN o instrucciones de inserción masiva.

<select id="buscarOrdenesPorIds" parameterType="list" resultType="Orden">
  SELECT * FROM ordenes
  WHERE id IN
  <foreach item="ordenId" index="idx" collection="listaIds" open="(" separator="," close=")">
    #{ordenId}
  </foreach>
</select>

Ejemplos Prácticos

Ejemplo 1: Consulta Dinámica de Empleados

Interfaz del Mapper:

public interface EmpleadoMapper {
    List<Empleado> buscarPorFiltros(Map<String, Object> filtros);
}

Archivo XML del Mapper:

<mapper namespace="com.ejemplo.mapper.EmpleadoMapper">
  <select id="buscarPorFiltros" parameterType="map" resultType="Empleado">
    SELECT * FROM empleados
    <where>
      <if test="nombre != null">
        AND nombre = #{nombre}
      </if>
      <if test="departamento != null">
        AND departamento = #{departamento}
      </if>
    </where>
  </select>
</mapper>

Llamada desde el Servicio:

@Service
public class EmpleadoServicio {
    @Autowired
    private EmpleadoMapper empleadoMapper;

    public List<Empleado> obtenerEmpleados(String nombre, String departamento) {
        Map<String, Object> parametros = new HashMap<>();
        parametros.put("nombre", nombre);
        parametros.put("departamento", departamento);
        return empleadoMapper.buscarPorFiltros(parametros);
    }
}

Ejemplo 2: Inserción Masiva de Productos

Interfaz del Mapper:

public interface ProductoMapper {
    void insertarVarios(List<Producto> productos);
}

Archivo XML del Mapper:

<mapper namespace="com.ejemplo.mapper.ProductoMapper">
  <insert id="insertarVarios" parameterType="list">
    INSERT INTO productos (nombre, precio, stock)
    VALUES
    <foreach collection="listaProductos" item="producto" separator=",">
      (#{producto.nombre}, #{producto.precio}, #{producto.stock})
    </foreach>
  </insert>
</mapper>

Llamada desde el Servicio:

@Service
public class ProductoServicio {
    @Autowired
    private ProductoMapper productoMapper;

    public void registrarProductos(List<Producto> productos) {
        productoMapper.insertarVarios(productos);
    }
}

Mejores Prácticas

Utiliza el SQL dinámico con moderación para mantener la legibilidad y mantenibilidad del código. Emplea siempre enlaces de parámetros con #{param} en lugar de concatenación directa para prevenir inyecciones SQL. Realiza pruebas exhaustivas y habilita el registro de MyBatis para depurar las consultas generadas.

Etiquetas: MyBatis SQL Dinámico java XML consultas SQL

Publicado el 7-30 08:14