Ejecutando MyBatis contra distintas bases de datos

1
12337

Ejecutando MyBatis contra distintas bases de datos

0. Índice de
contenidos.

1. Entorno

Este tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Mac Book Pro 17″ (2,6 Ghz Intel Core i7, 8 GB DDR3)
  • Sistema Operativo: Mac OS X Snow Leopard 10.6.4
  • VirtualBox 4.1.12
  • MyBatis 3.2.3

2. Introducción

Uno de los problemas más comunes que nos encontramos a la hora de crear una librería de acceso a datos es el de tener que trabajar con más de una base de datos de distinta naturaleza en cada entorno. Un ejemplo típico es cuando en producción la aplicación tiene que funcionar con un base de datos muy pesada y con licencia como Oracle o SQLServer y nosotros tenemos en nuestro entorno de desarrollo algo más ligero y gratis como PostgreSQL o MySQL.

Esto en sí, no sería un problema si todos los gestores de base de datos respetaran el estándar. Esto suele ser así para operaciones básicas pero, por ejemplo, a la hora de paginar resultados o llamar a procedimientos almacenados la sintaxis difiere bastante.

Para seguir este tutorial antes es recomendable haber leído este otro, donde se crea un proyecto Maven desde 0 con el soporte de Spring para MyBatis; y que puede ayudar a entender el código que está publicado en GitHub para vuestro uso y disfrute, aconsejo ejecutar los tests e ir viendo que es lo que hace y sale por consola.

3. Vamos al lío

Partimos de un entorno en el cual los desarrolladores trabajan con PostgreSQL y en producción tenemos una instancia de Oracle. Lo primero que tenemos que hacer es indicar a MyBatis con qué vendors de base de datos va a trabajar. Para hacer esto editamos el fichero applicationContext.xml de nuestra librería donde tenemos definidos los beans de MyBatis y añadimos los siguientes:


	<bean id="vendorProperties"
		class="org.springframework.beans.factory.config.PropertiesFactoryBean">
		<property name="properties">
			<props>
				<prop key="Oracle">oracle
				<prop key="PostgreSQL">postgresql
			</props>
		</property>
	</bean>

	<bean id="databaseIdProvider" class="org.apache.ibatis.mapping.VendorDatabaseIdProvider">
		<property name="properties" ref="vendorProperties" />
	</bean>

Ahora añadimos la propiedad «databaseIdProvider» a la declaración del SQLSessionFactoryBean quedando de esta forma:

	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="mapperLocations" value="classpath*:mappers/*.xml" />
		<property name="dataSource" ref="dataSource" />
		<property name="databaseIdProvider" ref="databaseIdProvider"/>
	</bean>
	

Hay que tener en cuenta que el valor de la propiedad «databaseIdProvider» se calcula automáticamente a partir del datasource proporcionado y que para saber la key que se corresponde exactamente con nuestro datasource, lo mejor que podemos hacer es ejecutar el siguiente test:


	@Test
	public void knowsDatabaseKey(){
		try {
			System.out.println(sqlSession.getConnection().getMetaData().getDatabaseProductName());
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

El texto que devuelva el test será la key del elemento de la lista de propiedades del bean vendorProperties; el valor de la propiedad puede ser cualquiera pero será el que tenga que coincidir con el valor de la propiedad «databaseId» de las operaciones del mapper.

Añadiendo el «databaseIdProvider» dentro de un mismo mapper podemos tener varios elementos con el mismo «id» pero con la propiedad «databaseId» distinta. De esta forma las operaciones con un id específico solo serán ejecutadas cuando el «databaseIdProvider» que se esté utilizando coincida.

Por ejemplo, sabemos que la sintaxis de las llamadas a procedimientos almacenados en Oracle y en PostgreSQL difieren. Por lo tanto podemos tener un mismo método sumar con dos databaseIds distintos, de esta forma:

En Oracle:


	<select id="sumar" parameterType="objOperandosResultado" 
	statementType="CALLABLE" databaseId="oracle">    	
		{ CALL sumar(        
		#{op1, mode=IN, jdbcType=INTEGER},        
		#{op2, mode=IN, jdbcType=INTEGER},
		#{total, mode=OUT, jdbcType=INTEGER})
        }	
    </select>
	

En PostgreSQL:


	<select id="sumar" parameterType="map" 
	resultType="int" databaseId="postgresql">    	
		SELECT * FROM sumar(#{a},#{b});	
	</select>
	

Recordad que el valor de la propiedad «databaseIdProvider» se calcula automáticamente a partir del datasource proporcionado. Por tanto, el método solo se va a ejecutar cuando la aplicación se ejecute contra el datasource indicado en la propiedad «databaseId» de la operación. En caso de no haya correspondencia lanzará un error diciendo que no ha encontrado la operación que implementa el método solicitado. Este supuesto solo ocurriría cuando el método se ejecutase contra un datasource cuyo driver fuera distinto de Oracle o de PostgreSQL. Para evitar esto pondríamos una tercera operación «sumar» sin especificar la propiedad «databaseId» a fin de que ese método se ejecute con cualquier datasource si es lo que queremos en nuestro proyecto.

4. Conclusiones

Como veis, no es difícil configurar MyBatis para que pueda lanzar un método contra una base de datos u otra dependiendo del datasource que se esté empleando y es una manera elegante de solventar el problema de los cambios de sintaxis en los gestores de base de datos.

Cualquier duda o sugerencia en la zona de comentarios.

Saludos.

1 COMENTARIO

  1. Hola Rubén, ante todo agradecerte por el post, por otra parte tengo una consulta ¿En qué parte debería especificar el url del driver, usuario y password tanto para el oracle como para el postgresql?
    Gracias.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad