Inyectar mappers

En lugar de codificar DAOs (data access objects) manualmente usando la clase SqlSessionDaoSupport o SqlSessionTemplate, Mybatis-Spring puede crear un mapper thread-safe que puedes inyectar directamente en otros beans.

<bean id="fooService" class="org.mybatis.spring.sample.service.FooServiceImpl">
  <constructor-arg ref="userMapper" />
</bean>

Una vez inyectado, el mapper está listo para se usado en la lógica de aplicación:

public class FooServiceImpl implements FooService {

  private final UserMapper userMapper;

  public FooServiceImpl(UserMapper userMapper) {
    this.userMapper = userMapper;
  }

  public User doSomeBusinessStuff(String userId) {
    return this.userMapper.getUser(userId);
  }
}

Observa que no se usa la SqlSession ni ninguna otra referencia a MyBatis en este código. No es necesario ni siquiera crear o cerrar la sesión, MyBatis-Spring se encarga de ello.

Registrar un mapper

La forma de registrar un mapper varía según si quieres usar la configuración XML clásica o la nueva Java Config de Spring 3.0+ (También conocida como @Configuration).

Con confiugración XML

Un mapper se registra en Spring incluyendo un MapperFactoryBean en tu fichero de configuración XML, de la siguiente forma:

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

Si el mapper UserMapper tiene un fichero XML de mapeo asociado el MapperFactoryBean lo cargará automáticamente. Por lo tanto no es necesario especificar dicho mapper en el fichero de configuración de MyBatis a no ser que los ficheros XML estén en una lugar distinto del classpath. Ver la sección de SqlSessionFactoryBean y la propiedad configLocation para más información.

El MapperFactoryBean requiere o un SqlSessionFactory o un SqlSessionTemplate. Ambos se pueden informar usando sendas propiedades sqlSessionFactory y sqlSessionTemplate. Si ambas propiedades han sdo informadas la SqlSessionFactory se ignora. Dado que un SqlSessionTemplate debe tener un session factory dicho factory se usará por el MapperFactoryBean.

Con Java Config

@Configuration
public class MyBatisConfig {
  @Bean
  public MapperFactoryBean<UserMapper> userMapper() throws Exception {
    MapperFactoryBean<UserMapper> factoryBean = new MapperFactoryBean<>(UserMapper.class);
    factoryBean.setSqlSessionFactory(sqlSessionFactory());
    return factoryBean;
  }
}

Escanear mappers

No es necesario registrar los mappers uno por uno en el fichero XML de Spring. En lugar de esto, puede dejar que MyBatis-Spring los busque en tu classpath.

Hay tres formas distintas de hacerlo:

  • Usando el elemneto <mybatis:scan/>.
  • Usando la anotación @MapperScan
  • Usando un fichero clásico XML de configuración de Spring y añadiendo el bean MapperScannerConfigurer

Tango <mybatis:scan/> como @MapperScan son características añadidas en MyBatis-Spring 1.2.0. @MapperScan requiere Spring 3.1+.

Since 2.0.2, mapper scanning feature support a option (lazy-initialization) that control lazy initialization enabled/disabled of mapper bean. The motivation for adding this option is supporting a lazy initialization control feature supported by Spring Boot 2.2. The default of this option is false (= not use lazy initialization). If developer want to use lazy initialization for mapper bean, it should be set to the true expressly.

IMPORTANT If use the lazy initialization feature, the developer need to understand following limitations. If any of following conditions are matches, usually the lazy initialization feature cannot use on your application.

  • When refers to the statement of other mapper using <association>(@One) and <collection>(@Many)
  • When includes to the fragment of other mapper using <include>
  • When refers to the cache of other mapper using <cache-ref>(@CacheNamespaceRef)
  • When refers to the result mapping of other mapper using <select resultMap="...">(@ResultMap)

