Configurar la base de datos en Grails

De ChuWiki

Configuración por defecto[editar]

Cuando hacemos nuestra aplicación grails creada con un grails create-app, se crea automáticamente para trabajar contra una base de datos HSQLDB embebida y se crea un fichero DataSource.groovy con la configuración para esa base de datos. Este fichero es parecido a esto

dataSource {
	pooled = true
	driverClassName = "org.hsqldb.jdbcDriver"
	username = "sa"
	password = ""
}
hibernate {
    cache.use_second_level_cache=true
    cache.use_query_cache=true
    cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider'
}
// environment specific settings
environments {
	development {
		dataSource {
			dbCreate = "create-drop" // one of 'create', 'create-drop','update'
			url = "jdbc:hsqldb:mem:devDB"
		}
	}
	test {
		dataSource {
			dbCreate = "update"
			url = "jdbc:hsqldb:mem:testDb"
		}
	}
	production {
		dataSource {
			dbCreate = "update"
			url = "jdbc:hsqldb:file:prodDb;shutdown=true"
		}
	}
}

Se definen en este fichero tres posibles dataSources según el tipo de entorno de trabajo: desarrollo (development), test y producción (production). En el primer trozo de código, dataSource {...}, se definen las propiedades de la base de datos comunes a los tres entornos de trabajo, es decir, si queremos un pool de conexiones (pooled), el driver de la base de datos (hsqldb), usuario y password. Luego, bajo development, test y production, se definen las propiedades específicas para cada uno de estos entornos de trabajo, en concreto, la url de conexión con la base de datos y si se deben o no crear las tablas en el arranque. Así, para desarrollo, tenemos

  • dbCreate = "create-drop" Cada vez que arranquemos nuestra aplicación en modo de trabajo "desarrollo" (lo normal cuando la arrancamos con nuestro IDE favorito o con grails run-app), las tablas en bd se borrarán y se crearán desde cero.
  • url = "jdbc:hsqldb:mem:devDB" La base de datos HSQLDB se almacenará en memoria.

Usar otra base de datos[editar]

Supongamos que para nuestra aplicación no queremos usar una HSQLDB embebida, sino otra base de datos más en serio, como MySQL. Los pasos a seguir son

  • Meter el jar con el conector de MySQL (o la base de datos que queramos) en el directorio lib de nuestro proyecto grails. En concreto, meteremos en grails-app/lib el jar mysql-connector-java-5.1.6.jar (o el que corresponda).
  • Configurar en DataSource.groovy los parámetros de conexión. No tenemos más que ir reemplazando los valores de las propiedades por los propios de nuestra base de datos. Si la base de datos se llama chukanboard y el usuario y password es grails, podemos poner esto
dataSource {
	pooled = true
	driverClassName = "com.mysql.jdbc.Driver"
	username = "grails"
	password = "grails"
	url = "jdbc:mysql://localhost/chukanboard"
}
hibernate {
    cache.use_second_level_cache=true
    cache.use_query_cache=true
    cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider'
}
// environment specific settings
environments {
	development {
		dataSource {
			dbCreate = "create-drop" // one of 'create', 'create-drop','update'
		}
	}
	test {
		dataSource {
			dbCreate = "update"
		}
	}
	production {
		dataSource {
			dbCreate = "update"
		}
	}
}

Hemos cambiado los valores para que sean propios de MySQL. Nos hemos llevado la url a la parte común, ya que ahora es común para los tres entornos de trabajo.

Sin embargo, quizás no es buena idea que cada vez que arrancamos nuestra aplicación en el IDE para probar algo se nos borre la base de datos entera. Puede ser buena idea dejar HSQLDB para los entornos de desarrollo y test y poner MySQL sólo para producción. Esto se puede hacer "redefiniendo" las propiedades sólo en la parte de producción. Dejamos, entones, el fichero DataSource.groovy como estaba y cambiamos sólo la parte de producción.

dataSource {
	pooled = true
	driverClassName = "org.hsqldb.jdbcDriver"
	username = "sa"
	password = ""
}
hibernate {
    cache.use_second_level_cache=true
    cache.use_query_cache=true
    cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider'
}
// environment specific settings
environments {
	development {
		dataSource {
			dbCreate = "create-drop" // one of 'create', 'create-drop','update'
			url = "jdbc:hsqldb:mem:devDB"
		}
	}
	test {
		dataSource {
			dbCreate = "update"
			url = "jdbc:hsqldb:mem:testDb"
		}
	}
	production {
		dataSource {
			dbCreate = "update"
	                driverClassName = "com.mysql.jdbc.Driver"
                	username = "grails"
			password = "grails"
			url = "jdbc:mysql://localhost/chukanboard"
		}
	}
}

De esta forma, cuando arranquemos con el IDE, con grails run-app o ejecutemos los test de prueba, la base de datos será HSQLDB. Si ejecutamos en un entorno de producción (generando el war con grails war y desplegándolo en un Tomcat, por ejemplo), la base de datos será la MySQL cuyos parámetros hemos puesto en production.

Usar un fichero de propiedades[editar]

El problema de configurar la base de datos en DataSource.groovy es que eso es un fichero fuente que se compila y luego se distribuye como un fichero DataSource.class ya compilado. No es, por tanto, fácil de cambiar y sería necesario recompilar los fuentes y generar de nuevo el war para llevárselo al entorno de producción. Si queremos dar facilidades al usuario para configurar nuestra aplicación con su propia base de datos, debemos facilitarle la vida de alguna forma. Esta forma consiste en meter estas propiedades de conexión en un fichero estándar de propiedades. Veamos como hacerlo con grails.

