Database MessageSource: obtener los literales de una base de datos

1
11014

Database MessageSource: obtener los literales de una base 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
  • Spring 3.1.0.RC1
  • Mybatis 3.0.4

2. Introducción

Lo más normal a la hora de internacionalizar aplicaciones es contar con ficheros de propiedades que almacenan los literales que se utilizan, los famosos ResourceBundle, que nos permite tener n ficheros cada uno del idioma que queramos visualizar. Este es el funcionamiento habitual a la hora de hacer i18n. En este tutorial vamos a ver como obtener estos literales almacenados en nuestra propia base de datos y seguir utilizando el estándar de Spring para no tener que cambiar el código de la aplicación, tanto para JSP’s cuando utilizamos la etiqueta <spring:message code=»code»/> o cuando utilizamos la interfaz MessageSource en nuestro código Java, así como la forma de parametrizar los mensajes.

3. Creación del proyecto de prueba

Como siempre vamos a demostrar lo que decimos y que mejor que crear un proyecto con Maven e importarlo a Eclipse. El proyecto va a utilizar Spring y Mybatis para el acceso a la base de datos, aquí tenéis un tutorial al respecto.

Definimos el pom.xml del proyecto de esta forma:



	4.0.0

	com.autentia
	custom-messagesource
	1.0-SNAPSHOT
	jar

	custom-messagesource
	http://maven.apache.org

	
		UTF-8
		3.1.0.RC1
	
	
	
		
			
				maven-compiler-plugin
				
					UTF-8
					1.6
					1.6
				
			
		
	

	
		
			junit
			junit
			4.7
			test
		
		
			org.mybatis
			mybatis
			3.0.4
		
		
			org.mybatis
			mybatis-spring
			1.0.0-RC2
		
		
			org.springframework
			spring-test
			${spring.version}
			test
		
		
			org.springframework
			spring-context
			${spring.version}
		
		
			org.springframework
			spring-core
			${spring.version}
		
		
			javax.annotation
			jsr250-api
			1.0
		
		
			postgresql
			postgresql
			9.0-801.jdbc4
		
		
			cglib
			cglib
			2.2
		
		
			log4j
			log4j
			1.2.16
		
	


Ahora vamos a crear la tabla en la base de datos que va a almacenar los literales. Simplemente va a ser una tabla que va a tener 3 columnas:

  • codLiteral: almacena el código numérico del literal. INTEGER NOT NULL
  • literal: almacena el texto del literal. VARCHAR (400)
  • locale: almacena el idioma del literal. VARCHAR (10)

La clave primaria está compuesta por los campos codLiteral y locale, de forma que a través del mismo código vamos a poder recuperar el literal correspondiente al idioma deseado y no puede repetirse un mismo código para un mismo locale.

Cargamos una serie de datos de prueba.

Ya tenemos el entorno de desarrollo preparado.

4. Vamos al lío

Lo primero que tenemos que resolver es el acceso al literal dado un código y un locale. Para ello vamos a crear el siguiente test:

package com.autentia.dao;

import javax.annotation.Resource;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import com.autentia.model.Literal;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/application-context-test.xml" })
@Transactional
public class LiteralMapperTest {

	@Resource
	LiteralMapper literalMapper;
	
	@Test
	public void testGetLiteralByCodAndLocale() {
		Literal literal = literalMapper.getLiteralByCodAndLocale(1, "es");	
		Assert.assertEquals("Texto 1", literal.getLiteral());
	}

}

Para la implementación del acceso a la base de datos utilizamos Mybatis, para lo cual definimos la interfaz LiteralMapper de esta forma:

package com.autentia.dao;

import org.apache.ibatis.annotations.Param;
import org.mybatis.spring.annotation.Mapper;

import com.autentia.model.Literal;

@Mapper
public interface LiteralMapper {

	public Literal getLiteralByCodAndLocale(@Param("codLiteral") Integer codLiteral, @Param("locale") String locale);
}

La implementación del método definido se hace a través de un mapper en el fichero LiteralMapper.xml de esta forma:



	
	
	