NOTE However, It become possible to use it by simultaneously initializing dependent beans using @DependsOn(Spring's feature) as follow:

@DependsOn("vendorMapper")
public interface GoodsMapper {
  // ...
}

Since 2.0.6, the develop become can specified scope of mapper using mapper scanning feature option(default-scope) and scope annotation(@Scope, @RefreshScope, etc …). The motivation for adding this option is supporting the refresh scope provided by the Spring Cloud. The default of this option is empty (= equiv to specify the singleton scope). The default-scope apply to the mapper bean(MapperFactoryBean) when scope of scanned bean definition is singleton(default scope) and create a scoped proxy bean for scanned mapper when final scope is not singleton.

<mybatis:scan>

El elemento XML <mybatis:scan/> busca mappers de una forma muy similar a cómo <context:component-scan/> busca beans.

A continuación se muestra un fichero XML de configuración:

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">

  <mybatis:scan base-package="org.mybatis.spring.sample.mapper" />

  <!-- ... -->

</beans>

La propiedad basePackage te permite indicar el paquete base donde residen tus mappers. Puedes indicar más de un paquete usando un punto y coma o una coma como separador. Los mappers serán buscados de forma recursiva comenzando en el/los paquetes especificados.

Fíjate que no es necesario indicar una SqlSessionFactory o SqlSessionTemplate porque el <mybatis:scan/> creará MapperFactoryBeans que pueden ser autowired. Pero si usas más de un DataSource el autowire puede que no te funcione. En este caso puedes usar las propiedades factory-ref or template-ref para indicar los beans correctos a utilizar.

<mybatis:scan/> soporta el filtrado de mappers mediante una interfaz marcador o una anotación. La propiedad annotation especifica la anotación que se debe buscar. La propiedad marker-interface especifica la interfaz a buscar. Si se indican ambas se añadirán todos los mappers que cumplan cualquier criterio. Por defecto ambas propiedades son null asi que todos los interfaces de los paquetes base serán cargados como mappers.

Los mappers descubiertos serán nombrados usando la estratégia de nombres por defecto de Spring para los componentes autodetectados (see the Spring reference document(Core Technologies -Naming autodetected components-). Es decir, si no se encuentra ninguna anotación, se usará el nombre no cualificado sin capitalizar del mapper. Pero si se encuentra una anotación @Component o JSR-330 @Named se obtendrá el nombre de dicha anotación. Fíjate que puedes usar como valor de la annotation el valor org.springframework.stereotype.Component, jakarta.inject.Named (if you have Jakarta EE) o una anotación propia (que debe ser a su vez anotada) de forma que la anotación hará las veces de localizador y de proveedor de nombre.

NOTE <context:component-scan/> no puede encontrar y registrar mappers. Los mappers son interfaces y, para poderlos registrar en Spring, el scanner deben conocer cómo crear un MapperFactoryBean para cada interfaz encontrado.

@MapperScan

Si usas la Java Configuration de Spring (@Configuration) posiblemente prefieras usar @MapperScan en lugar de <mybatis:scan/>.

La anotación @MapperScan se usa de la siguiente forma:

@Configuration
@MapperScan("org.mybatis.spring.sample.mapper")
public class AppConfig {
  // ...
}

La anotación fucniona exactamente igual que <mybatis:scan/> que hemos visto en la sección anterior. También te permite especificar un interfaz marcador o una anotación mediante sus propiedades markerInterface y annotationClass. Tambien puedes indicar una SqlSessionFactory o un SqlSessionTemplate específicos mediante las propiedades sqlSessionFactory y sqlSessionTemplate.

NOTE Since 2.0.4, If basePackageClasses or basePackages are not defined, scanning will occur from the package of the class that declares this annotation.

MapperScannerConfigurer

MapperScannerConfigurer es un BeanDefinitionRegistryPostProcessor que se puede incluir como un bean normal en el fichero clásico XML de configuración de Spring. Para configurar un MapperScannerConfigurer añade lo siguiente al fichero de configuración de Spring:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="org.mybatis.spring.sample.mapper" />
</bean>

Si quieres indicar un sqlSessionFactory o un sqlSessionTemplate observa que se requeiren los nombres de los beans y no sus referencias por ello se usa el atributo value en lugar del habitual ref:

<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />