Java Smells: SQL programático

Os voy a enseñar código irritante:

private void writeHtml(Writer writer) throws IOException{
    writer.write("<html>");
    writer.write("<head>");
    // ...
    writer.write("</head>");
    writer.write("<body>");
    writer.write("some html output");
    writer.write("</body>");
    writer.write("</html>");
}

La mitad de vosotros estáis pensando ¿WTF? ¿Qué hace todo ese código html ahí en medio? ¿Por qué no está usando Velocity? ¿Por qué no está usando Mustache? ¿Por qué no está usando [inserte aquí un motor de plantillas]?. La otra mitad de vosotros que ve en esto algo normal, por favor, circulen.

Encontrarnos esto nos irrita porque mezclando diferentes lenguajes dentro de un mismo fuente complica el actualizar una parte (o la parte java , o la parte html), y asume que el desarrollador de uno de los lenguajes es el responsable del desarrollo del otro, cosa que aunque a veces es correcto, es una mala suposición que solo nos puede traer desgracias.

Sin embargo, por alguna razón que se me escapa, a la gente le produce menos miedo ver esto:

ResultSet res=stm.executeQuery("SELECT * FROM USERS");

como si el mezclar SQL con Java fuera algo más aceptable, como si dijéramos que un programador Java es un DBA con todo el conocimiento de cómo y porqué se hace una determinada consulta.

Huyendo del hecho de escribir consultas directamente en código, hemos pasado a usar frameworks ORM, Object-Relational-Mapping, que dan una visión de la base de datos como si fueran clases, aportando métodos de acceso a las operaciones de inserción / modificación / borrado / consulta de una forma más cómoda para el desarrollador genérico Java, así podemos encontrar como con Spring-Data usando repositorios podemos encontrar cosas como:

List<User> users=userRepository.findAll();

Con lo que ya no vemos de ninguna forma el SQL que nos podía irritar, ¿dónde nos lleva esto? Nos lleva a que delegamos la comunicación con la base de datos a una librería (insertar aquí JPA/Hibernate) de propósito general. ¿Qué pasaría entonces si por ejemplo, queremos cambiar un atributo de una lista de usuarios que cumplen una condición?

List<User> users=userRepository.findAll();
for(User user:users){
   if(user.getBirthDate()==null){
     user.setPendingData(true);
     userRepository.save(user);
   }
}

Y aquí, pese a no ver por ninguna parte código SQL, un desarrollador poco experimentado no vería nada raro, pero un DBA podría leer entre lineas que hay varios problemas realmente grave en este código fuente:

– ¿Porqué hemos traido todos los registros de la base de datos? Podría haber millones de registros, y la tabla de usuarios podría tener decenas de columnas, hemos traído todos, los necesitáramos o no para actualizar únicamente algunos de ellos

– ¿Porqué hemos traido las filas completas? Si sólo vamos a comprobar la fecha de nacimiento, ¿porqué hemos traído toda la fila íntegra? podríamos haber traido sin darnos cuenta un CLOB, o un BLOB ocupando la memoria innecesariamente

– Y finalmente, ¿porqué no hemos hecho TODO esto directamente en la base de datos con una única consulta SQL?

UPDATE USERS SET PENDING_DATA=true WHERE BIRTH_DATE IS NULL

El problema reside en la comodidad a la hora de desarrollar usando frameworks que nos abstraen la base de datos, esto provoca que olvidemos realmente con lo que estamos tratando más allá del propio código fuente que tenemos bajo nuestra nariz. Esta comodidad va acompañada de malos vicios, que desgraciadamente son más difíciles de detectar que cuando eran sentencias SQL directamente insertadas en el código. Estos vicios suelen ser los causantes de fallos de rendimiento tanto en memoria (tratamos más datos de los que necesitamos) como de tráfico de red (movemos por la red más datos de los que necesitamos) y provocan el ya bien conocido «eso se resuelve reiniciando el servidor».

Lógicamente, los responsables de estos problemas no son JPA / Hibernate / Spring-Data (todos las soluciones ORM permiten hacer las cosas bien a costa de programar un poco más) , son los desarrolladores que por tomar el camino más cómodo sacrifican el rendimiento de la aplicación y le pasan el problema al equipo de mantenimiento ( que normalmente son los propios desarrolladores en cuanto pase el día de puesta en producción ).

Una posible solución a este problema es optar por soluciones mixtas, como por ejemplo Spring JDBC Template o Jdbi, que en ambos casos nos permiten tener separados SQL de código fuente, pero dándonos acceso directo a ambos, permitiendo que desarrolladores y DBAs puedan cada uno trabajar en su marco de conocimiento.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *