mybatis-3 Documentation

MyBatis SQL mapper framework for Java

20,445 stars Java Java 19 files · ~60,941 tokens #java#mybatis#sql
RAW Doc

Technical Documentation: mybatis/mybatis-3

ℹ️ Provenance: In-Tree Documentation (19 Chapters) · CodeWiki Reference · Recency: Active (< 180 days)

File: README.md

MyBatis SQL Mapper Framework for Java








The MyBatis SQL mapper framework makes it easier to use a relational database with object-oriented applications.
MyBatis couples objects with stored procedures or SQL statements using an XML descriptor or annotations.
Simplicity is the biggest advantage of the MyBatis data mapper over object relational mapping tools.

Contributions

See here

Tests

Mybatis-3 code runs more expressive testing depending on jdk usage and platform.

By default, we set <excludedGroups>TestcontainersTests,RequireIllegalAccess</excludedGroups> which will exclude a subset of tests with @Tag('TestcontainersTests') and @Tag('RequireIllegalAccess').

When we run on ci platform, we further make adjustments as needed. See here for details.

As of 12/28/2024, using combined system + jdk will result in given number of tests ran. This will change as tests are added or removed over time.

without adjusting settings (ie use as is, platform does not matter)

  • any OS + jdk 21 = 1899 tests
  • any OS + jdk 25 = 1899 tests

our adjustments for GH actions where platform does matter

  • windows + jdk 21 = 1899 tests

  • windows + jdk 25 = 1899 tests

  • linux + jdk 21 = 1934 tests

  • linux + jdk 25 = 1934 tests

  • mac + jdk 21 = 1899 tests

  • mac + jdk 25 = 1899 tests


File: CONTRIBUTING.md

If you have a question or need any help...

Please use the mailing list instead of creating issues on the tracker. Thank you!

Reporting a bug

  • Create a new issue on the tracker.
  • The best way to report a bug is to create a failing test case. Please see the Contributing code section.

Proposing a new feature

  • It is a good idea to discuss your changes on the mailing list to get feedback from the community.
  • If you have a patch with unit tests, send a pull request. Please see the Contributing code section.

Improving documentation

Contributing code

Formatting

Mybatis-core is now being auto formatted. Given nature of some code logic with mybatis, it is more appropriate to force a formatting structure manually for snippets such as sql statements. To do so, add following blocks around code.

  • // @formatter:off to start the block of unformatted code
  • // @formatter:on to end the block of unformatted code

If comment sections need same behaviour such as javadocs, note that the entire block must be around entire comment as direct usage does not properly indicate that formatter treats it all as one comment block regardless.

  • You are the author of your contributions and will always be.
  • Everything you can find it this project is licensed under the Apache Software License 2.0
  • Every contribution you do must be licensed under the Apache Software License 2.0. Otherwise we will not be able to accept it.
  • Please make sure that all the new files you create hold the following header:
