Gestionando relaciones en MyBatis

2
17085

Gestionando relaciones en MyBatis

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

En el último Codemotion tuve la oportunidad de dar una charla sobre este framework pero por falta de tiempo la gestión de las relaciones quedo sin explicar adecuadamente. Por lo que con este tutorial espero aclararlo.

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 se vio en el charla y 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. Estado inicial

Lo que se pretende con este tutorial es ver como mapear las relaciones en el siguiente caso de uso. Tenemos «Autores» y tenemos «Tutoriales» donde un «Autor» puede escribir n «Tutoriales» y un «Tutorial» solo puede ser escrito por un «Autor».

Sin ningún tipo de mapeo entre relaciones tenemos el siguiente resultmap para el caso de los «Tutoriales»:


	<resultMap type="com.autentia.model.Tutorial" id="tutorialResult">
		<id column="id_tutorial" property="idTutorial" javaType="int"/>
	    <result column="titulo" property="titulo" javaType="string"/>
	    <result column="categoria" property="categoria" javaType="string"/>
	    <result column="valoracion" property="valoracion" javaType="int"/>
	</resultMap>

Y este sería el resultmap para el caso de los «Autores»:

	
	<resultMap type="com.autentia.model.Autor" id="autorResult">
		<id column="id_autor" property="idAutor" javaType="int"/>
	    <result column="nombre" property="nombre" javaType="string"/>
	    <result column="apellidos" property="apellidos" javaType="string"/>
	</resultMap>
	

Viendo estos resultmap es inmediato ver que tenemos una tabla «Tutoriales» con los campos «id_tutorial», «titulo», «categoria» y «valoración» y una tabla «Autores» con los campos «id_autor», «nombre» y «apellidos».

4. Realizamos el mapeo 1 a N (Lado de 1)

Esto quiere decir que vamos a mapear la relación donde un tutorial solo puede estar escrito por un autor. En el modelo E/R esto significa que la clave id_autor de «Autores» pasa a ser clave foránea de la tabla «Tutoriales» y que por tanto en nuestro modelo vamos a tener un nuevo atributo en la clase «Tutorial» que será de la clase «Autor».

En las consultas que recuperan información de los tutoriales sin importar el autor podemos seguir utilizando el resultmap con id «tutorialResult», solo que cuando hagamos «getAutor()» nos devolverá «null».

Si queremos recuperar también la información del «Autor» asociado al «Tutorial» debemos añadir un nuevo resultmap donde le indicamos la relación de esta forma en el fichero TutorialesMapper.xml:


	<resultMap type="com.autentia.model.Tutorial" id="tutorialResultWithAutor">
		<id column="id_tutorial" property="idTutorial" javaType="int"/>
	    <result column="titulo" property="titulo" javaType="string"/>
	    <result column="category" property="category" javaType="string"/>
	    <result column="valoracion" property="valoracion" javaType="int"/>
	    <association property="autor" column="id_autor" 
	    resultMap="com.autentia.dao.AutoresMapper.autorResult"/>
	</resultMap>

Como se puede ver, utilizamos la etiqueta «association» donde le indicamos cual es la propiedad de nuestro modelo, cual es la columna de la base de datos donde se almacena la clave foránea y que resultmap es el encargado de mapear esta información.

Ahora podemos utilizar este resultmap en las consultas de «Tutoriales» donde nos interese tener también la información del «Autor»; como en este ejemplo:


	<select id="getByIdWithAutor" parameterType="int" resultMap="tutorialResultWithAutor">
		select id_tutorial, titulo, categoria, valoracion, tutoriales.id_autor, nombre, apellidos 
		from tutoriales
		INNER JOIN autores ON tutoriales.id_autor = autores.id_autor
		where id_tutorial = #{id_tutorial}
	</select>

Viendo la SQL que ejecutamos vemos que tenemos que hacer un join entre las tablas «Tutoriales» y «Autores» a fin de recuperar toda la información que necesitamos. Esta es la forma más eficiente de recuperar esta información.

Ahora cuando ejecutamos el método «getByIdWithAutor()» recuperaremos un objeto de la clase «Tutorial» con todos los atributos incluso el de «Autor» rellenos con los datos que haya en las tablas.

5. Realizamos el mapeo 1 a N (Lado de N)

En este caso vamos a mapear la relación donde un «Autor» puede escribir n «Tutoriales». Es decir, vamos a tener una relación bidireccional por lo que en nuestro modelo dentro de la clase «Autor» vamos a tener una lista de elementos de la clase «Tutorial». Como en el caso anterior para poder recuperar esta información con una úinca sentencia tenemos que añadir el siguiente resultmap al fichero AutoresMapper.xml.

	<resultMap type="com.autentia.model.Autor" id="autorResultWithTutoriales">
		<id column="id_autor" property="idAutor" javaType="int"/>
	    <result column="nombre" property="nombre" javaType="string"/>
	    <result column="apellidos" property="apellidos" javaType="string"/>
	    <collection property="tutoriales" javaType="ArrayList" ofType="com.autentia.model.Tutorial"
	    column="id_autor" resultMap="com.autentia.dao.TutorialesMapper.tutorialResult"/>
	</resultMap>

A través de la etiqueta «collection» establecemos cual es la propiedad del modelo, el tipo de la lista, el tipo del dato de que cada elemento de la lista, la columna relacionada en la tabla y con que resultmap lo vamos a mapear.

Fijaos que estamos utilizando el resultmap de «TutorialesMapper» que no recupera los datos del «Autor» porque ya lo tenemos, en el caso de querer también los datos del autor por cada tutorial bastaría con cambiar el resultmap de «tutorialResult» por «tutorialResultWithAutor».

De esta forma cuando llamemos a un método de la interfaz que ejecuta la siguiente operación:

	<select id="getAutorWithTutoriales" parameterType="int" resultMap="autorResultWithTutoriales">
		select tutoriales.id_autor, nombre, apellidos, id_tutorial, titulo, categoria, valoracion 
		from autores inner join tutoriales on autores.id_autor = tutoriales.id_autor 
		where tutoriales.id_autor = #{idAutor}
	</select>

Automáticamente recuperamos un autor con toda la información incluida la lista de tutoriales que tenga asociada.

6. Conclusiones

Con este tutorial trato de mostrar lo fácil que es hacer un mapeo de relaciones con MyBatis y sobre todo que en ningún momento perdemos el control de lo que se está ejecutando en la base de datos.

Cualquier duda o sugerencia en la zona de comentarios.

Saludos.

2 COMENTARIOS

  1. Buenas Ruben, Excelente tutorial, de hecho te vi en el codemotion con la charla de Mybatis y estuvo genial!!, tengo la posibilidad de aplicarlo en un proyecto que estoy haciendo, pero me he quedado atascado con un problema al mapear dos entidades que tienen el mismo nombre de clave primaria(id),al hacer el inner, no consigo que se mapeen de manera correcta.

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