En nuestro proyecto grails hay un fichero Config.groovy en el que se carga la configuración de nuestra aplicación. Al principio del fichero, vemos este comentario

// grails.config.locations = [ "classpath:${appName}-config.properties",
//                             "classpath:${appName}-config.groovy",
//                             "file:${userHome}/.grails/${appName}-config.properties",
//                             "file:${userHome}/.grails/${appName}-config.groovy"]

// if(System.properties["${appName}.config.location"]) {
//    grails.config.locations << "file:" + System.properties["${appName}.config.location"]
// }

Esta es la forma que tiene grails de cargar ficheros de propiedades que nosotros definamos. Basta poner en grails.config.locations una lista de ficheros de propiedades que queramos cargar. Cada fichero lleva delante "classpath:" o "file:" para indicar si el fichero está en el classpath de nuestra aplicación o si es un fichero normal. Luego, símplemente, debemos poner el nombre de fichero (y el path, si es neceario). Así, por ejemplo, podemos poner

grails.config.locations = ["classpath:chukanboard.properties"]

para indicar que el fichero chukanboard.properties que queremos cargar está en el classpath, o por ejemplo

grails.config.locations = ["file:../conf/chukanboard.properties"]

para indicar que el fichero está en ../conf.

¿Cómo ponemos estos ficheros para que queden bien en su sitio?

Si queremos el classpath, una buena opción es poner el fichero de propiedades en el mismo directorio de fuentes de configuración, es decir, en grails-app/conf, junto con Config.groovy, DataSource.groovy, etc. Al hacerlo así, cuando generemos el war con grails war, o cuando compilemos con el IDE, este fichero chukanboard.properties acabará en WEB-INF/classes, junto con Config.class y DataSource.class

grails-app
 +---- conf
         +--- Config.groovy
         +--- DataSource.groovy
         +--- chukanboard.properties

Aunque no sea muy elegante poner el fichero de propiedades junto con los fuentes, al menos de esta manera todo el proceso es automático (al compilar el fichero acabará en el classpath). Si optamos por un directorio propio aparte, lo más posible es que tengamos que hacer algo de forma manual para que ese fichero acabe en el classpath.

Si queremos un file normal, nos encontramos con las siguientes pegas:

El directorio de ejecución cuando desplegamos un war, por ejemplo en Tomcat, es el directorio donde se ejecuta el servidor web (por ejemplo, el directorio bin de donde esté instalado Tomcat). Eso nos obliga a poner nuestro fichero de propiedades en un sitio ajeno a nuestra aplicación, o presuponer unos paths "raros". Así, por ejemplo, si está es la estructura de directorios de un Tomcat con nuestra aplicación ChuKanBoard desplegada

TOMCAT_HOME
  +--- bin
  |     +---- catalina.sh    //directorio actual de ejecución.
  +--- webapps
        +--- ChuKanBoard
               +--- WEB-INF
                       +--- chukanboard.properties

debemos poner

grails.config.locations = ["file:../webapps/ChuKanBoard/WEB-INF/chukanboard.properties]

Esto, claramente, puede no funcionar si se despliega de otra forma o en otro servidor que no sea Tomcat.

Otra opción es poner un path absoluto, que quizás en entornos linux no es tan descabellado

grails.config.locations = ["file:/etc/ChuKanBoard/chukanboard.properties]

pero desde luego, tenemos que pedir al usuario que ponga el fichero en ese sitio, ya que nuestro war no va a colocarlo ahí cuando se despligue.

También podemos usar variables de entorno, pero o bien le decimos al usuario que debe declararla, o bien usamos ${userHome}, que es la única que se tiene disponible.

grails.config.locations = ["file:${userHome}/chukanboard.properties]

y nuevamente debemos pedir al usuario que coloque el fichero en su HOME, ya que el war no va a dejarlo ahí.

De momento, grails no ofrece una solución mejor a dónde ubicar los ficheros de propiedades ni permite, en el fichero Config.groovy, referencia al path donde se despliega nuestro war (El servletContext no está inicializado todavía en este punto del arranque).

Supongamos que nos decidimos por el classpath, ya que de alguna forma, es lo más automático. Vamos a poner ahí un fichero de propiedades con la conexión a la base de datos. Definimos el fichero de propiedades, al que llamamos chukanboard.properties, de esta manera

dataSource.url = jdbc:mysql://localhost/chukanboard
dataSource.driverClassName = com.mysql.jdbc.Driver
dataSource.username = grails
dataSource.password = grails

y lo colocamos, como hemos dicho, junto a Config.groovy y DataSource.groovy, en el directorio grails-app/conf. En el fichero Config.groovy debemos además poner la línea

grails.config.locations = ["classpath:chukanboard.properties"]

y con esto queda todo listo. Las propiedades del fichero sobreescribirán a las del DataSource.groovy, haciendo que nuestra conexión sea la MySQL que hemos definidio en el fichero de propiedades. Si el usuario quiere cambiar los datos de conexión, basta con que edite, modifique el fichero y rearranque la aplicación desplegada en el Tomcat para obligarla a releer las nuevas propiedades.