Primeros pasos
Instalación
Para usar MyBatis sólo tienes que incluir el fichero mybatis-x.x.x.jar en el classpath.
Si usas Maven añade esta dependencia en tu pom.xml:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
Cómo crear un SqlSessionFactory a partir de XML
Una aplicación que usa MyBatis debe utilizar una instancia de SqlSessionFactory. Se puede obtener una instancia de SqlSessionFactory mediante un SqlSessionFactoryBuilder. Un SqlSessionFactoryBuilder puede construir una instancia de SqlSessionFactory a partir de un fichero de configuración XML o de una instancia personalizada de la clase Configuration.
Crear una instancia SqlSessionFactory desde un fichero xml es muy sencillo. Se recomienda usar un classpath resource, pero es posible usar cualquier InputStream, incluso creado con un path de fichero o una URL de tipo file://
. MyBatis proporciona una clase de utilidad, llamada Resources, que contiene métodos que simplifican la carga de recursos desde el classpath u otras ubicaciones.
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
El fichero de configuración XML contiene la configuración del core de MyBatis, incluyendo el DataSource para obtener instancias de conexión a la base de datos y también un TransactionManager para determinar cómo deben controlarse las transacciones. Los detalles completos de la configuración XML se describen más adelante en este documento, pero continuación se muestra un ejemplo sencillo:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
Aunque hay mucha más información sobre el fichero de configuración XML, el ejemplo anterior contiene las partes más importantes. Observa que hay una cabecera XML, requerida para validar el fichero XML. El cuerpo del elemento environment contiene la configuración de la gestión de transacciones correspondiente al entorno. El elemento mappers contiene la lista de mappers – Los ficheros XML que contienen las sentencias SQL y las definiciones de mapeo.
Cómo crear un SqlSessionFactory sin XML
Si lo prefieres puedes crear la configuración directamente desde Java, en lugar de desde XML, o crear tu propio builder . MyBatis dispone una clase Configuration que proporciona las mismas opciones de configuración que el fichero XML.
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
Puedes observar que en este caso la configuración está añadiendo una clase mapper. Las clases mapper son clases Java que contienen anotaciones de mapeo SQL que permiten evitar el uso de XML. Sin embargo el XML sigue siendo necesario en ocasiones, debido a ciertas limitaciones de las anotaciones Java y la complejidad que pueden alcanzar los mapeos (ej. mapeos anidados de Joins). Por esto, MyBatis siempre busca si existe un fichero XML asociado a la clase mapper (en este caso, se buscará un fichero con nombre BlogMapper.xml cuyo nombre deriva del classpath y nombre de BlogMapper.class). Hablaremos más sobre esto más adelante.
Cómo obtener un SqlSession a partir del SqlSessionFactory
Ahora que dispones de un SqlSessionFactory, tal y como su nombre indica, puedes adquirir una instancia de SqlSession. SqlSession contiene todos los métodos necesarios para ejecutar sentencias SQL contra la base de datos. Puedes ejecutar mapped statements con la instancia de SqlSession de la siguiente forma:
try (SqlSession session = sqlSessionFactory.openSession()) {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}
Aunque esta forma de trabajar con la SqlSession funciona correctamente y les será familiar a aquellos que han usado las versiones anteriores de MyBatis, actualmente existe una opción más recomendada. Usar un interface (ej. BlogMapper.class) que describe tanto el parámetro de entrada como el de retorno para una sentencia. De esta forma tendrás un código más sencillo y type safe, sin castings ni literales de tipo String que son fuente frecuente de errores.
Por ejemplo:
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}
Vemos con detalle cómo funciona esto.
Cómo funcionan los Mapped Statements
Te estarás preguntando qué se está ejecutando en SqlSession o en la clase Mapper. Los Mapped Statements son una materia muy densa, y será el tema que domine la mayor parte de esta documentación. Pero, para que te hagas una idea de qué se está ejecutando realmente proporcionaremos un par de ejemplos.
En cualquiera de los ejemplos a continuación podrían haberse usado indistintamente XML o anotaciones. Veamos primero el XML. Todas las opciones de configuración de MyBatis pueden obtenerse mediante el lenguaje de mapeo XML que ha popularizado a MyBatis durante años. Si ya has usado MyBatis antes el concepto te será familiar, pero verás que hay numerosas mejoras en los ficheros de mapeo XML que iremos explicando más adelante. Por ejemplo este mapped statement en XML haría funcionar correctamente la llamada al SqlSession que hemos visto previamente.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
Aunque pudiera parecer que hay excesivo XML para un ejemplo tan simple, en realidad no hay tanto. Puedes definir tantos mapped statements en un solo fichero XML como quieras así que rentabilizarás las líneas XML extra que corresponden a la cabecera XML y a la declaración de doctype. El resto del fichero se explica por sí mismo. Define un nombre para el mapped statement “selectBlog”, en un namespace (espacio de nombres) “org.mybatis.example.BlogMapper”, que permite realizar una llamada especificando el nombre completo (fully qualified) “org.mybatis.example.BlogMapper.selectBlog” tal y como muestra el código a continuación:
Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
Observa el gran parecido que hay entre esta llamada y la llamada a una clase java y hay una razón para eso. Este literal puede mapearse con una clase que tenga el mismo nombre que el namespace, con un método que coincida con el nombre del statement, y con parámetros de entrada y retorno iguales que los del statement. Esto permite que puedas hacer la misma llamada contra una interfaz Mapper tal y como se muestra a continuación:
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
Este segunda forma de llamada tiene muchas ventajas. Primeramente, no se usan literales de tipo String lo cual es mucho más seguro dado que los errores se detectan en tiempo de compilación. Segundo, si tu IDE dispone de autocompletado de código podrás aprovecharlo.
NOTA Una nota sobre los namespaces.
Los namespaces (espacios de nombres) eran opcionales en versiones anteriores de MyBatis, lo cual creaba confusión y era de poca ayuda. Los namespaces son ahora obligatorios y tienen un propósito más allá de la clasificación de statements.
Los namespaces permiten realizar el enlace con los interfaces como se ha visto anteriormente, e incluso si crees que no los vas a usar a corto plazo, es recomendable que sigas estas prácticas de organización de código por si en un futuro decides hacer lo contrario. Usar un namespace y colocarlo en el paquete java que corresponde con el namespace hará tu código más legible y mejorará la usabilidad de MyBatis a largo plazo.
Resolución de nombres: Para reducir la cantidad de texto a escribir MyBatis usa las siguientes normas de resolución de nombres para todos los elementos de configuración, incluidos statements, result maps, cachés, etc.
- Primeramente se buscan directamente los nombres completamente cualificados (fully qualified names) (ej. “com.mypackage.MyMapper.selectAllThings”).
- Pueden usarse los nombres cortos (ej. “selectAllThings”) siempre que no haya ambigüedad. Sin embargo, si hubiera dos o más elementos (ej. “com.foo.selectAllThings" y "com.bar.selectAllThings”), entonces obtendrás un error indicando que el nombre es ambiguo y que debe ser “fully qualified”.
Hay otro aspecto importante sobre las clases Mapper como BlogMapper. Sus mapped statements pueden no estar en ningún fichero XML. En su lugar, pueden usarse anotaciones. Por ejemplo, el XML puede ser eliminado y reemplazarse por:
package org.mybatis.example;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
Las anotaciones son mucho más claras para sentencias sencillas, sin embargo, las anotaciones java son limitadas y más complicadas de usar para sentencias complejas. Por lo tanto, si tienes que hacer algo complejo, es mejor que uses los ficheros XML.
Es decisión tuya y de tu proyecto cuál de los dos métodos usar y cómo de importante es que los mapped statements estén definidos de forma consistente. Dicho esto, no estás limitado a usar un solo método, puedes migrar fácilmente de los mapped statements basados en anotaciones a XML y viceversa.
Ámbito y ciclo de vida
Es muy importante entender los distintos ámbitos y ciclos de vida de las clases de las que hemos hablado hasta ahora. Usarlas de forma incorrecta puede traer serias complicaciones.
NOTA Ciclo de vida de los objetos y frameworks de inyección de dependencias
Los frameworks de inyección de dependencias pueden crear SqlSessions y mappers thread safe (reeentrante) y transaccionales, e inyectarlos directmaente en tus beans de forma que puedes olvidarte de su ciclo de vida. Echa un vistazo a los sub-projectos MyBatis-Spring o MyBatis-Guice para conocer más detalles sobre cómo usar MyBatis con estos frameworks.