La definición del fichero application-context-text.xml define la configuración de MyBatis y la definición del datasource de prueba.




	

	

	
		
	

	
		
	

	

	
		
		
	

	
		
		
		
		
	

	
		
		
			
				classpath:log4j.properties
			
		
	



Ejecutando este test tenemos que ver que pasa perfectamente y que recupera el literal indicado. De tal forma que ya tenemos un problema resuelto.

Ahora a por el siguiente. Vamos a implementar la clase que se encargue de internacionalizar los literales a través de Spring. Esta clase tiene que extender de AbstractMessageSource e implementar el método resolveCode. Pero antes vamos al test:

package com.autentia;

import java.util.Locale;

import javax.annotation.Resource;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.MessageSource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/application-context-test.xml" })
public class DatabaseMessageSourceTest {

	@Resource
	MessageSource messageSource;
	
	@Test
	public void testResolveCode() {
		
		Assert.assertEquals("Texto 1", messageSource.getMessage("1", null, new Locale("es")));
		
		Assert.assertEquals("Texto 1 Inglés", messageSource.getMessage("1", null, new Locale("en")));
		
		Assert.assertEquals("Texto prueba", messageSource.getMessage("3", new String[]{"prueba"}, new Locale("es")));
		
	}

}

Como veis este código no difiere de la forma habitual de internacionalización de Spring. Tenemos tres tests: el primero de ellos nos devuelve el texto en castellano, el segundo el mismo texto en inglés y el tercero nos devuelve un texto parametrizado.

Ahora creamos la clase DatabaseMessageSource que como ya hemos dicho tiene que extender de AbstractMessageSource e implementar el método resolveCode, para nuestro caso el código quedaría de la siguiente forma:


package com.autentia;

import java.text.MessageFormat;
import java.util.Locale;

import javax.annotation.Resource;

import org.springframework.context.support.AbstractMessageSource;
import org.springframework.stereotype.Service;

import com.autentia.dao.LiteralMapper;
import com.autentia.model.Literal;

@Service
public class DatabaseMessageSource extends AbstractMessageSource{
	
	@Resource
	LiteralMapper literalMapper;

	@Override
	protected MessageFormat resolveCode(String code, Locale locale) {
		Literal literal = literalMapper.getLiteralByCodAndLocale(Integer.parseInt(code), locale.getLanguage());
		return new MessageFormat(literal.getLiteral()); 
	}

}

Simplemente accede a la base de datos con el código y el locale proporcionado para devolver el texto en un objeto MessageFormat gestionado por Spring. Ahora para que el test funcione tenemos que definir el bean «messageSource» indicando que va a ser implementado por la clase DatabaseMessageSource dentro del fichero application-context-test.xml.


Ahora ejecutamos el test y tenemos que ver que pasa perfectamente. Dejo como deberes ver como funciona también en aplicaciones web utilizando la etiqueta <spring:message code=»code»/>

5. Conclusiones

Como veis prácticamente todo ya está resuelto con Spring. Nuestro primer impulso podría ser crear nuestra propia clase que gestionará estos mensajes, pero eso supondría trabajar de una forma no estándar y acoplarnos completamente a esa solución, perdiendo las ventajas de trabajar con Spring.

Cualquier duda o sugerencia en la zona de comentarios.

Saludos.


1 COMENTARIO

  1. Buenas Rubén,

    He leído tu post (y aunque antiguo funciona). consiguiendo con spring boot 2.3.1, leer los mensajes de una base de datos que he creado específicamente para los idiomas (Mysql sobre docker es una máquina virtual debian). Lo que no consigo, con esta opción es leer los mensajes de error de la aplicación ubicados en /src/main/resources/ ValidationMessages.

    Soy nuevo en esto, aunque le pongo muchas ganas y me gustaría saber si me puedes «dar luz» sobre este asunto de tal manera que pueda acceder a la base de datos y a los mencionados ficheros Validation.

    Gracias

    Un saludo

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