SQL dinámico con MyBatis (3 de 3)

mybatis2Si hemos desarrollado con JDBC o Hibernate, cuando concatenamos strings para conseguir la consulta SQL que deseemos según las condiciones de la consulta, el hecho de crear estas cadenas nos provoca verdaderos quebraderos de cabeza y gasto de mucho tiempo en probar y que no se nos haya olvidado una coma, comillas, cualquier carácter entre medias, etc

MyBatis mejora el "montaje" de SQL dinámico en los mapped statement empleando una serie de expresiones OGNL https://es.wikipedia.org/wiki/OGNL para hacernos la vida mas cómoda como:

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

Un ejemplo vale más que mil palabras. Para construir una SQL normalita donde incluir un trozo en la clausula where que varía según unas condiciones se haría de la siguiente forma:

<select id="findActiveBlogLike">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’ 
    AND title like #{title}
    AND author_name like #{author.name}
</select>

Como podemos ver en este ejemplo, para agregar nuevas condiciones a nuestra SQL en la sección del where, no tenemos que preparar engorrosos string's concatenados como en JDBC o Hibernate, si no que con una serie de sencillos tags de MyBatis podemos conseguirlo. En el código anterior, tanto si title o author son distintos a null, agregamos nuevas condiciones a la sentencia select para acotar más la búsqueda.

En ocasiones no queremos usar una condición (como hemos hecho anteriormente) sino elegir una de entre varias opciones. De forma similar al switch de Java; MyBatis para esto ofrece el elemento choose. Usemos el ejemplo anterior, pero ahora vamos a buscar solamente por título si se ha proporcionado un título y por autor si se ha proporcionado un autor. Si no se proporciona ninguno devolvemos una lista de Blogs destacados (quizá una lista seleccionada por los administradores en lugar de una gran lista de blogs sin sentido).

<select id="findActiveBlogLike">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
      AND title like #{title}
      AND author_name like #{author.name}
      AND featured = 1
</select>

trim, where, set

En el primer ejemplo hemos visto como MyBatis soluciona el problema para generar una sentencia SQL de forma dinámica. Ahora imaginemos lo que sucedería si volvemos a este ejemplo del “if”, pero esta vez, hacemos que state = 'ACTIVE = 1' sea también una condición dinámica.

<select id="findActiveBlogLike">
  SELECT * FROM BLOG 
  WHERE state = #{state}
    AND title like #{title}
    AND author_name like #{author.name}
</select>

Entonces, ¿qué sucede si no se cumple ninguna condición? que obtendríamos una sentencia SQL errónea con este aspecto:

SELECT * FROM BLOG 
WHERE

¿Y qué sucede si solo se cumple la segunda condición? que también tendríamos otra sentencia incorrecta:

SELECT * FROM BLOG 
WHERE 
AND title LIKE 'someTitle'

Este problema no es fácil resolverlo con condicionales. MyBatis tiene una respuesta sencilla que funcionará en el 90% de los casos. Y en los casos en los que no funciona puedes personalizarlo para hacerlo funcionar. Añadiendo el tag "where", MyBatis sabe que debe insertar en la sentencia la sección “WHERE” solo si los tags internos "if" devuelven algún contenido. Más aun, si el contenido comienza con “AND” o “OR”, sabe cómo eliminarlo, por ejemplo:

<select id="findActiveBlogLike">
  SELECT * FROM BLOG 
         state = #{state}
        AND title like #{title}
        AND author_name like #{author.name}
</select>

Otra necesidad común del SQL dinámico es iterar sobre una colección, habitualmente para construir una condición IN. Por ejemplo:

<select id="selectPostIn">
  SELECT *
  FROM POST P
  WHERE ID in #{item}
</select>