Desde hace tiempo que Groovy es mi lenguaje favorito para hacer scripts que a veces necesito para interactuar con bases de datos (algunos reportes o modificar ciertos datos, procesamiento por lotes, etc).
Son principalmente dos razones por las que me gusta tanto usar Groovy para este tipo de scripts:
En Scala aún no hay un componente similar. Y aunque lo hubiera, no usaría Scala para scripting (ya he escrito al respecto anteriormente). Pero creo que sí hace falta. Si bien existe el hiperútil componente JdbcTemplate de Spring, para el cual además ya existen unas adiciones para Scala (junto con otras cosas de bean factories) en el proyecto ScalaPrimavera, no siempre es posible o deseable comenzar a depender de Spring.
Así que me propuse desarrollar un componente similar al Sql de Groovy, con algunas cosas adicionales que me han resultado muy útiles de JdbcTemplate, con la finalidad de usarlo en aplicaciones Scala, de modo que debe ser seguro su uso en ambientes multi-hilo, debe tener buen performance, debe manejar bien la transaccionalidad y sobre todo debe ser sencillo de usar.
Primero empecé imaginándome el uso:
//Crearlo con un DataSource para poder usarlo desde varios hilos
//con distintas conexiones
val sql = new ScalaSql(algunDataSource)
//Obtener un solo registro; siendo Scala, esto debería ser un Option
val row = sql.firstRow("SELECT * FROM usuario WHERE clave=?", clave)
//Obtener una lista de registros
val rows = sql.rows("SELECT * FROM usuario WHERE fecha_alta > ?", new Date)
//Pasamos un closure que recibe un renglón y se invoca con cada
//registro obtenido; el renglón debe ser un mapa inmutable
sql.eachRow("SELECT * FROM cuenta WHERE saldo > ?", saldo) { row =>
//Hacer algo con el mapa
//Si alguna columna tiene NULL en la base de datos, debemos tener None
//Si conoces los nombres de columnas, no habrá problema en obtener los valores
if (row("nombre") == None) { //no trae nombre
} else { //si trae nombre
}
row("saldo") match {
case s:BigDecimal => //Manejar BigDecimal de Scala, no Java
case None => No viene saldo
}
}
//Hacer operaciones dentro de una transaccion
sql.withTransaction { conn => //idealmente no necesitas nada con la conexion
//Con esto obtenemos llaves autogeneradas
val nuevaClave = sql.executeInsert("INSERT INTO usuario (nombre,saldo) VALUES (?, ?, ?)", row("nombre"), row("saldo"))
//Dentro de la transaccion invocamos al mismo componente
sql.executeUpdate("DELETE FROM usuario WHERE clave=?", row("clave"))
//Si al final no hubo excepciones, se hace commit
}
En fin, esa es la idea. El código ya está disponible en GitHub y conforme lo utilice, iré agregándole funcionalidad. De entrada no me parece necesario que pueda ser creado o utilizado con una sola conexión, por diversas razones:
Como lo menciono en la página del proyecto, encontré algo similar ya existente, que incluso sospecho que también está inspirado en el Groovy SQL ya que sí tiene soporte para usarse con una conexión directamente (similar a cuando se crea el Groovy SQL con el puro URL, usuario, password y nombre de la clase del driver JDBC), pero no hay transaccionalidad ni obtención de llaves autogeneradas (aunque esto depende en última instancia de que el driver JDBC lo soporte realmente).
Pero bueno, pues ya hay una opción más para lidiar con bases de datos SQL desde Scala.