text
/*
 *    Copyright [year] the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       https://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

How to send your modifications as a pull request

The best way to submit a patch is to send a pull request.
Here are the steps of a typical workflow.

  1. Fork the repository on GitHub.
  2. Clone your fork to your local machine.
  3. Create a topic branch with a descriptive name.
  4. Make changes with unit tests in the topic branch.
  5. Push commits to your fork on GitHub.
  6. Send a pull request.

For steps 1 to 3, please read this GitHub help if you are not familiar with these operations.
Step 4 and 5 are basic git operations. Please see the online documentation for its usage.

For how to write a unit test, please see the unit test page.


File: renovate.json

{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
"helpers:pinGitHubActionDigests"
]
}


File: src/site/es/markdown/getting-started.md

title: MyBatis 3 | Primeros pasos
author: Clinton Begin, Eduardo Macarron

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:

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.

java
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
<?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.

java
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:

java
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:

java
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
<?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:

java
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:

java
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:

java
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.


SqlSessionFactoryBuilder

Esta clase puede instanciarse, usarse y desecharse. No es necesario mantenerla una vez que ya has creado la SqlSessionFactory. Por lo tanto el mejor ámbito para el SqlSessionFactoryBuilder es el método (ej. una variable local de método). Puedes reusar el SqlSessionFactoryBuilder para construir más de una instancia de SqlSessionFactory, pero aun así es recomendable no conservar el objeto para asegurarse de que todos los recursos utilizados para el parseo de XML se han liberado correctamente y están disponibles para temas más importantes.

SqlSessionFactory

Una vez creado, el SqlSessionFactory debería existir durante toda la ejecución de tu aplicación. No debería haber ningún o casi ningún motivo para eliminarlo o recrearlo. Es una buena práctica el no recrear el SqlSessionFactory en tu aplicación más de una vez. Y lo contrario debería considerarse código sospechoso. Por tanto el mejor ámbito para el SqlSessionFactory es el ámbito de aplicación. Esto puede conseguirse de muchas formas. Lo más sencillo es usar el patrón Singleton o el Static Singleton.

SqlSession

Cada thread (hilo de ejecución) debería tener su propia instancia de SqlSession. Las instancias de SqlSession no son thread safe y no deben ser compartidas. Por tanto el ámbito adecuado es el de petición (request) o bien el método. No guardes nunca instancias de SqlSession en un campo estático o incluso en una propiedad de instancia de una clase. Nunca guardes referencias a una SqlSession en ningún tipo de ámbito gestionado como la HttpSession. Si estás usando un framework web considera que el SqlSession debería tener un ámbito similar al HttpRequest. Es decir, cuando recibas una petición http puedes abrir una SqlSession y cerrarla cuando devuelvas la respuesta. Cerrar la SqlSession es muy importante. Deberías asegurarte de que se cierra con un bloque finally. A continuación se muestra el patrón estándar para asegurarse de que las sesiones se cierran correctamente.

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  // do work
}

Usando este patrón en todo el código se asegura que los recursos de base de datos se liberarán correctamente.

Instancias de mapper

Los mappers son interfaces que creas como enlace con los mapped statements. Las instancias de mappers se obtienen de una SqlSession. Y por tanto, técnicamente el mayor ámbito de una instancia de Mapper es el mismo que el de la SqlSession de la que fueron creados. Sin embargo el ámbito más recomendable para una instancia de mapper es el ámbito de método. Es decir, deberían ser obtenidos en el método que vaya a usarlos y posteriormente descartarlos. No es necesario que sean cerrados explícitamente. Aunque no es un problema propagar estos objetos por varias clases dentro de una misma llamada, debes tener cuidado porque puede que la situación se te vaya de las manos. Hazlo fácil (keep it simple) y mantén los mappers en el ámbito de método. Este ejemplo muestra esta práctica:

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // do work
}

File: src/site/es/markdown/index.md

title: MyBatis 3 | Introducción
author: Clinton Begin, Eduardo Macarron

Avoid blank site

Introducción

¿Qué es MyBatis?

MyBatis es un framework de persistencia que soporta SQL, procedimientos almacenados y mapeos avanzados. MyBatis elimina casi todo el código JDBC, el establecimiento manual de parámetros y la obtención de resultados. MyBatis puede configurarse con XML o anotaciones y permite mapear tipos de datos primitivos, objetos de tipo Map y POJOs (Plain Old Java Objects) a registros de base de datos.

Colabora para mejorar esta documentación...

Si ves que hay alguna carencia en esta documentación o que falta alguna característica por documentar, te animamos a que lo investigues y la documentes tu mismo!

Las fuentes de este manual están disponibles en formato Markdown en el Git del proyecto. Haz un fork, cámbialas y envía un pull request.

Eres el mejor candidato para documentar porque los lectores de esta documentación son gente como tú!


File: src/site/es/markdown/logging.md

title: MyBatis 3 | Logging
author: Clinton Begin, Eduardo Macarron

Avoid blank site

Logging

MyBatis proporciona información de logging mediante el uso interno de una factoría. La factoría interna delega la información de logging en alguna de las siguientes implementaciones.

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j (deprecated since 3.5.9)
  • JDK logging

La solución de logging elegida se basa en una introspección en tiempo de ejecución de la propia factoría interna de MyBatis. La factoría usará la primera implementación de logging que encuentre (el orden de búsqueda es el de la lista de más arriba). Si no se encuentra ninguna, el logging se desactivará.

Muchos entornos vienen con Commons Logging incluido como pare del classpath del servidor (por ejemplo Tomcat y WebSphere). Es importante conocer que en esos entorno, MyBatis usará JCL como implementación de logging. En un entorno como WebSphere esto significa que tu configuración de log4j será ignorada dado que WebSphere proporciona su propia implementación de JCL. Esto puede ser muy frustrante porque parece que MyBatis está ignorando tu configuración de logging (en realidad, MyBatis está ignorando tu configuración de log4j porque está usando JCL en dicho entorno). Si tu aplicación se ejecuta en un entorno que lleva JCL incluido pero quieres usar un método distinto de logging puedes añadir un setting a tu fichero mybatis-config.xml:

xml
<configuration>
  <settings>
    ...
    <setting name="logImpl" value="LOG4J"/>
    ...
  </settings>
</configuration>

Los valores válidos son: SLF4J, LOG4J, LOG4J2, JDK_LOGGING, COMMONS_LOGGING, STDOUT_LOGGING, NO_LOGGING o un nombre de clase plenamente cualificado que implemente org.apache.ibatis.logging.Log y reciba un string como parametro de constructor.

Tambien puedes seleccionar el método de logging llamando a uno de los siguientes métodos:

java
org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
org.apache.ibatis.logging.LogFactory.useLog4JLogging();
org.apache.ibatis.logging.LogFactory.useLog4J2Logging();
org.apache.ibatis.logging.LogFactory.useJdkLogging();
org.apache.ibatis.logging.LogFactory.useCommonsLogging();
org.apache.ibatis.logging.LogFactory.useStdOutLogging();

Si eliges llamar a alguno de estos métodos, deberías hacerlo antes de llamar a ningún otro método de MyBatis. Además, estos métodos solo establecerán la implementación de log indicada si dicha implementación está disponible en el classpath. Por ejemplo, si intentas seleccionar log4j2 y log4j2 no está disponible en el classpath, MyBatis ignorará la petición y usará su algoritmo normal de descubrimiento de implementaciones de logging.

Los temas específicos de JCL, Log4j y el Java Logging API quedan fuera del alcance de este documento. Sin embargo la configuración de ejemplo que se proporciona más abajo te ayudará a comenzar. Si quieres conocer más sobre estos frameworks, puedes obtener más información en las siguientes ubicaciones:

Configuración

Para ver el log de las sentencias debes activar el log en un paquete, el nombre plenamente cualificado de una clase, un namespace o un nombre plenamente cualificado de un mapped statement.

Nuevamente, cómo hagas esto es dependiente de la implementación de logging que se esté usando. Mostraremos cómo hacerlo con SLF4J(Logback). Configurar los servicios de logging es simplemente cuestión de añadir uno o varios ficheros de configuración (por ejemplo logback.xml) y a veces un nuevo JAR. El ejemplo siguiente configura todos los servicios de logging para que usen SLF4J(Logback) como proveedor. Sólo son dos pasos:

Paso 1: Añade el fichero SLF4J + Logback JAR

Dado que usamos SLF4J(Logback), necesitaremos asegurarnos que el fichero JAR está disponible para nuestra aplicación. Para usar SLF4J(Logback), necesitas añadir el fichero JAR al classpath de tu aplicación.

En aplicaciones Web o de empresa debes añadir tu fichero logback-classic.jar ,logback-core.jar and slf4j-api.jar a tu directorio WEB-INF/lib, y en una aplicación standalone simplemente añádela al parámetro –classpath de la JVM.

If you use the maven, you can download jar files by adding following settings on your pom.xml.

xml
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.x.x</version>
</dependency>

Paso 2: Configurar Logback

Configurar Logback es sencillo. Supongamos que quieres habilitar el log para este mapper:

java
package org.mybatis.example;
public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

Crea un fichero con nombre logback.xml como el que se muestra a continuación y colocalo en tu classpath:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>

  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%5level [%thread] - %msg%n</pattern>
    </encoder>
  </appender>

  <logger name="org.mybatis.example.BlogMapper">
    <level value="trace"/>
  </logger>
  <root level="error">
    <appender-ref ref="stdout"/>
  </root>

</configuration>

El fichero anterior hará que SLF4J(Logback) reporte información detallada para org.mybatis.example.BlogMapper e información de errores para el resto de las clases de tu aplicación.

Si quieres activar un nivel más fino de logging puedes activar el logging para statements específicos en lugar de para todo un mapper. La siguiente línea activa el logging sólo para el statement selectBlog:

xml
<logger name="org.mybatis.example.BlogMapper.selectBlog">
  <level value="trace"/>
</logger>

Si por el contrario quieres activar el log para un grupo de mappers debes añadir un logger para el paquete raiz donde residen tus mappers:

xml
<logger name="org.mybatis.example">
  <level value="trace"/>
</logger>

Hay consultas que pueden devolver una gran cantidad de datos. En esos casos puedes querer ver las sentencias SQL pero no los datos. Para conseguirlo las sentencias se loguean con nivel DEBUG (FINE en JDK) y los resultados con TRACE (FINER en JDK), por tanto si quieres ver la sentencia pero no el resultado establece el nivel a DEBUG

xml
<logger name="org.mybatis.example">
  <level value="debug"/>
</logger>

Y si estás usando ficheros XML como este?

xml
<?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>

En tal caso puedes activar el logging de todo el fichero añadiendo un logger para el namespace como se muestra a continuación:

xml
<logger name="org.mybatis.example.BlogMapper">
  <level value="trace"/>
</logger>

O para un statement específico:

xml
<logger name="org.mybatis.example.BlogMapper.selectBlog">
  <level value="trace"/>
</logger>

Sí, como ya te habrás dado cuenta, no hay ninguna diferencia entre configurar el logging para un mapper o para un fichero XML.

NOTA Si usas SLF4J o Log4j 2 MyBatis le llamará usando MYBATIS como marker.

El resto de la configuración sirve para configurar los appenders, lo cual queda fuera del ámbito de este documento. Sin embargo, puedes encontrar más información en el site de Logback. O, puedes simplemente experimentar para ver los efectos que consigues con las distintas opciones de configuración.

Configuration example for Log4j 2

xml
<!-- pom.xml -->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.x.x</version>
</dependency>
xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- log4j2.xml -->
<Configuration xmlns="http://logging.apache.org/log4j/2.0/config">

  <Appenders>
    <Console name="stdout" target="SYSTEM_OUT">
      <PatternLayout pattern="%5level [%t] - %msg%n"/>
    </Console>
  </Appenders>

  <Loggers>
    <Logger name="org.mybatis.example.BlogMapper" level="trace"/>
    <Root level="error" >
      <AppenderRef ref="stdout"/>
    </Root>
  </Loggers>

</Configuration>

Configuration example for Log4j

xml
<!-- pom.xml -->
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
properties
# log4j.properties
log4j.rootLogger=ERROR, stdout

log4j.logger.org.mybatis.example.BlogMapper=TRACE

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

Configuration example for JDK logging

properties
# logging.properties
handlers=java.util.logging.ConsoleHandler
.level=SEVERE

org.mybatis.example.BlogMapper.level=FINER

java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%1$tT.%1$tL %4$s %3$s - %5$s%6$s%n

File: src/site/fr/markdown/getting-started.md

title: MyBatis 3 | Premiers pas
author: Clinton Begin, Dridi Boukelmoune

Premiers pas

Chaque application MyBatis tourne autour d'une instance de SqlSessionFactory. Une instance de SqlSessionFactory peut être obtenue à l'aide du SqlSessionFactoryBuilder. SqlSessionFactoryBuilder peut créer une instance de SqlSessionFactoryBuilder à partir d'un fichier XML de configuration, où à partir d'une instance personnalisée de la classe Configuration.

Créer une SqlSessionFactory à partir d'un fichier XML

Créer une instance de SqlSessionFactory à partir d'un fichier XML est très simple. Il est recommandé d'utiliser un fichier présent dans le classpath, mais on peut utiliser n'importe quelle instance d'InputStream, y compris à partir d'un chemin vers un fichier ou d'une URL de type file://. MyBatis contient une classe utilitaire, Resources, qui contient plusieurs méthodes facilitant le chargement de ressources du classpath ou d'ailleurs.

java
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

Le fichier de configuration XML contient les réglages pour le noyeau de MyBatis, y compris une DataSource pour obtenir les connections vers la base de données, ainsi qu'un TransactionManager pour contrôler les transactions et déterminer leur périmètre. Le détail complet du fichier de configuration XML est décrit plus tard dans le document, mais voici un exemple simple:

xml
<?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>

Le fichier de configuration XML est loin de se limiter à cela, mais l'exemple ci-dessus présente les points les plus critiques. Attention à l'entête XML, nécessaire à la validation du document XML. L'élément environment contient la configuration du gestionnaire de transactions et du pool de connexions. L'élément mappers contient une liste de mappers, des fichiers XML qui contiennent les requêtes SQL et le mapping objet-relationnel.

Créer une SqlSessionFactory sans XML

Il est possible de créer la configuration directement en Java, plutôt qu'en XML. MyBatis fournit une classe Configuration qui propose les mêmes options de configuration qu'avec un fichier XML.

java
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);

On remarque dans ce cas l'ajout d'une classe mapper. Il est possible de configurer MyBatis à l'aide d'annotations. Cependant, en raison de certaines limitations des annotations faces aux cas les plus complexes de mapping avec MyBatis, XML est toujours requis (e.g. Nested Join Mapping). Pour cette raison, MyBatis cherchera toujours la présence d'un fichier XML (dans le cas présent, BlogMapper.xml serait chargé en se basant sur le classpath et le nom de BlogMapper.class). Le sujet sera approfondi plus tard.

Acquérir une SqlSession à partir d'une SqlSessionFactory

Maintenant que nous avons une SqlSessionFactory, comme le nom le suggère, nous pouvons créer une instance de SqlSession. Elle contient la totalité des méthodes nécessaires à l'exécution de requêtes SQL. On peut directement exécuter une requête d'un mapper à partir d'une SqlSession. Par exemple :

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}

Même si cette approche fonctionne, et qu'elle est bien connue des vétérans de MyBatis, il existe une approche plus propre. En utilisant une interface (e.g. BloggerMapper.class) qui décrit correctement les paramètres et la valeur de retour pour une requête particulière, on peut désormais exécuter du code plus propre et fortement typé et éviter les erreurs classiques de transtypage.

Par exemple:

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}

Explorons maintenant ce qui est exécuté ici.

A la découverte des requêtes SQL mappées

Arrivé ici vous vous demandé peut-être ce qui est exécuté par la SqlSession ou le Mapper. Le sujet des requêtes SQL mappées est vaste, et il sera prépondérant dans la majorité de cette documentation. Mais pour vous donner une idée de ce qui est exécuté, voici quelques exemples.

Dans chacun des exemples ci-dessus, les requêtes auraient pu être définies aussi bien en XML qu'avec des annotations. Jetons d'abord un coup d'œil à l'XML. La totalité des fonctionnalités de MyBatis est accessible avec le DSL basé sur XML qui a fait le succès de MyBatis au travers des années. Si vous avez utilisé MyBatis par le passé, vous serez familier du concept, mais le mapping XML a connu de nombreuses améliorations qui seront mises en lumières. Voici un exemple de mapping XML compatible avec les exemples précédents d'utilisation de SqlSession.

xml
<?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>

Cela peut paraître beaucoup d'overhead pour un exemple aussi simple, mais c'est en fait très léger. On peut définir autant de requêtes dans un même mapper XML, si bien que les entêtes XML paraissent insignifiants. Le reste du fichier parle de lui-même. On définit un nom pour la requête “selectBlog”, dans le namespace “org.mybatis.example.BlogMapper”, ce qui nous permettrait de l'exécuter par son nom canonique (fully qualified name) “org.mybatis.example.BlogMapper.selectBlog”, comme nous l'avions fait plus haut :

java
Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

C'est très similaire à un appel de méthode Java à partir du nom canonique (sans import) de la classe, et il y a une raison pour ça. Ce nom peut directement être associé à une classe “Mapper” du même nom que le namespace, avec un nom de méthode qui correspond à celui de la requête, le paramètre, et le type de retour correspondant. Cela permet d'appeler facilement les méthodes de l'interface BlogMapper vue précédemment, mais on peut aussi l'utiliser comme dans l'exemple suivant:

java
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

La seconde approche a de nombreux avantages. Premièrement, on ne dépend pas d'une chaîne de caractère mais d'un type, c'est plus sûr. Deuxièmement, dans un IDE supportant l'auto-completion, cela facilite la recherche des requêtes à exécuter.


NOTE A propos des namespaces.

Les Namespaces étaient optionnels dans les précédentes versions de MyBatis, ce qui n'aidait pas en plus d'être déroutant. Ils sont maintenant obligatoires et ont un but au-delà de la simple isolation des requêtes (noms canoniques).

On constate que les namespaces permettent de faire le lien avec les interfaces, et même si vous ne pensez pas les utiliser aujourd'hui, il est vivement recommandé de suivre ces pratiques. En utilisant déclarant un namespace, et en utilisant l'interface Java correspondante, à long terme MyBatis devient plus simple à utiliser et cela rend votre code plus fiable.

Résolution des noms: Afin de réduire la quantité de code à produire, MyBatis utilise les règles suivantes de résolution des éléments de configuration nommés, dont les requêtes, result maps, caches etc.

  • Les noms canoniques (e.g. “com.mypackage.MyMapper.selectAllThings”) sont recherchés tels quels et utilisés lorsqu'ils existent.
  • Les noms courts (e.g. “selectAllThings”) peuvent être utilisés pour référencer les symboles en l'absence d'ambiguïté. Cependant, s'il en existe au moins deux (e.g. “com.foo.selectAllThings et com.bar.selectAllThings”), une exception sera levée expliquant que le nom est ambigu et doit donc être canonique, complet.

Une dernière possibilité s'offre aux mappers comme BlogMapper. Leurs requêtes n'ont pas nécessairement besoin d'un mapping XML. Il est possible de passer par des annotations Java. On peut par exemple remplacer le mapping XML par:

java
package org.mybatis.example;
public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

Les annotations génère moins d'overhead pour les requêtes simples, elles sont cependant limitées et plus fouillies pour des requêtes plus complexes. De fait, pour tout ce qui est compliqué, XML est à privilégier.

Il sera de votre ressort de déterminer quelle approche vous convient le mieux, et quelle importance vous accordez à la cohérence dans la définition de vos requêtes SQL. Ceci-dit, MyBatis ne vous enferme pas dans une approche unique. Il est facile de migrer des annotations vers l'XML et vice versa.

Périmètre et Cycle de vie

Il est très important de comprendre la portée des différentes classes qui ont été abordées jusqu'ici. Utilisées incorrectement, elles peuvent poser de sévères problèmes de concurrence.


NOTE Cycle de vie au sein d'un framework d'injection de dépendance

Les frameworks d'injection de dépendance (ou IoC, inversion de contrôle) peuvent créer des SqlSession et des mappers transactionnels, thread safe, et les injecter directement dans vos beans si bien que vous n'avez plus à vous soucier de leur cycle de vie. Vous serez probablement intéressé par les sous-projets MyBatis-Spring et MyBatis-Guice pour apprendre plus sur l'injection de dépendances avec MyBatis.


SqlSessionFactoryBuilder

Cette classe peut être instanciée, utilisée puis jetée aux oubliettes. Il n'y a aucun intérêt à la conserver une fois qu'on a créé la SqlSessionFactory. De fait, le mieux est de limiter la portée des SqlSessionFactoryBuilder à une méthode (i.e. variable locale). On peut réutiliser une instance pour construire plusieurs SqlSessionFactory, mais il est préférable de ne pas garder de SqlSessionFactoryBuilder pour permettre la libération des ressources qui ont servi à parser les fichiers XML.

SqlSessionFactory

Une fois créée, la SqlSessionFactory devrait exister pendant toute l'exécution de l'application. Il n'y a pas ou peu de raisons de disposer d'une instance ou de la recréer. La bonne pratique est de ne pas créer de multiples instances pendant l'exécution d'une application. Par conséquent, la portée la plus adaptée est celle de l'application. Cela peut être fait de différentes façons. La plus simple est d'utiliser le pattern du Singleton ou du Static Singleton.

SqlSession

Chaque thread devrait avoir sa propre instance de SqlSession. Les instances de SqlSession n'ont pas été pensées pour être partagée et ne sont pas thread safe. De fait, les portées les plus adaptées sont méthode (scope method) ou requête (scope request). Ne conservez jamais de SqlSession dans un champ statique ni même dans un champ d'une classe (NDT sauf si la classe se trouve dans un scope de type request). Ne gardez pas non plus de référence de SqlSession dans un framework gérant ses propres cycles de vie (i.e. managed scope), comme par exemple HttpSession de l'API Servlet. Si vous utilisez un framework WEB, SqlSession devait avoir la même portée qu'une requête HTTP (NDT une requête est généralement ratachée à un unique thread). En d'autres termes, lorsque vous recevez une requête HTTP, vous pouvez ouvrir une SqlSession, et lors de la réponse HTTP, la refermer. Fermer la session est très important. Vous devez vous assurer de toujours fermer la session dans un bloc finally. Le pattern classique à suivre pour assurer la fermeture de la connexion :

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  // do work
}

En utilisant ce pattern de façon systématique, votre code assurera la fermeture de toutes les ressources de base de données.

Mappers

Les mappers sont des interfaces qui sont liées aux requêtes déclarées. Les instances de mappers sont obtenues à partir d'une SqlSession. Donc, techniquement, leur portée ne peut pas excéder celle de la SqlSession dont ils sont issus. Cependant, il convient de limiter la portée d'un mapper à une méthode. C'est à dire, un mapper devrait être créé, utilisé et libéré au sein d'une même méthode. Ils n'ont pas explicitement besoin d'être fermés. Même s'il n'est pas gênant de les conserver pendant toute une requête (e.g. HTTP), à l'image de leur SqlSession, vous prenez le risque d'être débordé face à la consommation de ressources induite. Le plus simple, c'est de limiter la portée d'un mapper à une méthode. Voici un exemple de mise en pratique (la portée du mapper est le bloc try):

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // do work
}

File: src/site/ja/markdown/getting-started.md

title: MyBatis 3 | スタートガイド
author: Clinton Begin, Iwao AVE!

スタートガイド

Installation

MyBatis を使うためには、ダウンロードした mybatis-x.x.x.jar をクラスパスに追加する必要があります。

Maven を利用している場合は pom.xml に下記の依存性を追加するだけで OK です。

xml
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

XML 形式の設定ファイルを使って SqlSessionFactory を生成する

MyBatis アプリケーションは、SqlSessionFactory のインスタンスを中心に構成されています。
SqlSessionFactory のインスタンスは、SqlSessionFactoryBuilder を使って取得することができます。 SqlSessionFactoryBuilder が SqlSessionFactory を生成する際の設定は、XML 形式の設定ファイルを読み込むか、独自に用意した Configuration クラスのインスタンスを渡すことで行います。

XML 形式の設定ファイルを使って SqlSessionFactory を生成するのはとても簡単です。
この設定ファイルはクラスパスに置くことが推奨されますが、ファイルパスや file:// 形式の URL 文字列から生成した InputStream を使ってクラスパス以外の場所に配置されたファイルを読み込むこともできます。 MyBatis 付属の Resources というユーティリティクラスには、クラスパスや、それ以外の場所からリソースを読み込むためのメソッドが多数用意されています。

java
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

XML 形式の設定ファイルには、MyBatis システムの基本設定が含まれます。 例えば、データベースから Connection のインスタンスを取得する DataSource や、トランザクションの制御方法を決定する TransactionManager などです。
XML 形式の設定ファイルの詳細については後ほど改めて説明しますが、ここでは簡単なサンプルを挙げておきます。

xml
<?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>

XML 形式の設定ファイルについて説明すべきことは他にもたくさんありますが、最も重要な設定は上記のサンプルに含まれています。
まず、XML ドキュメントのバリデーションを行うために必要となる XML ヘッダがあります。
environment 要素には、トランザクション管理やコネクションプーリングといった環境依存の設定が含まれています。
mappers 要素には Mapper のリストが含まれています。Mapper とは、SQL 文とマッピングの定義を含む XML ファイルです。

XML を使わずに SqlSessionFactory を生成する

XML を使わず Java のコードで設定を行いたい方のために、Configuration クラスが提供されています。
XML 形式の設定ファイルで指定できるオプションは、このクラスでも全て設定可能です。

java
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);

今回は、Configuration のインスタンスに Mapper クラスを追加していることに気づいたでしょうか。
Mapper クラスには、XML を使わずに SQL マッピングを定義するためのアノテーションが含まれています。 ただし、Java アノテーションの制約と MyBatis のマッピング機能の複雑さのため、高度なマッピングを定義する際に XML 形式の Mapper が必要となる場合があります(ネストされた結合クエリの結果をマッピングする場合など)。
このため、MyBatis は対になる XML ファイルを探して自動的に読み込むようになっています(この例では、BlogMapper.class のクラスパスと名前を元に BlogMapper.xml という XML ファイルが読み込まれます)。 この動作については、後ほど詳しく説明します。

SqlSessionFactory から SqlSession を取得する

名前を見れば分かると思いますが、生成した SqlSessionFactory から SqlSession のインスタンスを取得することができます。
SqlSession には、一連の SQL コマンドを実行するためのメソッドが全て用意されています。 SqlSession のインスタンスに対して、マップされた SQL 文を直接指定して実行することができます。
例えば下記のようなコードになります。

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}

この方法は期待通りに動作しますし、旧バージョンの MyBatis に慣れている方には分かりやすいと思いますが、 現在のバージョンではもっと美しい方法があります。 実行する SQL 文にマッチするように引数と戻り値がきちんと定義されたインターフェイスを使えば、ミスしがちな文字列やキャストなしで、より美しく、型に安全なコードを使って SQL を実行することができます。

例:

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}

では、実際にどのような処理が行われているのかを見て行きましょう。

Mapped SQL Statements について

SqlSession や Mapper クラスによって何が行われているのか、そろそろ気になり出した頃ではないでしょうか。 Mapped SQL Statements は非常に大きな話題で、このドキュメントの大部分を占めることになると思いますが、 ここではいくつか例を挙げて、実際にどのような処理が行われているのかを簡単に説明してみたいと思います。

上で挙げた2つのサンプルに対応する SQL Statement は、XML、アノテーションのどちらを使っても定義することができます。
はじめに XML について見て行きましょう。 何年にも渡って MyBatis の人気を築いてきた XML ベースのマッピング言語を使えば、MyBatis の全機能を把握することができます。 このドキュメントの後の方で出てきますが、旧バージョンの MyBatis を使ったことがある方なら、コンセプトは同じですが、多くの改良が加えられていることが分かると思います。 以下は、先ほどの例で SqlSession のメソッドを呼び出したときに実行されることを想定した XML ベースの Mapped Statement です。

xml
<?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>

例が簡単なので無駄に記述量が多いように見えるかも知れませんが、本質的にはそれほどではありません。
ひとつの Mapper XML ファイルに複数の Mapped Statement を定義することができるので、XML ヘッダや doctype 宣言は一度書くだけです。
残りの部分はほぼ自明ですが、「org.mybatis.example.BlogMapper」というネームスペースの中に「selectBlog」という名前の Mapped Statement を定義しています。
上のサンプルと同じですが、この Mapped Statement を実行するには、以下のように完全修飾名「org.mybatis.example.BlogMapper.selectBlog」を指定して SqlSession のメソッドを呼び出します。

java
Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

この例が、完全修飾名で指定した Java クラスのメソッドを呼び出すのと似ていることに気づいたでしょうか。 これには理由があります。 この完全修飾名は、Mapped Statement の名前、引数、戻り値にマッチするメソッドを持った、同名の Mapper クラスにマッピングすることができます。 上で見たような Mapper インターフェイスへの簡単な呼び出しができるのはこの仕組みのおかげです。 再掲しておきます。

java
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

2番目の方法には多くの利点があります。
まず、文字列リテラルに頼らずに済むので、ずっと安全です。また、Mapped SQL Statement を選ぶときに IDE のコード補完機能を使うことができます。


NOTE ネームスペースについて

ネームスペース: 過去のバージョンの MyBatis では、ネームスペースはオプションでしたが、これは混乱や誤用の原因となっていました。
現在のバージョンではネームスペースは必須であり、単に長い完全修飾名を使って Statement を整理する以上に重要な役割を果たしています。

上で見たように、ネームスペースはインターフェイスバインディングを行うために使われています。 たとえ今は必要ないと思っても、将来気が変わったときのために推奨される手順通りに設定しておくべきです。 長い目で見れば、ネームスペースを指定して正しい Java パッケージに配置しておくことでクリーンなコードを書くことができ、MyBatis の使い勝手も向上するはずです。

名前解決: タイピング量を減らすため、MyBatis は次のようなルールに則って設定要素(Statement, Result Map, Cache など)の名前解決を行います。

  • 完全修飾名(例えば「com.mypackage.MyMapper.selectAllThings」)で指定した場合は、指定された完全修飾名で要素を探し、もし見つかればその要素を参照します。
  • 短縮名(例えば「selectAllThings」)で指定した場合は、短縮名部分が一致する要素を参照します。 ただし、同じ短縮名を持った要素が2つ以上存在する場合(例えば「com.foo.selectAllThings」と「com.bar.selectAllThings」)は、 指定した短縮名が曖昧なので完全修飾名で指定する必要がある、というエラーが発生します。

BloggerMapper のような Mapper クラスなら、実は XML を使う必要はありません。 代わりに、Java アノテーションを使って下記のように Mapped Statement を記述することができます。

java
package org.mybatis.example;
public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

アノテーションを使うと単純な SQL 文をスッキリ書くことができますが、複雑な SQL 文をアノテーションを使って書こうとすると可読性が落ちますし、アノテーション固有の制限もありますので、複雑な Mapped Statement を扱わなくてはならない場合には XML を使うことをお勧めします。

XML とアノテーションのどちらを使うべきか、そしてどのように一貫性を持たせるかは、あなたとあなたのプロジェクトチーム次第です。 ただ、常にどちらか一方を使わなくてはならない、という訳ではありません。 アノテーションベースの Mapped Statement を XML に書き換えることは簡単なことですし、逆もまたしかりです。

スコープとライフサイクル

これまでに説明した様々なクラスに適したスコープや、そのライフサイクルについて理解しておくことは大変重要です。 誤って使用すると、深刻な整合性の問題の原因となります。


NOTE オブジェクトのライフサイクルと依存性注入(Dependency Injection)フレームワーク

Dependency Injection フレームワークを使うと、スレッドセーフでトランザクション管理された SqlSession や Mapper のインスタンスを作成し、あなたの Bean にインジェクトすることもできます。 こうすることで、SqlSession や Mapper のライフサイクルについてアプリケーションロジックと分離することができます。 MyBatis と DI フレームワークの組み合わせについては、サブプロジェクトである MyBatis-Spring または MyBatis-Guice を参照してください。


SqlSessionFactoryBuilder

このクラスは、インスタンス化し、使用し、破棄することができます。 一旦 SqlSessionFactory を生成してしまえば、このクラスを残しておく理由はありません。 したがって、このクラスのスコープとして最適なのはメソッドスコープ(つまり、ローカルメソッド変数)です。 SqlSessionFactoryBuilder を再利用して複数の SqlSessionFactory を生成することも可能ですが、XML をパースするためのリソースが他の重要なものを圧迫しないように、このクラスを保持して使いまわさない方が得策です。

SqlSessionFactory

生成した SqlSessionFactory は、あなたのアプリケーション実行中はそのまま残しておくべきです。 生成した SqlSessionFactory を破棄したり、再度生成する理由はないはずです。 SqlSessionFactory を再生成しない、というのは所謂ベストプラクティスで、これを行なっていたら「何かおかしいぞ」と考えるべきです。 したがって、SqlSessionFactory に最適なのはアプリケーションスコープ、ということになります。 これを実現する方法はいくつもあります。 最も簡単なのはシングルトンパターンまたはスタティックシングルトンパターンを使う方法です。

SqlSession

各スレッドは、独立した SqlSession のインスタンスを使うべきです。 SqlSession のインスタンスは共有されることを前提としていないため、スレッドセーフではありません。 当然、最適なスコープはメソッドスコープになります。 SqlSession のインスタンスへの参照を static なフィールドや、インスタンスフィールドにも格納してはいけません。 Servlet フレームワークの HttpSession のようなマネージドスコープに SqlSession への参照を保持するのもダメです。 もし何らかの Web フレームワークを使っているのであれば、SqlSession のスコープが HTTP リクエストと同調するようにしておくべきです。 つまり、HTTP リクエストを受け取ったら SqlSession をオープンし、レスポンスを返すときにクローズすれば良いのです。 セッションをクローズすることはとても重要です。 間違いがないよう、常に finally ブロックの中でセッションをクローズするようにした方が良いでしょう。 SqlSession を確実にクローズするための一般的なパターンは下記のようなものです。

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  // do work
}

常にこのパターンに従っておけば、すべてのデータベースリソースを確実にクローズすることができます。

Mapper インスタンス

Mapper は、Mapped Statement をバインドするためのインターフェイスです。 Mapper インターフェイスのインスタンスは SqlSession から取得されます。 したがって、Mapper インスタンスの理論上の最長のスコープは、インスタンス取得元の SqlSession と同じということになります。 ですが、Mapper インスタンスに最適なスコープはメソッドスコープです。 つまり、Mapper インスタンスを利用するメソッドの中で取得・破棄するということです。 Mapper インスタンスを明示的にクローズする必要はありません。 リクエスト処理が完了するまで残しておいても問題ありませんが、SqlSession 同様、このレベルで余分なリソースを大量に扱うと、すぐに手に負えない状況になってしまうでしょう。 単純化のため、Mapper はメソッドスコープの中で使うようにしてください。 このプラクティスを実践したのが次のサンプルになります。

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // do work
}

File: src/site/ja/markdown/index.md

title: MyBatis 3 | イントロダクション
author: Clinton Begin, Iwao AVE!

Avoid blank site

イントロダクション

MyBatis とは?

MyBatis はカスタム SQL、ストアドプロシージャ、高度なマッピング処理に対応した優れた永続化フレームワークです。 MyBatisを使うことで、直接 JDBC を扱うコードを書いたり、クエリ引数やクエリ結果を手動で設定する必要がほとんどなくなります。 MyBatis の設定やデータベースレコードと Java オブジェクトの関連付けは、XML またはアノテーションを使って行うことができます。

このドキュメントの改善にご協力ください...

このドキュメントの中で誤りや特定の機能に関する記述が抜けていることに気づいたら、詳しく調べてドキュメントを更新して頂けると助かります。

このマニュアルのソースは Markdown 形式で、プロジェクトの GitHub リポジトリで配布されています。
リポジトリをフォークして内容を編集したらプルリクエストを送信してください。

このドキュメントを読む必要のある人、つまりあなたこそが最高の著者なのです!


File: src/site/ja/markdown/logging.md

[Binary file]


File: src/site/ko/markdown/getting-started.md

title: 마이바티스 3 | 시작하기
author: Clinton Begin, 이동국(한국어 번역)

시작하기

설치

마이바티스를 사용하기 위해 mybatis-x.x.x.jar 파일을 클래스패스에 두어야 한다.

메이븐을 사용한다면 pom.xml에 다음의 설정을 추가하자.

xml
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

XML에서 SqlSessionFactory 빌드하기

모든 마이바티스 애플리케이션은 SqlSessionFactory 인스턴스를 사용한다. SqlSessionFactory인스턴스는 SqlSessionFactoryBuilder를 사용하여 만들 수 있다. SqlSessionFactoryBuilder는 XML설정파일에서 SqlSessionFactory인스턴스를 빌드할 수 있다.

XML파일에서 SqlSessionFactory인스턴스를 빌드하는 것은 매우 간단하다. 설정을 위해 클래스패스 자원을 사용하는 것을 추천하나 파일 경로나 file:// URL로부터 만들어진 InputStream인스턴스를 사용할 수도 있다. 마이바티스는 클래스패스와 다른 위치에서 자원을 로드하는 것으로 좀더 쉽게 해주는 Resources 라는 유틸성 클래스를 가지고 있다.

java
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

XML설정파일에서 지정하는 마이바티스의 핵심이 되는 설정은 트랜잭션을 제어하기 위한 TransactionManager와 함께 데이터베이스 Connection인스턴스를 가져오기 위한 DataSource 를 포함한다. 세부적인 설정은 조금 뒤에 보고 간단한 예제를 먼저보자.

xml
<?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>

좀 더 많은 XML 설정방법이 있지만 위 예제에서는 가장 핵심적인 부분만을 보여주고 있다. XML 가장 위부분에서는 XML 문서의 유효성체크를 위해 필요하다. environment엘리먼트는 트랜잭션 관리와 커넥션 풀링을 위한 환경적인 설정을 나타낸다. mappers엘리먼트는 SQL 코드와 매핑 정의를 가지는 XML 파일인 mapper 의 목록을 지정한다.

XML 을 사용하지 않고 SqlSessionFactory 빌드하기

XML 보다 자바를 사용해서 직접 설정하길 원한다면 XML 파일과 같은 모든 설정을 제공하는 Configuration 클래스를 사용하면 된다.

java
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);

이 설정에서 추가로 해야 할 일은 Mapper클래스를 추가하는 것이다. Mapper클래스는 SQL 매핑 애노테이션을 가진 자바 클래스이다. 어쨌든 자바 애노테이션의 몇가지 제약과 몇가지 설정방법의 복잡함에도 불구하고 XML 매핑은 세부적인 매핑을 위해 언제든 필요하다. 좀더 세부적인 내용은 조금 뒤 다시 보도록 하자.

SqlSessionFactory 에서 SqlSession 만들기

SqlSessionFactory 이름에서 보듯이 SqlSession 인스턴스를 만들 수 있다. SqlSession 은 데이터베이스에 대해 SQL명령어를 실행하기 위해 필요한 모든 메소드를 가지고 있다. 그래서 SqlSession 인스턴스를 통해 직접 SQL 구문을 실행할 수 있다. 예를 들면 :

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}

이 방법이 마이바티스의 이전버전을 사용한 사람이라면 굉장히 친숙할 것이다. 하지만 좀더 좋은 방법이 생겼다. 주어진 SQL 구문의 파라미터와 리턴값을 설명하는 인터페이스(예를 들면, BlogMapper.class )를 사용하여 문자열 처리 오류나 타입 캐스팅 오류 없이 좀더 타입에 안전하고 깔끔하게 실행할 수 있다.

예를들면:

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}

그럼 자세히 살펴보자.

매핑된 SQL 구문 살펴보기

이 시점에 SqlSession이나 Mapper클래스가 정확히 어떻게 실행되는지 궁금할 것이다. 매핑된 SQL 구문에 대한 내용이 가장 중요하다. 그래서 이 문서 전반에서 가장 자주 다루어진다. 하지만 다음의 두가지 예제를 통해 정확히 어떻게 작동하는지에 대해서는 충분히 이해하게 될 것이다.

위 예제처럼 구문은 XML이나 애노테이션을 사용해서 정의할 수 있다. 그럼 먼저 XML 을 보자. 마이바티스가 제공하는 대부분의 기능은 XML을 통해 매핑 기법을 사용한다. 이전에 마이바티스를 사용했었다면 쉽게 이해되겠지만 XML 매핑 문서에 이전보다 많은 기능이 추가되었다. SqlSession을 호출하는 XML 기반의 매핑 구문이다.

xml
<?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>

간단한 예제치고는 조금 복잡해 보이지만 실제로는 굉장히 간단하다. 한 개의 매퍼 XML 파일에는 많은 수의 매핑 구문을 정의할 수 있다. XML 도입부의 헤더와 doctype 을 제외하면 나머지는 쉽게 이해되는 구문의 형태이다. 여기선 org.mybatis.example.BlogMapper 네임스페이스에서 selectBlog라는 매핑 구문을 정의했고 이는 결과적으로 org.mybatis.example.BlogMapper.selectBlog형태로 실제 명시되게 된다. 그래서 다음처럼 사용하게 되는 셈이다.

java
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

이건 마치 패키지를 포함한 전체 경로의 클래스내 메소드를 호출하는 것과 비슷한 형태이다. 이 이름은 매핑된 select구문의 이름과 파라미터 그리고 리턴타입을 가진 네임스페이스와 같은 이름의 Mapper 클래스와 직접 매핑될 수 있다. 이건 위에서 본것과 같은 Mapper 인터페이스의 메소드를 간단히 호출하도록 허용한다. 위 예제에 대응되는 형태는 아래와 같다.

java
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

두번째 방법은 많은 장점을 가진다. 먼저 문자열에 의존하지 않는다는 것이다. 이는 애플리케이션을 좀더 안전하게 만든다. 두번째는 개발자가 IDE 를 사용할 때 매핑된 SQL 구문을 사용할때의 수고를 덜어준다. 세번째는 리턴타입에 대해 타입 캐스팅을 하지 않아도 된다. 그래서 BlogMapper 인터페이스는 깔끔하고 리턴 타입에 대해 타입에 안전하며 이는 파라미터에도 그대로 적용된다.


참고 네임스페이스(Namespaces)에 대한 설명

네임스페이스(Namespaces) 가 이전버전에서는 사실 선택사항이었다. 하지만 이제는 패키지경로를 포함한 전체 이름을 가진 구문을 구분하기 위해 필수로 사용해야 한다.

네임스페이스는 인터페이스 바인딩을 가능하게 한다. 네임스페이스를 사용하고 자바 패키지의 네임스페이스를 두면 코드가 깔끔해지고 마이바티스의 사용성이 크게 향상될 것이다.

이름 분석(Name Resolution): 타이핑을 줄이기 위해 마이바티스는 구문과 결과매핑, 캐시등의 모든 설정엘리먼트를 위한 이름 분석 규칙을 사용한다.

  • “com.mypackage.MyMapper.selectAllThings”과 같은 패키지를 포함한 전체 경로명(Fully qualified names)은 같은 형태의 경로가 있다면 그 경로내에서 직접 찾는다.
  • “selectAllThings”과 같은 짧은 형태의 이름은 모호하지 않은 엔트리를 참고하기 위해 사용될 수 있다. 그래서 짧은 이름은 모호해서 에러를 자주 보게 되니 되도록이면 전체 경로를 사용해야 할 것이다.

BlogMapper와 같은 Mapper클래스에는 몇가지 트릭이 있다. 매핑된 구문은 XML 에 전혀 매핑될 필요가 없다. 대신 자바 애노테이션을 사용할 수 있다. 예를들어 위 XML 예제는 다음과 같은 형태로 대체될 수 있다.

java
package org.mybatis.example;
public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

간단한 구문에서는 애노테이션이 좀더 깔끔하다. 어쨌든 자바 애노테이션은 좀더 복잡한 구문에서는 제한적이고 코드를 엉망으로 만들 수 있다. 그러므로 복잡하게 사용하고자 한다면 XML 매핑 구문을 사용하는 것이 좀더 나을 것이다.

여기서 당신과 당신의 프로젝트 팀은 어떤 방법이 좋은지 결정해야 할 것이다. 매핑된 구문을 일관된 방식으로 정의하는 것이 중요하다. 앞서 언급했지만 반드시 한가지만 고집해야 하는 건 아니다. 애노테이션은 XML로 쉽게 변환할 수 있고 그 반대의 형태 역시 쉽게 처리할 수 있다.

스코프(Scope) 와 생명주기(Lifecycle)

이제부터 다룰 스코프와 생명주기에 대해서 이해하는 것은 매우 중요하다. 스코프와 생명주기를 잘못 사용하는 것은 다양한 동시성 문제를 야기할 수 있다.


참고 객체 생명주기와 의존성 삽입 프레임워크

의존성 삽입 프레임워크는 쓰레드에 안전하도록 해준다. 트랜잭션 성질을 가지는 SqlSessions과 매퍼들 그리고 그것들을 직접 빈에 삽입하면 생명주기에 대해 기억하지 않아도 되게 해준다. DI프레임워크와 마이바티스를 사용하기 위해 좀더 많은 정보를 보기 위해서는 MyBatis-Spring이나 MyBatis-Guice 하위 프로젝트를 찾아보면 된다.


SqlSessionFactoryBuilder

이 클래스는 인스턴스화해서 사용한 뒤 버리면 된다. SqlSessionFactory 를 생성한 후 유지할 필요는 없다. 그러므로 SqlSessionFactoryBuilder 인스턴스의 가장 좋은 스코프는 메소드 스코프(예를들면 메소드 지역변수)이다. 여러개의 SqlSessionFactory 인스턴스를 빌드하기 위해 SqlSessionFactoryBuilder를 재사용할 수도 있지만 유지하지 않는 것이 가장 좋다.

SqlSessionFactory

한번 만든뒤 SqlSessionFactory는 애플리케이션을 실행하는 동안 존재해야만 한다. 그래서 삭제하거나 재생성할 필요가 없다. 애플리케이션이 실행되는 동안 여러 차례 SqlSessionFactory 를 다시 빌드하지 않는 것이 가장 좋은 형태이다. 재빌드하는 형태는 결과적으로 “나쁜냄새” 가 나도록 한다. 그러므로 SqlSessionFactory 의 가장 좋은 스코프는 애플리케이션 스코프이다. 애플리케이션 스코프로 유지하기 위한 다양한 방법이 존재한다. 가장 간단한 방법은 싱글턴 패턴이나 static 싱글턴 패턴을 사용하는 것이다. 또는 구글 쥬스나 스프링과 같은 의존성 삽입 컨테이너를 선호할 수도 있다. 이러한 프레임워크는 SqlSessionFactory의 생명주기를 싱글턴으로 관리할 것이다.

SqlSession

각각의 쓰레드는 자체적으로 SqlSession인스턴스를 가져야 한다. SqlSession인스턴스는 공유되지 않고 쓰레드에 안전하지도 않다. 그러므로 가장 좋은 스코프는 요청 또는 메소드 스코프이다. SqlSession 을 static 필드나 클래스의 인스턴스 필드로 지정해서는 안된다. 그리고 서블릿 프레임워크의 HttpSession 과 같은 관리 스코프에 둬서도 안된다. 어떠한 종류의 웹 프레임워크를 사용한다면 HTTP 요청과 유사한 스코프에 두는 것으로 고려해야 한다. 달리 말해서 HTTP 요청을 받을때마다 만들고 응답을 리턴할때마다 SqlSession 을 닫을 수 있다. SqlSession 을 닫는 것은 중요하다. 언제나 finally 블록에서 닫아야만 한다. 다음은 SqlSession을 닫는 것을 확인하는 표준적인 형태다.

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  // do work
}

코드전반에 이런 형태를 사용하는 것은 모든 데이터베이스 자원을 잘 닫는 것으로 보장하게 할 것이다.

Mapper 인스턴스

Mapper는 매핑된 구문을 바인딩 하기 위해 만들어야 할 인터페이스이다. mapper 인터페이스의 인스턴스는 SqlSession 에서 생성한다. 그래서 엄밀히 말하면 mapper 인스턴스의 가장 넓은 스코프는 그것을 얻어온 SqlSession 과 동일하다. 어쨌든 mapper 인스턴스의 가장 좋은 스코프는 메소드 스코프이다. 사용할 메소드가 호출되면 생성되고 끝난다. 명시적으로 닫을 필요는 없다.

java
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // do work
}

File: src/site/ko/markdown/index.md

title: 마이바티스 3 | 소개
author: Clinton Begin, 이동국(한국어 번역)

Avoid blank site

소개

마이바티스는 무엇인가?

마이바티스는 개발자가 지정한 SQL, 저장프로시저 그리고 몇가지 고급 매핑을 지원하는 퍼시스턴스 프레임워크이다. 마이바티스는 JDBC로 처리하는 상당부분의 코드와 파라미터 설정및 결과 매핑을 대신해준다. 마이바티스는 데이터베이스 레코드에 원시타입과 Map 인터페이스 그리고 자바 POJO 를 설정해서 매핑하기 위해 XML과 애노테이션을 사용할 수 있다.

이 문서가 더 좋아지도록 도와주세요…

이 문서가 일부 내용을 자세히 다루지 않거나 특정 기능에 대해 설명하지 않을 경우 마이바티스를 공부하는 가장 좋은 방법은 사용자 스스로 그 부분에 대한 문서를 작성하는 것이다!

이 문서는 Markdown포맷으로 되어 있으며 프로젝트 깃허브에서 볼수 있다. 저장소를 포크하고 수정한 뒤 Pull request요청을 보내면 됩니다.

그러면 이 문서의 가장 멋진 저자가 되고 다른 많은 사람들이 이 문서를 읽을 것이다!

번역에 대하여

번역자 : 이동국([email protected], https://ldg.pe.kr, https://www.facebook.com/dongguk.lee.3)

다음의 몇가지 언어로 번역된 마이바티스 문서를 읽을수 있다.

text
<li class="en">[English](../getting-started.html)</li>
      <li class="es">[Español](../es/index.html)</li>

      <li class="ja">[日本語](../ja/index.html)</li>
      <li class="ko">[한국어](../ko/index.html)</li>
      <li class="zh">[简体中文](../zh_CN/index.html)</li>

당신의 모국어로 작성된 마이바티스 문서를 읽고 싶은가? 그렇다면 모국어로 작성된 문서를 패치로 첨부한 이슈를 등록해달라!


File: src/site/ko/markdown/logging.md

[Binary file]


File: src/site/markdown/configuration.md

title: MyBatis 3 | Configuration
author: Clinton Begin

Avoid blank site

Configuration

The MyBatis configuration contains settings and properties that have a dramatic effect on how MyBatis behaves. The high level structure of the document is as follows:

properties

These are externalizable, substitutable properties that can be configured in a typical Java Properties file instance, or passed in through sub-elements of the properties element. For example:

xml
<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

The properties can then be used throughout the configuration files to substitute values that need to be dynamically configured. For example:

xml
<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

The username and password in this example will be replaced by the values set in the properties elements. The driver and url properties would be replaced with values contained from the config.properties file. This provides a lot of options for configuration.

Properties can also be passed into the SqlSessionFactoryBuilder.build() methods. For example:

java
SqlSessionFactory factory =
  new SqlSessionFactoryBuilder().build(reader, props);

// ... or ...

SqlSessionFactory factory =
  new SqlSessionFactoryBuilder().build(reader, environment, props);

If a property exists in more than one of these places, MyBatis loads them in the following order:

  • Properties specified in the body of the properties element are read first,
  • Properties loaded from the classpath resource or url attributes of the properties element are read second, and override any duplicate properties already specified,
  • Properties passed as a method parameter are read last, and override any duplicate properties that may have been loaded from the properties body and the resource/url attributes.

Thus, the highest priority properties are those passed in as a method parameter, followed by resource/url attributes and finally the properties specified in the body of the properties element.

Since the MyBatis 3.4.2, your can specify a default value into placeholder as follow:

xml
<dataSource type="POOLED">
  <!-- ... -->
  <property name="username" value="${username:ut_user}"/> <!-- If 'username' property not present, username become 'ut_user' -->
</dataSource>

This feature is disabled by default. If you specify a default value into placeholder, you should enable this feature by adding a special property as follow:

xml
<properties resource="org/mybatis/example/config.properties">
  <!-- ... -->
  <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- Enable this feature -->
</properties>

NOTE This will conflict with the ":" character in property keys (e.g. db:username) or the ternary operator of OGNL expressions (e.g. ${tableName != null ? tableName : 'global_constants'}) on a SQL definition. If you use either and want default property values, you must change the default value separator by adding this special property:

xml
<properties resource="org/mybatis/example/config.properties">
  <!-- ... -->
  <property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- Change default value of separator -->
</properties>
xml
<dataSource type="POOLED">
  <!-- ... -->
  <property name="username" value="${db:username?:ut_user}"/>
</dataSource>

settings

These are extremely important tweaks that modify the way that MyBatis behaves at runtime. The following table describes the settings, their meanings and their default values.

Setting Description Valid Values Default
cacheEnabled Globally enables or disables any caches configured in any mapper under this configuration. true | false true
lazyLoadingEnabled Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. This value can be superseded for a specific relation by using the fetchType attribute on it. true | false false
aggressiveLazyLoading When enabled, any method call will load all the lazy properties of the object. Otherwise, each property is loaded on demand (see also lazyLoadTriggerMethods). true | false false (true in ≤3.4.1)
multipleResultSetsEnabled Deprecated. This option has no effect. true | false true
useColumnLabel Uses the column label instead of the column name. Different drivers behave differently in this respect. Refer to the driver documentation, or test out both modes to determine how your driver behaves. true | false true
useGeneratedKeys Allows JDBC support for generated keys. A compatible driver is required. This setting forces generated keys to be used if set to true, as some drivers deny compatibility but still work (e.g. Derby). true | false false
autoMappingBehavior Specifies if and how MyBatis should automatically map columns to fields/properties. NONE disables auto-mapping. PARTIAL will only auto-map results with no nested result mappings defined inside. FULL will auto-map result mappings of any complexity (containing nested or otherwise). NONE, PARTIAL, FULL PARTIAL
autoMappingUnknownColumnBehavior Specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target.
  • NONE: Do nothing
  • WARNING: Output warning log (The log level of 'org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' must be set to WARN)
  • FAILING: Fail mapping (Throw SqlSessionException)
  • Note that there could be false-positives when autoMappingBehavior is set to FULL.
    NONE, WARNING, FAILING NONE
    defaultExecutorType Configures the default executor. SIMPLE executor does nothing special. REUSE executor reuses prepared statements. BATCH executor reuses statements and batches updates. SIMPLE | REUSE | BATCH SIMPLE
    defaultStatementTimeout Sets the number of seconds the driver will wait for a response from the database. Any positive integer Not Set (null)
    defaultFetchSize Sets the driver a hint as to control fetching size for return results. This parameter value can be override by a query setting. Any positive integer Not Set (null)
    defaultResultSetType Specifies a scroll strategy when omit it per statement settings. (Since: 3.5.2) FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(same behavior with 'Not Set') Not Set (null)
    safeRowBoundsEnabled Allows using RowBounds on nested statements. If allow, set the false. true | false false
    safeResultHandlerEnabled Allows using ResultHandler on nested statements. If allow, set the false. true | false true
    mapUnderscoreToCamelCase Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names aColumn. true | false false
    localCacheScope MyBatis uses local cache to prevent circular references and speed up repeated nested queries. By default (SESSION) all queries executed during a session are cached. If localCacheScope=STATEMENT local session will be used just for statement execution, no data will be shared between two different calls to the same SqlSession. SESSION | STATEMENT SESSION
    jdbcTypeForNull Specifies the JDBC type for null values when no specific JDBC type was provided for the parameter. Some drivers require specifying the column JDBC type but others work with generic values like NULL, VARCHAR or OTHER. JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER OTHER
    lazyLoadTriggerMethods Specifies which Object's methods trigger a lazy load A method name list separated by commas equals,clone,hashCode,toString
    defaultScriptingLanguage Specifies the language used by default for dynamic SQL generation. A type alias or fully qualified class name. org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
    defaultEnumTypeHandler Specifies the TypeHandler used by default for Enum. (Since: 3.4.5) A type alias or fully qualified class name. org.apache.ibatis.type.EnumTypeHandler
    callSettersOnNulls Specifies if setters or map's put method will be called when a retrieved value is null. It is useful when you rely on Map.keySet() or null value initialization. Note primitives such as (int,boolean,etc.) will not be set to null. true | false false
    returnInstanceForEmptyRow MyBatis, by default, returns null when all the columns of a returned row are NULL. When this setting is enabled, MyBatis returns an empty instance instead. Note that it is also applied to nested results (i.e. collection and association). Since: 3.4.2 true | false false
    logPrefix Specifies the prefix string that MyBatis will add to the logger names. Any String Not set
    logImpl Specifies which logging implementation MyBatis should use. If this setting is not present logging implementation will be autodiscovered. SLF4J | LOG4J(deprecated since 3.5.9) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING Not set
    proxyFactory Specifies the proxy tool that MyBatis will use for creating lazy loading capable objects. CGLIB (deprecated since 3.5.10) | JAVASSIST JAVASSIST (MyBatis 3.3 or above)
    vfsImpl Specifies VFS implementations Fully qualified class names of custom VFS implementation separated by commas. Not set
    useActualParamName Allow referencing statement parameters by their actual names declared in the method signature. To use this feature, your project must be compiled in Java 8 with -parameters option. (Since: 3.4.1) true | false true
    configurationFactory Specifies the class that provides an instance of Configuration. The returned Configuration instance is used to load lazy properties of deserialized objects. This class must have a method with a signature static Configuration getConfiguration(). (Since: 3.2.3) A type alias or fully qualified class name. Not set
    shrinkWhitespacesInSql Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. (Since 3.5.5) true | false false
    defaultSqlProviderType Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted. A type alias or fully qualified class name Not set
    nullableOnForEach Specifies the default value of 'nullable' attribute on 'foreach' tag. (Since 3.5.9) true | false false
    argNameBasedConstructorAutoMapping When applying constructor auto-mapping, argument name is used to search the column to map instead of relying on the column order. (Since 3.5.10) true | false false

    An example of the settings element fully configured is as follows:

    xml
    <settings>
      <setting name="cacheEnabled" value="true"/>
      <setting name="lazyLoadingEnabled" value="true"/>
      <setting name="aggressiveLazyLoading" value="true"/>
      <setting name="useColumnLabel" value="true"/>
      <setting name="useGeneratedKeys" value="false"/>
      <setting name="autoMappingBehavior" value="PARTIAL"/>
      <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
      <setting name="defaultExecutorType" value="SIMPLE"/>
      <setting name="defaultStatementTimeout" value="25"/>
      <setting name="defaultFetchSize" value="100"/>
      <setting name="safeRowBoundsEnabled" value="false"/>
      <setting name="safeResultHandlerEnabled" value="true"/>
      <setting name="mapUnderscoreToCamelCase" value="false"/>
      <setting name="localCacheScope" value="SESSION"/>
      <setting name="jdbcTypeForNull" value="OTHER"/>
      <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
      <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
      <setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
      <setting name="callSettersOnNulls" value="false"/>
      <setting name="returnInstanceForEmptyRow" value="false"/>
      <setting name="logPrefix" value="exampleLogPreFix_"/>
      <setting name="logImpl" value="SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING"/>
      <setting name="proxyFactory" value="CGLIB | JAVASSIST"/>
      <setting name="vfsImpl" value="org.mybatis.example.YourselfVfsImpl"/>
      <setting name="useActualParamName" value="true"/>
      <setting name="configurationFactory" value="org.mybatis.example.ConfigurationFactory"/>
    </settings>

    typeAliases

    A type alias is simply a shorter name for a Java type. It's only relevant to the XML configuration and simply exists to reduce redundant typing of fully qualified classnames. For example:

    xml
    <typeAliases>
      <typeAlias alias="Author" type="domain.blog.Author"/>
      <typeAlias alias="Blog" type="domain.blog.Blog"/>
      <typeAlias alias="Comment" type="domain.blog.Comment"/>
      <typeAlias alias="Post" type="domain.blog.Post"/>
      <typeAlias alias="Section" type="domain.blog.Section"/>
      <typeAlias alias="Tag" type="domain.blog.Tag"/>
    </typeAliases>

    With this configuration, Blog can now be used anywhere that domain.blog.Blog could be.

    You can also specify a package where MyBatis will search for beans. For example:

    xml
    <typeAliases>
      <package name="domain.blog"/>
    </typeAliases>

    Each bean found in domain.blog , if no annotation is found, will be registered as an alias using uncapitalized non-qualified class name of the bean. That is domain.blog.Author will be registered as author. If the @Alias annotation is found its value will be used as an alias. See the example below:

    java
    @Alias("author")
    public class Author {
        ...
    }

    There are many built-in type aliases for common Java types. They are all case insensitive, note the special handling of primitives due to the overloaded names.

    Alias Mapped Type
    _byte byte
    _char (since 3.5.10) char
    _character (since 3.5.10) char
    _long long
    _short short
    _int int
    _integer int
    _double double
    _float float
    _boolean boolean
    string String
    byte Byte
    char (since 3.5.10) Character
    character (since 3.5.10) Character
    long Long
    short Short
    int Integer
    integer Integer
    double Double
    float Float
    boolean Boolean
    date Date
    decimal BigDecimal
    bigdecimal BigDecimal
    biginteger BigInteger
    object Object
    date[] Date[]
    decimal[] BigDecimal[]
    bigdecimal[] BigDecimal[]
    biginteger[] BigInteger[]
    object[] Object[]
    map Map
    hashmap HashMap
    list List
    arraylist ArrayList
    collection Collection
    iterator Iterator

    typeHandlers

    Whenever MyBatis sets a parameter on a PreparedStatement or retrieves a value from a ResultSet, a TypeHandler is used to retrieve the value in a means appropriate to the Java type. The following table describes the default TypeHandlers.

    NOTE Since version 3.4.5, MyBatis supports JSR-310 (Date and Time API) by default.

    Type Handler Java Types JDBC Types
    BooleanTypeHandler java.lang.Boolean, boolean Any compatible BOOLEAN
    ByteTypeHandler java.lang.Byte, byte Any compatible NUMERIC or BYTE
    ShortTypeHandler java.lang.Short, short Any compatible NUMERIC or SMALLINT
    IntegerTypeHandler java.lang.Integer, int Any compatible NUMERIC or INTEGER
    LongTypeHandler java.lang.Long, long Any compatible NUMERIC or BIGINT
    FloatTypeHandler java.lang.Float, float Any compatible NUMERIC or FLOAT
    DoubleTypeHandler java.lang.Double, double Any compatible NUMERIC or DOUBLE
    BigDecimalTypeHandler java.math.BigDecimal Any compatible NUMERIC or DECIMAL
    StringTypeHandler java.lang.String CHAR, VARCHAR
    ClobReaderTypeHandler java.io.Reader -
    ClobTypeHandler java.lang.String CLOB, LONGVARCHAR
    NStringTypeHandler java.lang.String NVARCHAR, NCHAR
    NClobTypeHandler java.lang.String NCLOB
    BlobInputStreamTypeHandler java.io.InputStream -
    ByteArrayTypeHandler byte[] Any compatible byte stream type
    BlobTypeHandler byte[] BLOB, LONGVARBINARY
    DateTypeHandler java.util.Date TIMESTAMP
    DateOnlyTypeHandler java.util.Date DATE
    TimeOnlyTypeHandler java.util.Date TIME
    SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
    SqlDateTypeHandler java.sql.Date DATE
    SqlTimeTypeHandler java.sql.Time TIME
    ObjectTypeHandler Any OTHER, or unspecified
    EnumTypeHandler Enumeration Type VARCHAR any string compatible type, as the code is stored (not index).
    EnumOrdinalTypeHandler Enumeration Type Any compatible NUMERIC or DOUBLE, as the position is stored (not the code itself).
    SqlxmlTypeHandler java.lang.String SQLXML
    InstantTypeHandler java.time.Instant TIMESTAMP
    LocalDateTimeTypeHandler java.time.LocalDateTime TIMESTAMP
    LocalDateTypeHandler java.time.LocalDate DATE
    LocalTimeTypeHandler java.time.LocalTime TIME
    OffsetDateTimeTypeHandler java.time.OffsetDateTime TIMESTAMP
    OffsetTimeTypeHandler java.time.OffsetTime TIME
    ZonedDateTimeTypeHandler java.time.ZonedDateTime TIMESTAMP
    YearTypeHandler java.time.Year INTEGER
    MonthTypeHandler java.time.Month INTEGER
    YearMonthTypeHandler java.time.YearMonth VARCHAR or LONGVARCHAR
    JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE

    You can override the type handlers or create your own to deal with unsupported or non-standard types. To do so, implement the interface org.apache.ibatis.type.TypeHandler or extend the convenience class org.apache.ibatis.type.BaseTypeHandler and optionally map it to a JDBC type. For example:

    java
    // ExampleTypeHandler.java
    @MappedJdbcTypes(JdbcType.VARCHAR)
    public class ExampleTypeHandler extends BaseTypeHandler<String> {
    
      @Override
      public void setNonNullParameter(PreparedStatement ps, int i,
        String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
      }
    
      @Override
      public String getNullableResult(ResultSet rs, String columnName)
        throws SQLException {
        return rs.getString(columnName);
      }
    
      @Override
      public String getNullableResult(ResultSet rs, int columnIndex)
        throws SQLException {
        return rs.getString(columnIndex);
      }
    
      @Override
      public String getNullableResult(CallableStatement cs, int columnIndex)
        throws SQLException {
        return cs.getString(columnIndex);
      }
    }
    xml
    <!-- mybatis-config.xml -->
    <typeHandlers>
      <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
    </typeHandlers>

    Using such a TypeHandler would override the existing type handler for Java String properties and VARCHAR parameters and results. Note that MyBatis does not introspect upon the database metadata to determine the type, so you must specify that it’s a VARCHAR field in the parameter and result mappings to hook in the correct type handler. This is due to the fact that MyBatis is unaware of the data type until the statement is executed.

    MyBatis will know the Java type that you want to handle with this TypeHandler by introspecting its generic type, but you can override this behavior by two means:

    • Adding a javaType attribute to the typeHandler element (for example: javaType="String")
    • Adding a @MappedTypes annotation to your TypeHandler class specifying the list of java types to associate it with. This annotation will be ignored if the javaType attribute as also been specified.

    The associated JDBC type can be specified by two means:

    • Adding a jdbcType attribute to the typeHandler element (for example: jdbcType="VARCHAR").
    • Adding a @MappedJdbcTypes annotation to your TypeHandler class specifying the list of JDBC types to associate it with. This annotation will be ignored if the jdbcType attribute as also been specified.

    When deciding which TypeHandler to use in a ResultMap, the Java type is known (from the result type), but the JDBC type is unknown. MyBatis therefore uses the combination javaType=[TheJavaType], jdbcType=null to choose a TypeHandler. This means that using a @MappedJdbcTypes annotation restricts the scope of a TypeHandler and makes it unavailable for use in ResultMaps unless explicitly set. To make a TypeHandler available for use in a ResultMap, set includeNullJdbcType=true on the @MappedJdbcTypes annotation. Since Mybatis 3.4.0 however, if a single TypeHandler is registered to handle a Java type, it will be used by default in ResultMaps using this Java type (i.e. even without includeNullJdbcType=true).

    And finally you can let MyBatis search for your TypeHandlers:

    xml
    <!-- mybatis-config.xml -->
    <typeHandlers>
      <package name="org.mybatis.example"/>
    </typeHandlers>

    Note that when using the autodiscovery feature JDBC types can only be specified with annotations.

    You can create a generic TypeHandler that is able to handle more than one class. For that purpose add a constructor that receives the class as a parameter and MyBatis will pass the actual class when constructing the TypeHandler.

    java
    //GenericTypeHandler.java
    public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {
    
      private Class<E> type;
    
      public GenericTypeHandler(Class<E> type) {
        if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
        this.type = type;
      }
      ...

    EnumTypeHandler and EnumOrdinalTypeHandler are generic TypeHandlers. We will learn about them in the following section.

    Handling Enums

    If you want to map an Enum, you'll need to use either EnumTypeHandler or EnumOrdinalTypeHandler.

    For example, let's say that we need to store the rounding mode that should be used with some number if it needs to be rounded. By default, MyBatis uses EnumTypeHandler to convert the Enum values to their names.

    Note EnumTypeHandler is special in the sense that unlike other handlers, it does not handle just one specific class, but any class that extends Enum

    However, we may not want to store names. Our DBA may insist on an integer code instead. That's just as easy: add EnumOrdinalTypeHandler to the typeHandlers in your config file, and now each RoundingMode will be mapped to an integer using its ordinal value.

    xml
    <!-- mybatis-config.xml -->
    <typeHandlers>
      <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
        javaType="java.math.RoundingMode"/>
    </typeHandlers>

    But what if you want to map the same Enum to a string in one place and to integer in another?

    The auto-mapper will automatically use EnumOrdinalTypeHandler, so if we want to go back to using plain old ordinary EnumTypeHandler, we have to tell it, by explicitly setting the type handler to use for those SQL statements.

    (Mapper files aren't covered until the next section, so if this is your first time reading through the documentation, you may want to skip this for now and come back to it later.)

    xml
    <!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="org.apache.ibatis.submitted.rounding.Mapper">
        <resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <result column="funkyNumber" property="funkyNumber"/>
            <result column="roundingMode" property="roundingMode"/>
        </resultMap>
    
        <select id="getUser" resultMap="usermap">
            select * from users
        </select>
        <insert id="insert">
            insert into users (id, name, funkyNumber, roundingMode) values (
                #{id}, #{name}, #{funkyNumber}, #{roundingMode}
            )
        </insert>
    
        <resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap2">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <result column="funkyNumber" property="funkyNumber"/>
            <result column="roundingMode" property="roundingMode"
             typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>
        </resultMap>
        <select id="getUser2" resultMap="usermap2">
            select * from users2
        </select>
        <insert id="insert2">
            insert into users2 (id, name, funkyNumber, roundingMode) values (
                #{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler}
            )
        </insert>
    
    </mapper>

    Note that this forces us to use a resultMap instead of a resultType in our select statements.

    objectFactory

    Each time MyBatis creates a new instance of a result object, it uses an ObjectFactory instance to do so. The default ObjectFactory does little more than instantiate the target class with a default constructor, or a parameterized constructor if parameter mappings exist. If you want to override the default behaviour of the ObjectFactory, you can create your own. For example:

    java
    // ExampleObjectFactory.java
    public class ExampleObjectFactory extends DefaultObjectFactory {
      @Override
      public <T> T create(Class<T> type) {
        return super.create(type);
      }
    
      @Override
      public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return super.create(type, constructorArgTypes, constructorArgs);
      }
    
      @Override
      public void setProperties(Properties properties) {
        super.setProperties(properties);
      }
    
      @Override
      public <T> boolean isCollection(Class<T> type) {
        return Collection.class.isAssignableFrom(type);
      }
    }
    xml
    <!-- mybatis-config.xml -->
    <objectFactory type="org.mybatis.example.ExampleObjectFactory">
      <property name="someProperty" value="100"/>
    </objectFactory>

    The ObjectFactory interface is very simple. It contains two create methods, one to deal with the default constructor, and the other to deal with parameterized constructors. Finally, the setProperties method can be used to configure the ObjectFactory. Properties defined within the body of the objectFactory element will be passed to the setProperties method after initialization of your ObjectFactory instance.

    plugins

    MyBatis allows you to intercept calls to at certain points within the execution of a mapped statement. By default, MyBatis allows plug-ins to intercept method calls of:

    • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
    • ParameterHandler (getParameterObject, setParameters)
    • ResultSetHandler (handleResultSets, handleOutputParameters)
    • StatementHandler (prepare, parameterize, batch, update, query)

    The details of these classes methods can be discovered by looking at the full method signature of each, and the source code which is available with each MyBatis release. You should understand the behaviour of the method you’re overriding, assuming you’re doing something more than just monitoring calls. If you attempt to modify or override the behaviour of a given method, you’re likely to break the core of MyBatis. These are low level classes and methods, so use plug-ins with caution.

    Using plug-ins is pretty simple given the power they provide. Simply implement the Interceptor interface, being sure to specify the signatures you want to intercept.

    java
    // ExamplePlugin.java
    @Intercepts({@Signature(
      type= Executor.class,
      method = "update",
      args = {MappedStatement.class,Object.class})})
    public class ExamplePlugin implements Interceptor {
      private Properties properties = new Properties();
    
      @Override
      public Object intercept(Invocation invocation) throws Throwable {
        // implement pre-processing if needed
        Object returnObject = invocation.proceed();
        // implement post-processing if needed
        return returnObject;
      }
    
      @Override
      public void setProperties(Properties properties) {
        this.properties = properties;
      }
    }
    xml
    <!-- mybatis-config.xml -->
    <plugins>
      <plugin interceptor="org.mybatis.example.ExamplePlugin">
        <property name="someProperty" value="100"/>
      </plugin>
    </plugins>

    The plug-in above will intercept all calls to the "update" method on the Executor instance, which is an internal object responsible for the low-level execution of mapped statements.

    NOTE Overriding the Configuration Class

    In addition to modifying core MyBatis behaviour with plugins, you can also override the Configuration class entirely. Simply extend it and override any methods inside, and pass it into the call to the SqlSessionFactoryBuilder.build(myConfig) method. Again though, this could have a severe impact on the behaviour of MyBatis, so use caution.

    environments

    MyBatis can be configured with multiple environments. This helps you to apply your SQL Maps to multiple databases for any number of reasons. For example, you might have a different configuration for your Development, Test and Production environments. Or, you may have multiple production databases that share the same schema, and you’d like to use the same SQL maps for both. There are many use cases.

    One important thing to remember though: While you can configure multiple environments, you can only choose ONE per SqlSessionFactory instance.

    So if you want to connect to two databases, you need to create two instances of SqlSessionFactory, one for each. For three databases, you’d need three instances, and so on. It’s really easy to remember:

    • One SqlSessionFactory instance per database

    To specify which environment to build, you simply pass it to the SqlSessionFactoryBuilder as an optional parameter. The two signatures that accept the environment are:

    java
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

    If the environment is omitted, then the default environment is loaded, as follows:

    java
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);

    The environments element defines how the environment is configured.

    xml
    <environments default="development">
      <environment id="development">
        <transactionManager type="JDBC">
          <property name="..." value="..."/>
        </transactionManager>
        <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>

    Notice the key sections here:

    • The default Environment ID (e.g. default="development").
    • The Environment ID for each environment defined (e.g. id="development").
    • The TransactionManager configuration (e.g. type="JDBC")
    • The DataSource configuration (e.g. type="POOLED")

    The default environment and the environment IDs are self explanatory. Name them whatever you like, just make sure the default matches one of them.

    transactionManager

    There are two TransactionManager types (i.e. type="[JDBC|MANAGED]") that are included with MyBatis:

    • JDBC – This configuration simply makes use of the JDBC commit and rollback facilities directly. It relies on the connection retrieved from the dataSource to manage the scope of the transaction. By default, it enables auto-commit when closing the connection for compatibility with some drivers. However, for some drivers, enabling auto-commit is not only unnecessary, but also is an expensive operation. So, since version 3.5.10, you can skip this step by setting the "skipSetAutoCommitOnClose" property to true. For example:

      xml
      <transactionManager type="JDBC">
        <property name="skipSetAutoCommitOnClose" value="true"/>
      </transactionManager>
    • MANAGED – This configuration simply does almost nothing. It never commits, or rolls back a connection. Instead, it lets the container manage the full lifecycle of the transaction (e.g. a JEE Application Server context). By default it does close the connection. However, some containers don’t expect this, and thus if you need to stop it from closing the connection, set the "closeConnection" property to false. For example:

      xml
      <transactionManager type="MANAGED">
        <property name="closeConnection" value="false"/>
      </transactionManager>

    NOTE If you are planning to use MyBatis with Spring there is no need to configure any TransactionManager because the Spring module will set its own one overriding any previously set configuration.

    Neither of these TransactionManager types require any properties. However, they are both Type Aliases, so in other words, instead of using them, you could put your own fully qualified class name or Type Alias that refers to your own implementation of the TransactionFactory interface.

    java
    public interface TransactionFactory {
      default void setProperties(Properties props) { // Since 3.5.2, change to default method
        // NOP
      }
      Transaction newTransaction(Connection conn);
      Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
    }

    Any properties configured in the XML will be passed to the setProperties() method after instantiation. Your implementation would also need to create a Transaction implementation, which is also a very simple interface:

    java
    public interface Transaction {
      Connection getConnection() throws SQLException;
      void commit() throws SQLException;
      void rollback() throws SQLException;
      void close() throws SQLException;
      Integer getTimeout() throws SQLException;
    }

    Using these two interfaces, you can completely customize how MyBatis deals with Transactions.

    dataSource

    The dataSource element configures the source of JDBC Connection objects using the standard JDBC DataSource interface.

    Most MyBatis applications will configure a dataSource as in the example. However, it’s not required. Realize though, that to facilitate Lazy Loading, this dataSource is required.

    There are three built-in dataSource types (i.e. type="[UNPOOLED|POOLED|JNDI]"):

    UNPOOLED – This implementation of DataSource simply opens and closes a connection each time it is requested. While it’s a bit slower, this is a good choice for simple applications that do not require the performance of immediately available connections. Different databases are also different in this performance area, so for some it may be less important to pool and this configuration will be ideal. The UNPOOLED DataSource has the following properties to configure:

    • driver – This is the fully qualified Java class of the JDBC driver (NOT of the DataSource class if your driver includes one).
    • url – This is the JDBC URL for your database instance.
    • username – The database username to log in with.
    • password - The database password to log in with.
    • defaultTransactionIsolationLevel – The default transaction isolation level for connections.
    • defaultNetworkTimeout – The default network timeout value in milliseconds to wait for the database operation to complete. See the API documentation of java.sql.Connection#setNetworkTimeout() for details.

    Optionally, you can pass properties to the database driver as well. To do this, prefix the properties with driver., for example:

    • driver.encoding=UTF8

    This will pass the property encoding, with the value UTF8, to your database driver via the DriverManager.getConnection(url, driverProperties) method.

    POOLED – This implementation of DataSource pools JDBC Connection objects to avoid the initial connection and authentication time required to create a new Connection instance. This is a popular approach for concurrent web applications to achieve the fastest response.

    In addition to the (UNPOOLED) properties above, there are many more properties that can be used to configure the POOLED datasource:

    • poolMaximumActiveConnections – This is the number of active (i.e. in use) connections that can exist at any given time. Default: 10
    • poolMaximumIdleConnections – The number of idle connections that can exist at any given time.
    • poolMaximumCheckoutTime – This is the amount of time that a Connection can be "checked out" of the pool before it will be forcefully returned. Default: 20000ms (i.e. 20 seconds)
    • poolTimeToWait – This is a low level setting that gives the pool a chance to print a log status and re-attempt the acquisition of a connection in the case that it’s taking unusually long (to avoid failing silently forever if the pool is misconfigured). Default: 20000ms (i.e. 20 seconds)
    • poolMaximumLocalBadConnectionTolerance – This is a low level setting about tolerance of bad connections got for any thread. If a thread got a bad connection, it may still have another chance to re-attempt to get another connection which is valid. But the retrying times should not more than the sum of poolMaximumIdleConnections and poolMaximumLocalBadConnectionTolerance. Default: 3 (Since: 3.4.5)
    • poolPingQuery – The Ping Query is sent to the database to validate that a connection is in good working order and is ready to accept requests. The default is "NO PING QUERY SET", which will cause most database drivers to fail with a decent error message.
    • poolPingEnabled – This enables or disables the ping query. If enabled, you must also set the poolPingQuery property with a valid SQL statement (preferably a very fast one). Default: false.
    • poolPingConnectionsNotUsedFor – This configures how often the poolPingQuery will be used. This can be set to match the typical timeout for a database connection, to avoid unnecessary pings. Default: 0 (i.e. all connections are pinged every time – but only if poolPingEnabled is true of course).

    JNDI – This implementation of DataSource is intended for use with containers such as EJB or Application Servers that may configure the DataSource centrally or externally and place a reference to it in a JNDI context. This DataSource configuration only requires two properties:

    • initial_context – This property is used for the Context lookup from the InitialContext (i.e. initialContext.lookup(initial_context)). This property is optional, and if omitted, then the data_source property will be looked up against the InitialContext directly.
    • data_source – This is the context path where the reference to the instance of the DataSource can be found. It will be looked up against the context returned by the initial_context lookup, or against the InitialContext directly if no initial_context is supplied.

    Similar to the other DataSource configurations, it’s possible to send properties directly to the InitialContext by prefixing those properties with env., for example:

    • env.encoding=UTF8

    This would send the property encoding with the value of UTF8 to the constructor of the InitialContext upon instantiation.

    You can plug any 3rd party DataSource by implementing the interface org.apache.ibatis.datasource.DataSourceFactory:

    java
    public interface DataSourceFactory {
      void setProperties(Properties props);
      DataSource getDataSource();
    }

    org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory can be used as super class to build new datasource adapters. For example this is the code needed to plug C3P0:

    java
    import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
    
      public C3P0DataSourceFactory() {
        this.dataSource = new ComboPooledDataSource();
      }
    }

    To set it up, add a property for each setter method you want MyBatis to call. Follows below a sample configuration which connects to a PostgreSQL database:

    xml
    <dataSource type="org.myproject.C3P0DataSourceFactory">
      <property name="driver" value="org.postgresql.Driver"/>
      <property name="url" value="jdbc:postgresql:mydb"/>
      <property name="username" value="postgres"/>
      <property name="password" value="root"/>
    </dataSource>

    databaseIdProvider

    MyBatis is able to execute different statements depending on your database vendor. The multi-db vendor support is based on the mapped statements databaseId attribute. MyBatis will load all statements with no databaseId attribute or with a databaseId that matches the current one. In case the same statement is found with and without the databaseId the latter will be discarded. To enable the multi vendor support add a databaseIdProvider to mybatis-config.xml file as follows:

    xml
    <databaseIdProvider type="DB_VENDOR" />

    The DB_VENDOR implementation databaseIdProvider sets as databaseId the String returned by DatabaseMetaData#getDatabaseProductName(). Given that usually that string is too long and that different versions of the same product may return different values, you may want to convert it to a shorter one by adding properties like follows:

    xml
    <databaseIdProvider type="DB_VENDOR">
      <property name="SQL Server" value="sqlserver"/>
      <property name="DB2" value="db2"/>
      <property name="Oracle" value="oracle" />
    </databaseIdProvider>

    When properties are provided, the DB_VENDOR databaseIdProvider will return the property value corresponding to the first key found in the returned database product name or "null" if there is not a matching property. In this case, if getDatabaseProductName() returns "Oracle (DataDirect)" the databaseId will be set to "oracle".

    You can build your own DatabaseIdProvider by implementing the interface org.apache.ibatis.mapping.DatabaseIdProvider and registering it in mybatis-config.xml:

    java
    public interface DatabaseIdProvider {
      default void setProperties(Properties p) { // Since 3.5.2, changed to default method
        // NOP
      }
      String getDatabaseId(DataSource dataSource) throws SQLException;
    }

    mappers

    Now that the behavior of MyBatis is configured with the above configuration elements, we’re ready to define our mapped SQL statements. But first, we need to tell MyBatis where to find them. Java doesn’t really provide any good means of auto-discovery in this regard, so the best way to do it is to simply tell MyBatis where to find the mapping files. You can use classpath relative resource references, fully qualified url references (including file:/// URLs), class names or package names. For example:

    xml
    <!-- Using classpath relative resources -->
    <mappers>
      <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
      <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
      <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    </mappers>
    xml
    <!-- Using url fully qualified paths -->
    <mappers>
      <mapper url="file:///var/mappers/AuthorMapper.xml"/>
      <mapper url="file:///var/mappers/BlogMapper.xml"/>
      <mapper url="file:///var/mappers/PostMapper.xml"/>
    </mappers>
    xml
    <!-- Using mapper interface classes -->
    <mappers>
      <mapper class="org.mybatis.builder.AuthorMapper"/>
      <mapper class="org.mybatis.builder.BlogMapper"/>
      <mapper class="org.mybatis.builder.PostMapper"/>
    </mappers>
    xml
    <!-- Register all interfaces in a package as mappers -->
    <mappers>
      <package name="org.mybatis.builder"/>
    </mappers>

    These statement simply tell MyBatis where to go from here. The rest of the details are in each of the SQL Mapping files, and that’s exactly what the next section will discuss.


    File: src/site/markdown/dynamic-sql.md

    title: MyBatis 3 | Dynamic SQL
    author: Clinton Begin

    Avoid blank site

    Dynamic SQL

    One of the most powerful features of MyBatis has always been its Dynamic SQL capabilities. If you have any experience with JDBC or any similar framework, you understand how painful it is to conditionally concatenate strings of SQL together, making sure not to forget spaces or to omit a comma at the end of a list of columns. Dynamic SQL can be downright painful to deal with.

    While working with Dynamic SQL will never be a party, MyBatis certainly improves the situation with a powerful Dynamic SQL language that can be used within any mapped SQL statement.

    The Dynamic SQL elements should be familiar to anyone who has used JSTL or any similar XML based text processors. In previous versions of MyBatis, there were a lot of elements to know and understand. MyBatis 3 greatly improves upon this, and now there are less than half of those elements to work with. MyBatis employs powerful OGNL based expressions to eliminate most of the other elements:

    • if
    • choose (when, otherwise)
    • trim (where, set)
    • foreach

    if

    The most common thing to do in dynamic SQL is conditionally include a part of a where clause. For example:

    xml
    <select id="findActiveBlogWithTitleLike"
         resultType="Blog">
      SELECT * FROM BLOG
      WHERE state = 'ACTIVE'
      <if test="title != null">
        AND title like #{title}
      </if>
    </select>

    This statement would provide an optional text search type of functionality. If you passed in no title, then all active Blogs would be returned. But if you do pass in a title, it will look for a title like that (for the keen eyed, yes in this case your parameter value would need to include any masking or wildcard characters).

    What if we wanted to optionally search by title and author? First, I’d change the name of the statement to make more sense. Then simply add another condition.

    xml
    <select id="findActiveBlogLike"
         resultType="Blog">
      SELECT * FROM BLOG WHERE state = 'ACTIVE'
      <if test="title != null">
        AND title like #{title}
      </if>
      <if test="author != null and author.name != null">
        AND author_name like #{author.name}
      </if>
    </select>

    choose, when, otherwise

    Sometimes we don’t want all of the conditionals to apply, instead we want to choose only one case among many options. Similar to a switch statement in Java, MyBatis offers a choose element.

    Let’s use the example above, but now let’s search only on title if one is provided, then only by author if one is provided. If neither is provided, let’s only return featured blogs (perhaps a strategically list selected by administrators, instead of returning a huge meaningless list of random blogs).

    xml
    <select id="findActiveBlogLike"
         resultType="Blog">
      SELECT * FROM BLOG WHERE state = 'ACTIVE'
      <choose>
        <when test="title != null">
          AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
          AND author_name like #{author.name}
        </when>
        <otherwise>
          AND featured = 1
        </otherwise>
      </choose>
    </select>

    trim, where, set

    The previous examples have been conveniently dancing around a notorious dynamic SQL challenge. Consider what would happen if we return to our "if" example, but this time we make the state condition dynamic as well.

    xml
    <select id="findActiveBlogLike"
         resultType="Blog">
      SELECT * FROM BLOG
      WHERE
      <if test="state != null">
        state = #{state}
      </if>
      <if test="title != null">
        AND title like #{title}
      </if>
      <if test="author != null and author.name != null">
        AND author_name like #{author.name}
      </if>
    </select>

    What happens if none of the conditions are met? You would end up with SQL that looked like this:

    sql
    SELECT * FROM BLOG
    WHERE

    This would fail. What if only the second condition was met? You would end up with SQL that looked like this:

    sql
    SELECT * FROM BLOG
    WHERE
    AND title like 'someTitle'

    This would also fail. This problem is not easily solved with conditionals, and if you’ve ever had to write it, then you likely never want to do so again.

    MyBatis has a simple answer that will likely work in 90% of the cases. And in cases where it doesn’t, you can customize it so that it does. With one simple change, everything works fine:

    xml
    <select id="findActiveBlogLike"
         resultType="Blog">
      SELECT * FROM BLOG
      <where>
        <if test="state != null">
             state = #{state}
        </if>
        <if test="title != null">
            AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
            AND author_name like #{author.name}
        </if>
      </where>
    </select>

    The where element knows to only insert "WHERE" if there is any content returned by the containing tags. Furthermore, if that content begins with "AND" or "OR", it knows to strip it off.

    If the where element does not behave exactly as you like, you can customize it by defining your own trim element. For example, the trim equivalent to the where element is:

    xml
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
      ...
    </trim>

    The prefixOverrides attribute takes a pipe delimited list of text to override, where whitespace is relevant. The result is the removal of anything specified in the prefixOverrides attribute, and the insertion of anything in the prefix attribute.

    There is a similar solution for dynamic update statements called set. The set element can be used to dynamically include columns to update, and leave out others. For example:

    xml
    <update id="updateAuthorIfNecessary">
      update Author
        <set>
          <if test="username != null">username=#{username},</if>
          <if test="password != null">password=#{password},</if>
          <if test="email != null">email=#{email},</if>
          <if test="bio != null">bio=#{bio}</if>
        </set>
      where id=#{id}
    </update>

    Here, the set element will dynamically prepend the SET keyword, and also eliminate any extraneous commas that might trail the value assignments after the conditions are applied.

    Alternatively, you can achieve the same effect by using trim element:

    xml
    <trim prefix="SET" suffixOverrides=",">
      ...
    </trim>

    Notice that in this case we’re overriding a suffix, while we’re still appending a prefix.

    foreach

    Another common necessity for dynamic SQL is the need to iterate over a collection, often to build an IN condition. For example:

    xml
    <select id="selectPostIn" resultType="domain.blog.Post">
      SELECT *
      FROM POST P
      <where>
        <foreach item="item" index="index" collection="list"
            open="ID in (" separator="," close=")" nullable="true">
              #{item}
        </foreach>
      </where>
    </select>

    The foreach element is very powerful, and allows you to specify a collection, declare item and index variables that can be used inside the body of the element. It also allows you to specify opening and closing strings, and add a separator to place in between iterations. The element is smart in that it won’t accidentally append extra separators.

    NOTE You can pass any Iterable object (for example List, Set, etc.), as well as any Map or Array object to foreach as collection parameter. When using an Iterable or Array, index will be the number of current iteration and value item will be the element retrieved in this iteration. When using a Map (or Collection of Map.Entry objects), index will be the key object and item will be the value object.

    This wraps up the discussion regarding the XML configuration file and XML mapping files. The next section will discuss the Java API in detail, so that you can get the most out of the mappings that you’ve created.

    script

    For using dynamic SQL in annotated mapper class, script element can be used. For example:

    java
    @Update({"<script>",
          "update Author",
          "  <set>",
          "    <if test='username != null'>username=#{username},</if>",
          "    <if test='password != null'>password=#{password},</if>",
          "    <if test='email != null'>email=#{email},</if>",
          "    <if test='bio != null'>bio=#{bio}</if>",
          "  </set>",
          "where id=#{id}",
          "</script>"})
        void updateAuthorValues(Author author);

    bind

    The bind element lets you create a variable out of an OGNL expression and bind it to the context. For example:

    xml
    <select id="selectBlogsLike" resultType="Blog">
      <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
      SELECT * FROM BLOG
      WHERE title LIKE #{pattern}
    </select>

    Multi-db vendor support

    If a databaseIdProvider was configured a "_databaseId" variable is available for dynamic code, so you can build different statements depending on database vendor. Have a look at the following example:

    xml
    <insert id="insert">
      <selectKey keyProperty="id" resultType="int" order="BEFORE">
        <if test="_databaseId == 'oracle'">
          select seq_users.nextval from dual
        </if>
        <if test="_databaseId == 'db2'">
          select nextval for seq_users from sysibm.sysdummy1
        </if>
      </selectKey>
      insert into users values (#{id}, #{name})
    </insert>

    Pluggable Scripting Languages For Dynamic SQL

    Starting from version 3.2 MyBatis supports pluggable scripting languages, so you can plug a language driver and use that language to write your dynamic SQL queries.

    You can plug a language by implementing the following interface:

    java
    public interface LanguageDriver {
      ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
      SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
      SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
    }

    Once you have your custom language driver you can set it to be the default by configuring it in the mybatis-config.xml file:

    xml
    <typeAliases>
      <typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
    </typeAliases>
    <settings>
      <setting name="defaultScriptingLanguage" value="myLanguage"/>
    </settings>

    Instead of changing the default, you can specify the language for an specific statement by adding the lang attribute as follows:

    xml
    <select id="selectBlog" lang="myLanguage">
      SELECT * FROM BLOG
    </select>

    Or, in the case you are using mappers, using the @Lang annotation:

    java
    public interface Mapper {
      @Lang(MyLanguageDriver.class)
      @Select("SELECT * FROM BLOG")
      List<Blog> selectBlog();
    }

    NOTE You can use Apache Velocity as your dynamic language. Have a look at the MyBatis-Velocity project for the details.

    All the xml tags you have seen in the previous sections are provided by the default MyBatis language that is provided by the driver org.apache.ibatis.scripting.xmltags.XMLLanguageDriver which is aliased as xml.


    File: src/site/markdown/getting-started.md

    title: MyBatis 3 | Getting started
    author: Clinton Begin

    Avoid blank site

    Getting started

    Installation

    To use MyBatis you just need to include the mybatis-x.x.x.jar file in the classpath.

    If you are using Maven just add the following dependency to your pom.xml:

    xml
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>x.x.x</version>
    </dependency>

    Building SqlSessionFactory from XML

    Every MyBatis application centers around an instance of SqlSessionFactory. A SqlSessionFactory instance can be acquired by using the SqlSessionFactoryBuilder. SqlSessionFactoryBuilder can build a SqlSessionFactory instance from an XML configuration file, or from a custom prepared instance of the Configuration class.

    Building a SqlSessionFactory instance from an XML file is very simple. It is recommended that you use a classpath resource for this configuration, but you could use any InputStream instance, including one created from a literal file path or a file:// URL. MyBatis includes a utility class, called Resources, that contains a number of methods that make it simpler to load resources from the classpath and other locations.

    java
    String resource = "org/mybatis/example/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory =
      new SqlSessionFactoryBuilder().build(inputStream);

    The configuration XML file contains settings for the core of the MyBatis system, including a DataSource for acquiring database Connection instances, as well as a TransactionManager for determining how transactions should be scoped and controlled. The full details of the XML configuration file can be found later in this document, but here is a simple example:

    xml
    <?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>

    While there is a lot more to the XML configuration file, the above example points out the most critical parts. Notice the XML header, required to validate the XML document. The body of the environment element contains the environment configuration for transaction management and connection pooling. The mappers element contains a list of mappers – the XML files and/or annotated Java interface classes that contain the SQL code and mapping definitions.

    Building SqlSessionFactory without XML

    If you prefer to directly build the configuration from Java, rather than XML, or create your own configuration builder, MyBatis provides a complete Configuration class that provides all of the same configuration options as the XML file.

    java
    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);

    Notice in this case the configuration is adding a mapper class. Mapper classes are Java classes that contain SQL Mapping Annotations that avoid the need for XML mapping. However, due to some limitations of Java Annotations and the complexity of some MyBatis mappings, XML mapping is still required for the most advanced mappings (e.g. Nested Join Mapping). For this reason, MyBatis will automatically look for and load a peer XML file if it exists (in this case, BlogMapper.xml would be loaded based on the classpath and name of BlogMapper.class). More on this later.

    Acquiring a SqlSession from SqlSessionFactory

    Now that you have a SqlSessionFactory, as the name suggests, you can acquire an instance of SqlSession. The SqlSession contains absolutely every method needed to execute SQL commands against the database. You can execute mapped SQL statements directly against the SqlSession instance. For example:

    java
    try (SqlSession session = sqlSessionFactory.openSession()) {
      Blog blog = session.selectOne(
        "org.mybatis.example.BlogMapper.selectBlog", 101);
    }

    While this approach works, and is familiar to users of previous versions of MyBatis, there is now a cleaner approach. Using an interface (e.g. BlogMapper.class) that properly describes the parameter and return value for a given statement, you can now execute cleaner and more type safe code, without error prone string literals and casting.

    For example:

    java
    try (SqlSession session = sqlSessionFactory.openSession()) {
      BlogMapper mapper = session.getMapper(BlogMapper.class);
      Blog blog = mapper.selectBlog(101);
    }

    Now let's explore what exactly is being executed here.

    Exploring Mapped SQL Statements

    At this point you may be wondering what exactly is being executed by the SqlSession or Mapper class. The topic of Mapped SQL Statements is a big one, and that topic will likely dominate the majority of this documentation. But to give you an idea of what exactly is being run, here are a couple of examples.

    In either of the examples above, the statements could have been defined by either XML or Annotations. Let's take a look at XML first. The full set of features provided by MyBatis can be realized by using the XML based mapping language that has made MyBatis popular over the years. If you've used MyBatis before, the concept will be familiar to you, but there have been numerous improvements to the XML mapping documents that will become clear later. Here is an example of an XML based mapped statement that would satisfy the above SqlSession calls.

    xml
    <?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>

    While this looks like a lot of overhead for this simple example, it is actually very light. You can define as many mapped statements in a single mapper XML file as you like, so you get a lot of mileage out of the XML header and doctype declaration. The rest of the file is pretty self explanatory. It defines a name for the mapped statement “selectBlog”, in the namespace “org.mybatis.example.BlogMapper”, which would allow you to call it by specifying the fully qualified name of “org.mybatis.example.BlogMapper.selectBlog”, as we did above in the following example:

    java
    Blog blog = session.selectOne(
      "org.mybatis.example.BlogMapper.selectBlog", 101);

    Notice how similar this is to calling a method on a fully qualified Java class, and there's a reason for that. This name can be directly mapped to a Mapper class of the same name as the namespace, with a method that matches the name, parameter, and return type as the mapped select statement. This allows you to very simply call the method against the Mapper interface as you saw above, but here it is again in the following example:

    java
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    Blog blog = mapper.selectBlog(101);

    The second approach has a lot of advantages. First, it doesn't depend on a string literal, so it's much safer. Second, if your IDE has code completion, you can leverage that when navigating your mapped SQL statements.


    NOTE A note about namespaces.

    Namespaces were optional in previous versions of MyBatis, which was confusing and unhelpful. Namespaces are now required and have a purpose beyond simply isolating statements with longer, fully-qualified names.

    Namespaces enable the interface bindings as you see here, and even if you don’t think you’ll use them today, you should follow these practices laid out here in case you change your mind. Using the namespace once, and putting it in a proper Java package namespace will clean up your code and improve the usability of MyBatis in the long term.

    Name Resolution: To reduce the amount of typing, MyBatis uses the following name resolution rules for all named configuration elements, including statements, result maps, caches, etc.

    • Fully qualified names (e.g. “com.mypackage.MyMapper.selectAllThings”) are looked up directly and used if found.
    • Short names (e.g. “selectAllThings”) can be used to reference any unambiguous entry. However if there are two or more (e.g. “com.foo.selectAllThings and com.bar.selectAllThings”), then you will receive an error reporting that the short name is ambiguous and therefore must be fully qualified.

    There's one more trick to Mapper classes like BlogMapper. Their mapped statements don't need to be mapped with XML at all. Instead they can use Java Annotations. For example, the XML above could be eliminated and replaced with:

    java
    package org.mybatis.example;
    public interface BlogMapper {
      @Select("SELECT * FROM blog WHERE id = #{id}")
      Blog selectBlog(int id);
    }

    The annotations are a lot cleaner for simple statements, however, Java Annotations are both limited and messier for more complicated statements. Therefore, if you have to do anything complicated, you're better off with XML mapped statements.

    It will be up to you and your project team to determine which is right for you, and how important it is to you that your mapped statements be defined in a consistent way. That said, you're never locked into a single approach. You can very easily migrate Annotation based Mapped Statements to XML and vice versa.

    Scope and Lifecycle

    It's very important to understand the various scopes and lifecycles classes we've discussed so far. Using them incorrectly can cause severe concurrency problems.


    NOTE Object lifecycle and Dependency Injection Frameworks

    Dependency Injection frameworks can create thread safe, transactional SqlSessions and mappers and inject them directly into your beans so you can just forget about their lifecycle. You may want to have a look at MyBatis-Spring or MyBatis-Guice sub-projects to know more about using MyBatis with DI frameworks.


    SqlSessionFactoryBuilder

    This class can be instantiated, used and thrown away. There is no need to keep it around once you've created your SqlSessionFactory. Therefore the best scope for instances of SqlSessionFactoryBuilder is method scope (i.e. a local method variable). You can reuse the SqlSessionFactoryBuilder to build multiple SqlSessionFactory instances, but it's still best not to keep it around to ensure that all of the XML parsing resources are freed up for more important things.

    SqlSessionFactory

    Once created, the SqlSessionFactory should exist for the duration of your application execution. There should be little or no reason to ever dispose of it or recreate it. It's a best practice to not rebuild the SqlSessionFactory multiple times in an application run. Doing so should be considered a “bad smell”. Therefore the best scope of SqlSessionFactory is application scope. This can be achieved a number of ways. The simplest is to use a Singleton pattern or Static Singleton pattern.

    SqlSession

    Each thread should have its own instance of SqlSession. Instances of SqlSession are not to be shared and are not thread safe. Therefore the best scope is request or method scope. Never keep references to a SqlSession instance in a static field or even an instance field of a class. Never keep references to a SqlSession in any sort of managed scope, such as HttpSession of the Servlet framework. If you're using a web framework of any sort, consider the SqlSession to follow a similar scope to that of an HTTP request. In other words, upon receiving an HTTP request, you can open a SqlSession, then upon returning the response, you can close it. Closing the session is very important. You should always ensure that it's closed within a finally block. The following is the standard pattern for ensuring that SqlSessions are closed:

    java
    try (SqlSession session = sqlSessionFactory.openSession()) {
      // do work
    }

    Using this pattern consistently throughout your code will ensure that all database resources are properly closed.

    Mapper Instances

    Mappers are interfaces that you create to bind to your mapped statements. Instances of the mapper interfaces are acquired from the SqlSession. As such, technically the broadest scope of any mapper instance is the same as the SqlSession from which they were requested. However, the best scope for mapper instances is method scope. That is, they should be requested within the method that they are used, and then be discarded. They do not need to be closed explicitly. While it's not a problem to keep them around throughout a request, similar to the SqlSession, you might find that managing too many resources at this level will quickly get out of hand. Keep it simple, keep Mappers in the method scope. The following example demonstrates this practice.

    java
    try (SqlSession session = sqlSessionFactory.openSession()) {
      BlogMapper mapper = session.getMapper(BlogMapper.class);
      // do work
    }

    File: src/site/markdown/index.md

    title: MyBatis 3 | Introduction
    author: Clinton Begin

    Avoid blank site

    Introduction

    What is MyBatis?

    MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results. MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records.

    If you are completely new to database access in Java, https://www.marcobehler.com/guides/a-guide-to-accessing-databases-in-java contains a good overview of the various parts, pieces and options and shows you where exactly MyBatis fits in.

    For tips, news, etc., follow these accounts 🦋Bluesky , X run by Alejandro Duarte.

    Help make this documentation better…

    If you find this documentation lacking in any way, or missing documentation for a feature, then the best thing to do is learn about it and then write the documentation yourself!

    Sources of this manual are available in Markdown format at project's Git. Fork the repository, update them and send a pull request.

    You’re the best author of this documentation, people like you have to read it!

    Translations

    Users can read about MyBatis in following translations:

    text
    <li class="en">[English](./getting-started.html)</li>
          <li class="es">[Español](./es/index.html)</li>
    
          <li class="ja">[日本語](./ja/index.html)</li>
          <li class="ko">[한국어](./ko/index.html)</li>
          <li class="zh">[简体中文](./zh_CN/index.html)</li>

    Do you want to read about MyBatis in your own native language? File an issue providing patches with your mother tongue documentation!


    File: src/site/markdown/java-api.md

    title: MyBatis 3 | Java API
    author: Clinton Begin

    Avoid blank site

    Java API

    Now that you know how to configure MyBatis and create mappings, you're ready for the good stuff. The MyBatis Java API is where you get to reap the rewards of your efforts. As you'll see, compared to JDBC, MyBatis greatly simplifies your code and keeps it clean, easy to understand and maintain. MyBatis 3 has introduced a number of significant improvements to make working with SQL Maps even better.

    Directory Structure

    Before we dive in to the Java API itself, it's important to understand the best practices surrounding directory structures. MyBatis is very flexible, and you can do almost anything with your files. But as with any framework, there's a preferred way.

    Let's look at a typical application directory structure:

    text
    /my_application
      /bin
      /devlib
      /lib                <-- MyBatis *.jar files go here.
      /src
        /org/myapp/
          /action
          /data           <-- MyBatis artifacts go here, including, Mapper Classes, XML Configuration, XML Mapping Files.
            /mybatis-config.xml
            /BlogMapper.java
            /BlogMapper.xml
          /model
          /service
          /view
        /properties       <-- Properties included in your XML Configuration go here.
      /test
        /org/myapp/
          /action
          /data
          /model
          /service
          /view
        /properties
      /web
        /WEB-INF
          /web.xml

    Remember, these are preferences, not requirements, but others will thank you for using a common directory structure.

    The rest of the examples in this section will assume you're following this directory structure.

    SqlSessions

    The primary Java interface for working with MyBatis is the SqlSession. Through this interface you can execute commands, get mappers and manage transactions. We'll talk more about SqlSession itself shortly, but first we have to learn how to acquire an instance of SqlSession. SqlSessions are created by a SqlSessionFactory instance. The SqlSessionFactory contains methods for creating instances of SqlSessions all different ways. The SqlSessionFactory itself is created by the SqlSessionFactoryBuilder that can create the SqlSessionFactory from XML, annotations or hand coded Java configuration.

    NOTE When using MyBatis with a dependency injection framework like Spring or Guice, SqlSessions are created and injected by the DI framework so you don't need to use the SqlSessionFactoryBuilder or SqlSessionFactory and can go directly to the SqlSession section. Please refer to the MyBatis-Spring or MyBatis-Guice manuals for further info.

    SqlSessionFactoryBuilder

    The SqlSessionFactoryBuilder has five build() methods, each which allows you to build a SqlSessionFactory from a different source.

    java
    SqlSessionFactory build(InputStream inputStream)
    SqlSessionFactory build(InputStream inputStream, String environment)
    SqlSessionFactory build(InputStream inputStream, Properties properties)
    SqlSessionFactory build(InputStream inputStream, String env, Properties props)
    SqlSessionFactory build(Configuration config)

    The first four methods are the most common, as they take an InputStream instance that refers to an XML document, or more specifically, the mybatis-config.xml file discussed above. The optional parameters are environment and properties. Environment determines which environment to load, including the datasource and transaction manager. For example:

    xml
    <environments default="development">
      <environment id="development">
        <transactionManager type="JDBC">
            ...
        <dataSource type="POOLED">
            ...
      </environment>
      <environment id="production">
        <transactionManager type="MANAGED">
            ...
        <dataSource type="JNDI">
            ...
      </environment>
    </environments>

    If you call a build method that takes the environment parameter, then MyBatis will use the configuration for that environment. Of course, if you specify an invalid environment, you will receive an error. If you call one of the build methods that does not take the environment parameter, then the default environment is used (which is specified as default="development" in the example above).

    If you call a method that takes a properties instance, then MyBatis will load those properties and make them available to your configuration. Those properties can be used in place of most values in the configuration using the syntax: ${propName}

    Recall that properties can also be referenced from the mybatis-config.xml file, or specified directly within it. Therefore it's important to understand the priority. We mentioned it earlier in this document, but here it is again for easy reference:


    If a property exists in more than one of these places, MyBatis loads them in the following order.

    • Properties specified in the body of the properties element are read first,
    • Properties loaded from the classpath resource or url attributes of the properties element are read second, and override any duplicate properties already specified,
    • Properties passed as a method parameter are read last, and override any duplicate properties that may have been loaded from the properties body and the resource/url attributes.

    Thus, the highest priority properties are those passed in as a method parameter, followed by resource/url attributes and finally the properties specified in the body of the properties element.


    So to summarize, the first four methods are largely the same, but with overrides to allow you to optionally specify the environment and/or properties. Here is an example of building a SqlSessionFactory from an mybatis-config.xml file.

    java
    String resource = "org/mybatis/builder/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(inputStream);

    Notice that we're making use of the Resources utility class, which lives in the org.apache.ibatis.io package. The Resources class, as its name implies, helps you load resources from the classpath, filesystem or even a web URL. A quick look at the class source code or inspection through your IDE will reveal its fairly obvious set of useful methods. Here's a quick list:

    java
    URL getResourceURL(String resource)
    URL getResourceURL(ClassLoader loader, String resource)
    InputStream getResourceAsStream(String resource)
    InputStream getResourceAsStream(ClassLoader loader, String resource)
    Properties getResourceAsProperties(String resource)
    Properties getResourceAsProperties(ClassLoader loader, String resource)
    Reader getResourceAsReader(String resource)
    Reader getResourceAsReader(ClassLoader loader, String resource)
    File getResourceAsFile(String resource)
    File getResourceAsFile(ClassLoader loader, String resource)
    InputStream getUrlAsStream(String urlString)
    Reader getUrlAsReader(String urlString)
    Properties getUrlAsProperties(String urlString)
    Class classForName(String className)

    The final build method takes an instance of Configuration. The Configuration class contains everything you could possibly need to know about a SqlSessionFactory instance. The Configuration class is useful for introspecting on the configuration, including finding and manipulating SQL maps (not recommended once the application is accepting requests). The configuration class has every configuration switch that you've learned about already, only exposed as a Java API. Here's a simple example of how to manually a Configuration instance and pass it to the build() method to create a SqlSessionFactory.

    java
    DataSource dataSource = BaseDataTest.createBlogDataSource();
    TransactionFactory transactionFactory = new JdbcTransactionFactory();
    
    Environment environment = new Environment("development", transactionFactory, dataSource);
    
    Configuration configuration = new Configuration(environment);
    configuration.setLazyLoadingEnabled(true);
    configuration.getTypeAliasRegistry().registerAlias(Blog.class);
    configuration.getTypeAliasRegistry().registerAlias(Post.class);
    configuration.getTypeAliasRegistry().registerAlias(Author.class);
    configuration.addMapper(BoundBlogMapper.class);
    configuration.addMapper(BoundAuthorMapper.class);
    
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(configuration);

    Now you have a SqlSessionFactory that can be used to create SqlSession instances.

    SqlSessionManager

    As of version 3.4.0+, MyBatis provides SqlSessionManager which implements both SqlSessionFactory and SqlSession interfaces. Unlike DefaultSqlSessionFactory which creates a new SqlSession for each call to openSession(), SqlSessionManager uses a ThreadLocal to manage sessions for each thread. This provides a convenient way to manage SqlSession lifecycle in multithreaded environments without requiring an external dependency injection framework.

    Creating a SqlSessionManager

    You can create a SqlSessionManager in two ways:

    1. Using one of the static newInstance() methods that accept a MyBatis XML configuration file:
    java
    SqlSessionManager manager = SqlSessionManager.newInstance(inputStream);

    Or with an environment or properties:

    java
    SqlSessionManager manager = SqlSessionManager.newInstance(inputStream, environment);
    
    // ... or ...
    
    SqlSessionManager manager = SqlSessionManager.newInstance(inputStream, properties);
    1. Wrapping an existing SqlSessionFactory instance:
    java
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSessionManager manager = SqlSessionManager.newInstance(factory);

    Managed Sessions

    The key feature of SqlSessionManager is its ability to manage sessions per thread. Once a managed session is started, all calls to SqlSession methods through the SqlSessionManager proxy will use the thread-local session.

    To start a managed session:

    java
    manager.startManagedSession();

    Or with specific options:

    java
    // With auto-commit
    manager.startManagedSession(true);
    
    // With a specific executor type
    manager.startManagedSession(ExecutorType.BATCH);
    
    // With a specific isolation level
    manager.startManagedSession(TransactionIsolationLevel.READ_COMMITTED);
    
    // With a specific connection
    manager.startManagedSession(connection);

    To check if a managed session has been started for the current thread:

    java
    boolean isStarted = manager.isManagedSessionStarted();

    Using the SqlSessionManager

    When a managed session is started, calling commit(), rollback(), close(), or any data access method (selectOne, selectList, insert, update, delete, etc.) will operate on the thread-local session. You must ensure to call close() when you are done with the session to clean up the thread-local storage, Because it uses a ThreadLocal, failing to call close() in a pooled web server environment (like Tomcat or Jetty) will leak the SQL session onto the thread, causing unpredictable transaction behavior or connection leaks when that thread is reused for a different HTTP request.:

    java
    try {
        manager.startManagedSession();
        // ... use the session ...
        manager.commit();
    } finally {
        manager.close();
    }

    If no managed session has been started, calling any SqlSession method on the SqlSessionManager will automatically open a short-lived session, execute that single method, and immediately close it. Note that it will not explicitly commit write operations, making this mode ideal primarily for single read queries.

    Difference from DefaultSqlSessionFactory

    Feature DefaultSqlSessionFactory SqlSessionManager
    Session creation Creates new session per openSession() call Can manage thread-local sessions via startManagedSession()
    Thread safety Requires external management Built-in thread-local session management
    Automatic session lifecycle No (Requires manual close) Yes, when no managed session is started
    DI framework integration Requires DI-specific integrations (e.g., SqlSessionTemplate in Spring) Can replace SqlSessionFactory and SqlSession in non-DI environments

    SqlSessionManager is particularly useful in environments where you don't use a dependency injection framework but still need proper session management per thread. If you are using Spring or Guice, you should continue to use MyBatis-Spring or MyBatis-Guice for session management.

    SqlSessionFactory

    SqlSessionFactory has eight methods that are used to create SqlSession instances. In general, the decisions you'll be making when selecting one of these methods are:

    • Transaction: Do you want to use a transaction scope for the session, or use auto-commit (usually means no transaction with most databases and/or JDBC drivers)?
    • Connection: Do you want MyBatis to acquire a Connection from the configured DataSource for you, or do you want to provide your own?
    • Execution: Do you want MyBatis to reuse PreparedStatements and/or batch updates (including inserts and deletes)?

    The set of overloaded openSession() method signatures allow you to choose any combination of these options that makes sense.

    java
    SqlSession openSession()
    SqlSession openSession(boolean autoCommit)
    SqlSession openSession(Connection connection)
    SqlSession openSession(TransactionIsolationLevel level)
    SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
    SqlSession openSession(ExecutorType execType)
    SqlSession openSession(ExecutorType execType, boolean autoCommit)
    SqlSession openSession(ExecutorType execType, Connection connection)
    Configuration getConfiguration();

    The default openSession() method that takes no parameters will create a SqlSession with the following characteristics:

    • A transaction scope will be started (i.e. NOT auto-commit).
    • A Connection object will be acquired from the DataSource instance configured by the active environment.
    • The transaction isolation level will be the default used by the driver or data source.
    • No PreparedStatements will be reused, and no updates will be batched.

    Most of the methods are pretty self explanatory. To enable auto-commit, pass a value of true to the optional autoCommit parameter. To provide your own connection, pass an instance of Connection to the connection parameter. Note that there's no override to set both the Connection and autoCommit, because MyBatis will use whatever setting the provided connection object is currently using. MyBatis uses a Java enumeration wrapper for transaction isolation levels, called TransactionIsolationLevel, but otherwise they work as expected and have the 5 levels supported by JDBC (NONE, READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE).

    The one parameter that might be new to you is ExecutorType. This enumeration defines 3 values:

    • ExecutorType.SIMPLE: This type of executor does nothing special. It creates a new PreparedStatement for each execution of a statement.
    • ExecutorType.REUSE: This type of executor will reuse PreparedStatements.
    • ExecutorType.BATCH: This executor will batch all update statements and demarcate them as necessary if SELECTs are executed between them, to ensure an easy-to-understand behavior.

    NOTE There's one more method on the SqlSessionFactory that we didn't mention, and that is getConfiguration(). This method will return an instance of Configuration that you can use to introspect upon the MyBatis configuration at runtime.

    NOTE If you've used a previous version of MyBatis, you'll recall that sessions, transactions and batches were all something separate. This is no longer the case. All three are neatly contained within the scope of a session. You need not deal with transactions or batches separately to get the full benefit of them.

    SqlSession

    As mentioned above, the SqlSession instance is the most powerful class in MyBatis. It is where you'll find all of the methods to execute statements, commit or rollback transactions and acquire mapper instances.

    There are over twenty methods on the SqlSession class, so let's break them up into more digestible groupings.

    Statement Execution Methods

    These methods are used to execute SELECT, INSERT, UPDATE and DELETE statements that are defined in your SQL Mapping XML files. They are pretty self explanatory, each takes the ID of the statement and the Parameter Object, which can be a primitive (auto-boxed or wrapper), a JavaBean, a POJO or a Map.

    java
    <T> T selectOne(String statement, Object parameter)
    <E> List<E> selectList(String statement, Object parameter)
    <T> Cursor<T> selectCursor(String statement, Object parameter)
    <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
    int insert(String statement, Object parameter)
    int update(String statement, Object parameter)
    int delete(String statement, Object parameter)

    The difference between selectOne and selectList is only in that selectOne must return exactly one object or null (none). If any more than one, an exception will be thrown. If you don't know how many objects are expected, use selectList. If you want to check for the existence of an object, you're better off returning a count (0 or 1). The selectMap is a special case in that it is designed to convert a list of results into a Map based on one of the properties in the resulting objects. Because not all statements require a parameter, these methods are overloaded with versions that do not require the parameter object.

    The value returned by the insert, update and delete methods indicate the number of rows affected by the statement.

    java
    <T> T selectOne(String statement)
    <E> List<E> selectList(String statement)
    <T> Cursor<T> selectCursor(String statement)
    <K,V> Map<K,V> selectMap(String statement, String mapKey)
    int insert(String statement)
    int update(String statement)
    int delete(String statement)

    A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.

    java
    try (Cursor<MyEntity> entities = session.selectCursor(statement, param)) {
       for (MyEntity entity : entities) {
          // process one entity
       }
    }

    Finally, there are five advanced versions of the select methods that allow you to restrict the range of rows to return, or provide custom result handling logic, usually for very large data sets.

    java
    <E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
    <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds)
    <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
    void select (String statement, Object parameter, ResultHandler<T> handler)
    void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)

    The RowBounds parameter causes MyBatis to skip the number of records specified, as well as limit the number of results returned to some number. The RowBounds class has a constructor to take both the offset and limit, and is otherwise immutable.

    java
    int offset = 100;
    int limit = 25;
    RowBounds rowBounds = new RowBounds(offset, limit);

    Different drivers are able to achieve different levels of efficiency in this regard. For the best performance, use result set types of SCROLL_SENSITIVE or SCROLL_INSENSITIVE (in other words: not FORWARD_ONLY).

    The ResultHandler parameter allows you to handle each row however you like. You can add it to a List, create a Map, Set, or throw each result away and instead keep only rolled up totals of calculations. You can do pretty much anything with the ResultHandler, and it's what MyBatis uses internally itself to build result set lists.

    Since 3.4.6, ResultHandler passed to a CALLABLE statement is used on every REFCURSOR output parameter of the stored procedure if there is any.

    The interface is very simple.

    java
    package org.apache.ibatis.session;
    public interface ResultHandler<T> {
      void handleResult(ResultContext<? extends T> context);
    }

    The ResultContext parameter gives you access to the result object itself, a count of the number of result objects created, and a stop() method that you can use to stop MyBatis from loading any more results.

    Using a ResultHandler has two limitations that you should be aware of:

    • Data gotten from a method called with a ResultHandler will not be cached.
    • When using advanced resultMaps MyBatis will probably require several rows to build an object. If a ResultHandler is used you may be given an object whose associations or collections are not yet filled.

    Batch update statement Flush Method

    There is method for flushing (executing) batch update statements that are stored in a JDBC driver class at any time. This method can be used when the ExecutorType is ExecutorType.BATCH.

    java
    List<BatchResult> flushStatements()

    Transaction Control Methods

    There are four methods for controlling the scope of a transaction. Of course, these have no effect if you've chosen to use auto-commit or if you're using an external transaction manager. However, if you're using the JDBC transaction manager, managed by the Connection instance, then the four methods that will come in handy are:

    java
    void commit()
    void commit(boolean force)
    void rollback()
    void rollback(boolean force)

    By default MyBatis does not actually commit unless it detects that the database has been changed by a call to insert, update, delete or select with affectData enabled. If you've somehow made changes without calling these methods, then you can pass true into the commit and rollback methods to guarantee that they will be committed (note, you still can't force a session in auto-commit mode, or one that is using an external transaction manager). Most of the time you won't have to call rollback(), as MyBatis will do that for you if you don't call commit. However, if you need more fine-grained control over a session where multiple commits and rollbacks are possible, you have the rollback option there to make that possible.

    NOTE MyBatis-Spring and MyBatis-Guice provide declarative transaction handling. So if you are using MyBatis with Spring or Guice please refer to their specific manuals.

    Local Cache

    MyBatis uses two caches: a local cache and a second level cache.

    Each time a new session is created MyBatis creates a local cache and attaches it to the session. Any query executed within the session will be stored in the local cache so further executions of the same query with the same input parameters will not hit the database. The local cache is cleared upon update, commit, rollback and close.

    By default local cache data is used for the whole session duration. This cache is needed to resolve circular references and to speed up repeated nested queries, so it can never be completely disabled but you can configure the local cache to be used just for the duration of a statement execution by setting localCacheScope=STATEMENT.

    Note that when the localCacheScope is set to SESSION, MyBatis returns references to the same objects which are stored in the local cache. Any modification of the returned objects (lists etc.) influences the local cache contents and subsequently the values which are returned from the cache in the lifetime of the session. Therefore, as best practice, do not to modify the objects returned by MyBatis.

    You can clear the local cache at any time by calling:

    java
    void clearCache()

    Ensuring that SqlSession is Closed

    java
    void close()

    The most important thing you must ensure is to close any session that you open. The best way to ensure this is to use the following unit of work pattern:

    java
    try (SqlSession session = sqlSessionFactory.openSession()) {
        // following 3 lines are pseudocode for "doing some work"
        session.insert(...);
        session.update(...);
        session.delete(...);
        session.commit();
    }

    NOTE Just like SqlSessionFactory, you can get the instance of Configuration that the SqlSession is using by calling the getConfiguration() method.

    java
    Configuration getConfiguration()

    Using Mappers

    java
    <T> T getMapper(Class<T> type)

    While the various insert, update, delete and select methods above are powerful, they are also very verbose, not type safe and not as helpful to your IDE or unit tests as they could be. We've already seen an example of using Mappers in the Getting Started section above.

    Therefore, a more common way to execute mapped statements is to use Mapper classes. A Mapper class is simply an interface with method definitions that match up against the SqlSession methods. The following example class demonstrates some method signatures and how they map to the SqlSession.

    java
    public interface AuthorMapper {
      // (Author) selectOne("selectAuthor", 5);
      Author selectAuthor(int id);
    
      // (List<Author>) selectList("selectAuthors")
      List<Author> selectAuthors();
    
      // (Map<Integer,Author>) selectMap("selectAuthorsAsMap", "id")
      @MapKey("id")
      Map<Integer, Author> selectAuthorsAsMap();
    
      // insert("insertAuthor", author)
      int insertAuthor(Author author);
    
      // update("updateAuthor", author)
      int updateAuthor(Author author);
    
      // delete("deleteAuthor", 5)
      int deleteAuthor(int id);
    }

    In a nutshell, each Mapper method signature should match that of the SqlSession method that it's associated to, but without the String parameter ID. Instead, the method name must match the mapped statement ID.

    In addition, the return type must match that of the expected result type for single results or an array or collection for multiple results or Cursor. All of the usual types are supported, including: Primitives, Maps, POJOs and JavaBeans.

    NOTE Mapper interfaces do not need to implement any interface or extend any class. As long as the method signature can be used to uniquely identify a corresponding mapped statement.

    NOTE Mapper interfaces can extend other interfaces. Be sure that you have the statements in the appropriate namespace when using XML binding to Mapper interfaces. Also, the only limitation is that you cannot have the same method signature in two interfaces in a hierarchy (a bad idea anyway).

    You can pass multiple parameters to a mapper method. If you do, they will be named by the literal "param" followed by their position in the parameter list by default, for example: #{param1}, #{param2} etc. If you wish to change the name of the parameters (multiple only), then you can use the @Param("paramName") annotation on the parameter.

    You can also pass a RowBounds instance to the method to limit query results.

    Mapper Annotations

    Since the very beginning, MyBatis has been an XML driven framework. The configuration is XML based, and the Mapped Statements have been defined in XML. With MyBatis 3, there are new options available. MyBatis 3 builds on top of a comprehensive and powerful Java based Configuration API. This Configuration API is the foundation for the XML based MyBatis configuration, as well as annotation-based configuration. Annotations offer a method to implement mapped statements without introducing the overhead of XML.

    NOTE Java annotations are limited in their expressiveness and flexibility. Because of this, the configuration of complex result maps is not as expressive as XML. By using a combination of @NamedResultMap annotations, and the ability of @Arg, @One and @Many to refer to other result maps by name, we believe that most of what can be achieved in XML is also possible with annotations.

    The annotations are as follows:

    Annotation Target XML equivalent Description
    @CacheNamespace Class <cache> Configures the cache for the given namespace (i.e. class). Attributes: implementation, eviction, flushInterval, size, readWrite, blocking, properties.
    @Property N/A `` Specifies the property value or placeholder(can replace by configuration properties that defined at the mybatis-config.xml). Attributes: name, value. (Available on MyBatis 3.4.2+)
    @CacheNamespaceRef Class <cacheRef> References the cache of another namespace to use. Note that caches declared in an XML mapper file are considered a separate namespace, even if they share the same FQCN. Attributes: value and name. If you use this annotation, you should be specified either value or name attribute. For the value attribute specify a java type indicating the namespace(the namespace name become a FQCN of specified java type), and for the name attribute(this attribute is available since 3.4.2) specify a name indicating the namespace.
    @ConstructorArgs Method <constructor> Collects a group of results to be passed to a result object constructor. Attributes: value, which is an array of Args.
    @Arg N/A
  • <arg>
  • ``
  • A single constructor argument that is part of a ConstructorArgs collection. Attributes: id, column, javaType, jdbcType, typeHandler, select, resultMap. The id attribute is a boolean value that identifies the property to be used for comparisons, similar to the `` XML element. Since 3.5.4, it can be used as repeatable annotation.
    @TypeDiscriminator Method <discriminator> A group of value cases that can be used to determine the result mapping to perform. Attributes: column, javaType, jdbcType, typeHandler, cases. The cases attribute is an array of Cases.
    @Case N/A <case> A single case of a value and its corresponding mappings. Attributes: value, type, results. The results attribute is an array of Results, thus this Case Annotation is similar to an actual ResultMap, specified by the Results annotation below.
    @NamedResultMap Type <resultMap> This annotation is used to create a result map that is not tied directly to a select statement and can be reused by multiple statements. This is analogous to creating a <resultMap> in XML. The annotation should be attached to a type (typically a mapper interface). The annotation can accept an array of @Arg, and/or an array of @Result, and/or a @TypeDiscriminator. Any number of @NamedResultMap annotations can be attached to a type.
    @Results Method <resultMap> A list of Result mappings that contain details of how a particular result column is mapped to a property or field. Attributes: value, id. The value attribute is an array of Result annotations. The id attribute is the name of the result mapping.
    @Result N/A
  • <result>
  • ``
  • A single result mapping between a column and a property or field. Attributes: id, column, property, javaType, jdbcType, typeHandler, one, many. The id attribute is a boolean value that indicates that the property should be used for comparisons (similar to `` in the XML mappings). The one attribute is for single associations, similar to <association>, and the many attribute is for collections, similar to <collection>. They are named as they are to avoid class naming conflicts. Since 3.5.4, it can be used as repeatable annotation.
    @One N/A <association> A mapping to a single property value of a complex type. Attributes: select, which is the fully qualified name of a mapped statement (i.e. mapper method) that can load an instance of the appropriate type. fetchType, which supersedes the global configuration parameter lazyLoadingEnabled for this mapping. resultMap(available since 3.5.5), which is the fully qualified name of a result map that map to a single container object from select result. columnPrefix(available since 3.5.5), which is column prefix for grouping select columns at nested result map. NOTE You will notice that join mapping is not supported via the Annotations API. This is due to the limitation in Java Annotations that does not allow for circular references.
    @Many N/A <collection> A mapping to a collection property of a complex type. Attributes: select, which is the fully qualified name of a mapped statement (i.e. mapper method) that can load a collection of instances of the appropriate types. fetchType, which supersedes the global configuration parameter lazyLoadingEnabled for this mapping. resultMap(available since 3.5.5), which is the fully qualified name of a result map that map to collection object from select result. columnPrefix(available since 3.5.5), which is column prefix for grouping select columns at nested result map. NOTE You will notice that join mapping is not supported via the Annotations API. This is due to the limitation in Java Annotations that does not allow for circular references.
    @MapKey Method This is used on methods which return type is a Map. It is used to convert a List of result objects as a Map based on a property of those objects. Attributes: value, which is a property used as the key of the map.
    @Options Method Attributes of mapped statements. This annotation provides access to the wide range of switches and configuration options that are normally present on the mapped statement as attributes. Rather than complicate each statement annotation, the Options annotation provides a consistent and clear way to access these. Attributes: useCache=true, flushCache=FlushCachePolicy.DEFAULT, resultSetType=DEFAULT, statementType=PREPARED, fetchSize=-1, timeout=-1, useGeneratedKeys=false, keyProperty="", keyColumn="", resultSets="", and databaseId="". It's important to understand that with Java Annotations, there is no way to specify null as a value. Therefore, once you engage the Options annotation, your statement is subject to all of the default values. Pay attention to what the default values are to avoid unexpected behavior. The databaseId(Available since 3.5.5), in case there is a configured DatabaseIdProvider, the MyBatis use the Options with no databaseId attribute or with a databaseId that matches the current one. If found with and without the databaseId the latter will be discarded.Note that keyColumn is only required in certain databases (like Oracle and PostgreSQL). See the discussion about keyColumn and keyProperty above in the discussion of the insert statement for more information about allowable values in these attributes.
  • @Insert
  • @Update
  • @Delete
  • @Select
  • Method
  • </li><li>
  • <delete>
  • <select>
  • Each of these annotations represents the actual SQL that is to be executed. They each take an array of strings (or a single string will do). If an array of strings is passed, they are concatenated with a single space between each to separate them. This helps avoid the "missing space" problem when building SQL in Java code. However, you're also welcome to concatenate together a single string if you like. Attributes: value, which is the array of Strings to form the single SQL statement. The databaseId(Available since 3.5.5), in case there is a configured DatabaseIdProvider, the MyBatis use a statement with no databaseId attribute or with a databaseId that matches the current one. If found with and without the databaseId the latter will be discarded.
  • @InsertProvider
  • @UpdateProvider
  • @DeleteProvider
  • @SelectProvider
  • Method
  • </li><li>
  • <delete>
  • <select>
  • Allows for creation of dynamic SQL. These alternative SQL annotations allow you to specify a class and a method name that will return the SQL to run at execution time (Since 3.4.6, you can specify the CharSequence instead of String as a method return type). Upon executing the mapped statement, MyBatis will instantiate the class, and execute the method, as specified by the provider. You can pass objects that passed to arguments of a mapper method, "Mapper interface type", "Mapper method" and "Database ID" via the ProviderContext(available since MyBatis 3.4.5 or later) as method argument. (In MyBatis 3.4 or later, it's allow multiple parameters) Attributes: value, type, method and databaseId. The value and type attribute is a class (The type attribute is alias for value, you must be specify either one. But both attributes can be omit when specify the defaultSqlProviderType as global configuration). The method is the name of the method on that class (Since 3.5.1, you can omit method attribute, the MyBatis will resolve a target method via the ProviderMethodResolver interface. If not resolve by it, the MyBatis use the reserved fallback method that named provideSql). The databaseId(Available since 3.5.5), in case there is a configured DatabaseIdProvider, the MyBatis will use a provider method with no databaseId attribute or with a databaseId that matches the current one. If found with and without the databaseId the latter will be discarded. NOTE Following this section is a discussion about the class, which can help build dynamic SQL in a cleaner, easier to read way.
    @Param Parameter N/A If your mapper method takes multiple parameters, this annotation can be applied to a mapper method parameter to give each of them a name. Otherwise, multiple parameters will be named by their position prefixed with "param" (not including any RowBounds parameters). For example #{param1}, #{param2} etc. is the default. With @Param("person"), the parameter would be named #{person}.
    @SelectKey Method <selectKey> This annotation duplicates the <selectKey> functionality for methods annotated with @Insert, @InsertProvider, @Update, or @UpdateProvider. It is ignored for other methods. If you specify a @SelectKey annotation, then MyBatis will ignore any generated key properties set via the @Options annotation, or configuration properties. Attributes: statement an array of strings which is the SQL statement to execute, keyProperty which is the property of the parameter object that will be updated with the new value, before which must be either true or false to denote if the SQL statement should be executed before or after the insert, resultType which is the Java type of the keyProperty, and statementType is a type of the statement that is any one of STATEMENT, PREPARED or CALLABLE that is mapped to Statement, PreparedStatement and CallableStatement respectively. The default is PREPARED. The databaseId(Available since 3.5.5), in case there is a configured DatabaseIdProvider, the MyBatis will use a statement with no databaseId attribute or with a databaseId that matches the current one. If found with and without the databaseId the latter will be discarded.
    @ResultMap Method N/A This annotation is used to provide the id of a <resultMap> element in an XML mapper, or the id of @NamedResultMap or @Results, to a @Select or @SelectProvider annotation. This allows annotated selects to reuse resultmaps that are defined in XML. This annotation will override any @Results or @ConstructorArgs annotation if both are specified on an annotated select.
    @ResultOrdered Method "resultOrdered" attribute of select XML This annotation is used to denote that a result set is known to be ordered. This is required when building object that have collection in their constructors
    @ResultType Method N/A This annotation is used when using a result handler. In that case, the return type is void so MyBatis must have a way to determine the type of object to construct for each row. If there is an XML result map, use the @ResultMap annotation. If the result type is specified in XML on the <select> element, then no other annotation is necessary. In other cases, use this annotation. For example, if a @Select annotated method will use a result handler, the return type must be void and this annotation (or @ResultMap) is required. This annotation is ignored unless the method return type is void.
    @Flush Method N/A If this annotation is used, it can be called the SqlSession#flushStatements() via method defined at a Mapper interface.(MyBatis 3.3 or above)

    Mapper Annotation Examples

    This example shows using the @SelectKey annotation to retrieve a value from a sequence before an insert:

    java
    @Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
    @SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
    int insertTable3(Name name);

    This example shows using the @SelectKey annotation to retrieve an identity value after an insert:

    java
    @Insert("insert into table2 (name) values(#{name})")
    @SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
    int insertTable2(Name name);

    This example shows using the @Flush annotation to call the SqlSession#flushStatements():

    java
    @Flush
    List<BatchResult> flush();

    These examples show how to name a ResultMap by specifying id attribute of @Results annotation.

    java
    @Results(id = "userResult", value = {
      @Result(property = "id", column = "uid", id = true),
      @Result(property = "firstName", column = "first_name"),
      @Result(property = "lastName", column = "last_name")
    })
    @Select("select * from users where id = #{id}")
    User getUserById(Integer id);
    
    @Results(id = "companyResults")
    @ConstructorArgs({
      @Arg(column = "cid", javaType = Integer.class, id = true),
      @Arg(column = "name", javaType = String.class)
    })
    @Select("select * from company where id = #{id}")
    Company getCompanyById(Integer id);

    This example shows solo parameter using the SelectProvider annotation:

    java
    @SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
    List<User> getUsersByName(String name);
    
    class UserSqlBuilder {
      public static String buildGetUsersByName(final String name) {
        return new SQL(){{
          SELECT("*");
          FROM("users");
          if (name != null) {
            WHERE("name like #{value} || '%'");
          }
          ORDER_BY("id");
        }}.toString();
      }
    }

    This example shows multiple parameters using the Sql Provider annotation:

    java
    @SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
    List<User> getUsersByName(
        @Param("name") String name, @Param("orderByColumn") String orderByColumn);
    
    class UserSqlBuilder {
    
      // If not use @Param, you should be define same arguments with mapper method
      public static String buildGetUsersByName(
          final String name, final String orderByColumn) {
        return new SQL(){{
          SELECT("*");
          FROM("users");
          WHERE("name like #{name} || '%'");
          ORDER_BY(orderByColumn);
        }}.toString();
      }
    
      // If use @Param, you can define only arguments to be used
      public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) {
        return new SQL(){{
          SELECT("*");
          FROM("users");
          WHERE("name like #{name} || '%'");
          ORDER_BY(orderByColumn);
        }}.toString();
      }
    }

    This example shows usage that share an sql provider class to all mapper methods using global configuration(Available since 3.5.6):

    java
    Configuration configuration = new Configuration();
    configuration.setDefaultSqlProviderType(TemplateFilePathProvider.class); // Specify an sql provider class for sharing on all mapper methods
    // ...
    java
    // Can omit the type/value attribute on sql provider annotation
    // If omit it, the MyBatis apply the class that specified on defaultSqlProviderType.
    public interface UserMapper {
    
      @SelectProvider // Same with @SelectProvider(TemplateFilePathProvider.class)
      User findUser(int id);
    
      @InsertProvider // Same with @InsertProvider(TemplateFilePathProvider.class)
      void createUser(User user);
    
      @UpdateProvider // Same with @UpdateProvider(TemplateFilePathProvider.class)
      void updateUser(User user);
    
      @DeleteProvider // Same with @DeleteProvider(TemplateFilePathProvider.class)
      void deleteUser(int id);
    }

    This example shows usage the default implementation of ProviderMethodResolver(available since MyBatis 3.5.1 or later):

    java
    @SelectProvider(UserSqlProvider.class)
    List<User> getUsersByName(String name);
    
    // Implements the ProviderMethodResolver on your provider class
    class UserSqlProvider implements ProviderMethodResolver {
      // In default implementation, it will resolve a method that method name is matched with mapper method
      public static String getUsersByName(final String name) {
        return new SQL(){{
          SELECT("*");
          FROM("users");
          if (name != null) {
            WHERE("name like #{value} || '%'");
          }
          ORDER_BY("id");
        }}.toString();
      }
    }

    This example shows usage the databaseId attribute on the statement annotation(Available since 3.5.5):

    java
    @Select(value = "SELECT SYS_GUID() FROM dual", databaseId = "oracle") // Use this statement if DatabaseIdProvider provide "oracle"
    @Select(value = "SELECT uuid_generate_v4()", databaseId = "postgres") // Use this statement if DatabaseIdProvider provide "postgres"
    @Select("SELECT RANDOM_UUID()") // Use this statement if the DatabaseIdProvider not configured or not matches databaseId
    String generateId();

    File: src/site/markdown/logging.md

    title: MyBatis 3 | Logging
    author: Clinton Begin

    Avoid blank site

    Logging

    MyBatis provides logging information through the use of an internal log factory. The internal log factory will delegate logging information to one of the following log implementations:

    • SLF4J
    • Apache Commons Logging
    • Log4j 2
    • Log4j (deprecated since 3.5.9)
    • JDK logging

    The logging solution chosen is based on runtime introspection by the internal MyBatis log factory. The MyBatis log factory will use the first logging implementation it finds (implementations are searched in the above order). If MyBatis finds none of the above implementations, then logging will be disabled.

    Many environments ship Commons Logging as a part of the application server classpath (good examples include Tomcat and WebSphere). It is important to know that in such environments, MyBatis will use Commons Logging as the logging implementation. In an environment like WebSphere this will mean that your Log4J configuration will be ignored because WebSphere supplies its own proprietary implementation of Commons Logging. This can be very frustrating because it will appear that MyBatis is ignoring your Log4J configuration (in fact, MyBatis is ignoring your Log4J configuration because MyBatis will use Commons Logging in such environments). If your application is running in an environment where Commons Logging is included in the classpath but you would rather use one of the other logging implementations you can select a different logging implementation by adding a setting in mybatis-config.xml file as follows:

    xml
    <configuration>
      <settings>
        ...
        <setting name="logImpl" value="LOG4J"/>
        ...
      </settings>
    </configuration>

    Valid values are SLF4J, LOG4J, LOG4J2, JDK_LOGGING, COMMONS_LOGGING, STDOUT_LOGGING, NO_LOGGING or a full qualified class name that implements org.apache.ibatis.logging.Log and gets an string as a constructor parameter.

    You can also select the implementation by calling one of the following methods:

    java
    org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
    org.apache.ibatis.logging.LogFactory.useLog4JLogging();
    org.apache.ibatis.logging.LogFactory.useLog4J2Logging();
    org.apache.ibatis.logging.LogFactory.useJdkLogging();
    org.apache.ibatis.logging.LogFactory.useCommonsLogging();
    org.apache.ibatis.logging.LogFactory.useStdOutLogging();

    If you choose to call one of these methods, you should do so before calling any other MyBatis method. Also, these methods will only switch to the requested log implementation if that implementation is available on the runtime classpath. For example, if you try to select Log4J2 logging and Log4J2 is not available at runtime, then MyBatis will ignore the request to use Log4J2 and will use it's normal algorithm for discovering logging implementations.

    The specifics of SLF4J, Apache Commons Logging, Apache Log4J and the JDK Logging API are beyond the scope of this document. However the example configuration below should get you started. If you would like to know more about these frameworks, you can get more information from the following locations:

    Logging Configuration

    To see MyBatis logging statements you may enable logging on a package, a mapper fully qualified class name, a namespace o a fully qualified statement name.

    Again, how you do this is dependent on the logging implementation in use. We'll show how to do it with SLF4J(Logback). Configuring the logging services is simply a matter of including one or more extra configuration files (e.g. logback.xml) and sometimes a new JAR file. The following example configuration will configure full logging services using SLF4J(Logback) as a provider. There are 2 steps.

    Step 1: Add the SLF4J + Logback JAR files

    Because we are using SLF4J(Logback), we will need to ensure its JAR file is available to our application. To use SLF4J(Logback), you need to add the JAR file to your application classpath.

    For web or enterprise applications you can add the logback-classic.jar ,logback-core.jar and slf4j-api.jar to your WEB-INF/lib directory, or for a standalone application you can simply add it to the JVM -classpath startup parameter.

    If you use the maven, you can download jar files by adding following settings on your pom.xml.

    xml
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.x.x</version>
    </dependency>

    Step 2: Configure Logback

    Configuring Logback is simple. Suppose you want to enable the log for this mapper:

    java
    package org.mybatis.example;
    public interface BlogMapper {
      @Select("SELECT * FROM blog WHERE id = #{id}")
      Blog selectBlog(int id);
    }

    Create a file called logback.xml as shown below and place it in your classpath:

    xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration>
    <configuration>
    
      <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
          <pattern>%5level [%thread] - %msg%n</pattern>
        </encoder>
      </appender>
    
      <logger name="org.mybatis.example.BlogMapper">
        <level value="trace"/>
      </logger>
      <root level="error">
        <appender-ref ref="stdout"/>
      </root>
    
    </configuration>

    The above file will cause SLF4J(Logback) to report detailed logging for org.mybatis.example.BlogMapper and just errors for the rest of the classes of your application.

    If you want to tune the logging at a finer level you can turn logging on for specific statements instead of the whole mapper file. The following line will enable logging just for the selectBlog statement:

    xml
    <logger name="org.mybatis.example.BlogMapper.selectBlog">
      <level value="trace"/>
    </logger>

    By the contrary you may want to enable logging for a group of mappers. In that case you should add as a logger the root package where your mappers reside:

    xml
    <logger name="org.mybatis.example">
      <level value="trace"/>
    </logger>

    There are queries that can return huge result sets. In that cases you may want to see the SQL statement but not the results. For that purpose SQL statements are logged at the DEBUG level (FINE in JDK logging) and results at the TRACE level (FINER in JDK logging), so in case you want to see the statement but not the result, set the level to DEBUG.

    xml
    <logger name="org.mybatis.example">
      <level value="debug"/>
    </logger>

    But what about if you are not using mapper interfaces but mapper XML files like this one?

    xml
    <?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>

    In that case you can enable logging for the whole XML file by adding a logger for the namespace as shown below:

    xml
    <logger name="org.mybatis.example.BlogMapper">
      <level value="trace"/>
    </logger>

    Or for an specific statement:

    xml
    <logger name="org.mybatis.example.BlogMapper.selectBlog">
      <level value="trace"/>
    </logger>

    Yes, as you may have noticed, there is no difference in configuring logging for mapper interfaces or for XML mapper files.

    NOTE If you are using SLF4J or Log4j 2 MyBatis will call it using the marker MYBATIS.

    The remaining configuration in the logback.xml file is used to configure the appenders, which is beyond the scope of this document. However, you can find more information at the Logback website. Or, you could simply experiment with it to see what effects the different configuration options have.

    Configuration example for Log4j 2

    xml
    <!-- pom.xml -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.x.x</version>
    </dependency>
    xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- log4j2.xml -->
    <Configuration xmlns="http://logging.apache.org/log4j/2.0/config">
    
      <Appenders>
        <Console name="stdout" target="SYSTEM_OUT">
          <PatternLayout pattern="%5level [%t] - %msg%n"/>
        </Console>
      </Appenders>
    
      <Loggers>
        <Logger name="org.mybatis.example.BlogMapper" level="trace"/>
        <Root level="error" >
          <AppenderRef ref="stdout"/>
        </Root>
      </Loggers>
    
    </Configuration>

    Configuration example for Log4j

    xml
    <!-- pom.xml -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    properties
    # log4j.properties
    log4j.rootLogger=ERROR, stdout
    
    log4j.logger.org.mybatis.example.BlogMapper=TRACE
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

    Configuration example for JDK logging

    properties
    # logging.properties
    handlers=java.util.logging.ConsoleHandler
    .level=SEVERE
    
    org.mybatis.example.BlogMapper.level=FINER
    
    java.util.logging.ConsoleHandler.level=ALL
    java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
    java.util.logging.SimpleFormatter.format=%1$tT.%1$tL %4$s %3$s - %5$s%6$s%n

    --- METRICS ---