Injecting Mappers

Rather than code data access objects (DAOs) manually using SqlSessionDaoSupport or SqlSessionTemplate, Mybatis-Spring can create a thread safe mapper that you can inject directly into other beans:

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

Once injected, the mapper is ready to be used in application logic:

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

Notice that there are no SqlSession or MyBatis references in this code. Nor is there any need to create, open or close the session, MyBatis-Spring will take care of that.

Registering a mapper

The way you register a mapper depends on whether you are using a classic XML configuration or the new 3.0+ Java Config (a.k.a. @Configuration).

With XML Config

A mapper is registered to Spring by including a MapperFactoryBean in your XML config file like follows:

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

If the UserMapper has a corresponding MyBatis XML mapper file in the same classpath location as the mapper interface, it will be parsed automatically by the MapperFactoryBean. There is no need to specify the mapper in a MyBatis configuration file unless the mapper XML files are in a different classpath location. See the SqlSessionFactoryBean's configLocation property for more information.

Note that MapperFactoryBean requires either an SqlSessionFactory or an SqlSessionTemplate. These can be set through the respective sqlSessionFactory and sqlSessionTemplate properties. If both properties are set, the SqlSessionFactory is ignored. Since the SqlSessionTemplate is required to have a session factory set, that factory will be used by MapperFactoryBean.

With 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;
  }
}

Scanning for mappers

There is no need to register all your mappers one by one. Instead, you can let MyBatis-Spring scan your classpath for them.

There are three different ways to do it:

  • Using the <mybatis:scan> element.
  • Using the annotation @MapperScan
  • Using a classic Spring xml file and registering the MapperScannerConfigurer

Both <mybatis:scan/> and @MapperScan are features introduced in MyBatis-Spring 1.2.0. @MapperScan requires Spring 3.1+.

Since 2.0.2, mapper scanning feature support an 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>

The <mybatis:scan/> XML element will search for mappers in a very similar way than the Spring built-in element <context:component-scan/> searches for beans.

Follows below a sample XML configuration:

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

The base-package attribute lets you set the base package for your mapper interface files. You can set more than one package by using a semicolon or comma as a separator. Mappers will be searched for recursively starting in the specified package(s).

Notice that there is no need to specify a SqlSessionFactory or SqlSessionTemplate as an attribute in the <mybatis:scan/> element because it will create MapperFactoryBeans that can be autowired. But if you are using more than one DataSource autowire may not work for you. In this case you can use the factory-ref or template-ref attributes to set the right bean name to use.

<mybatis:scan/> supports filtering the mappers created by either specifying a marker interface or an annotation. The annotation property specifies an annotation to search for. The marker-interface attribute specifies a parent interface to search for. If both properties are specified, mappers are added for interfaces that match either criteria. By default, these two properties are null, so all interfaces in the given base package(s) will be loaded as mappers.

Discovered mappers will be named using Spring default naming strategy for autodetected components (see the Spring reference document(Core Technologies -Naming autodetected components-)). That is, if no annotation is found, it will use the uncapitalized non-qualified class name of the mapper. But if either a @Component or a JSR-330 @Named annotation is found it will get the name from the annotation. Notice that you can set the annotation attribute to org.springframework.stereotype.Component, jakarta.inject.Named (if you have JakartaEE) or to your own annotation (that must be itself annotated) so the annotation will work both as a marker and as a name provider.

NOTE <context:component-scan/> won't be able to scan and register mappers. Mappers are interfaces and, in order to register them to Spring, the scanner must know how to create a MapperFactoryBean for each interface it finds.

@MapperScan

If you are using the Spring Java Configuration (a.k.a @Configuration) you would prefer to use the @MapperScan rather than the <mybatis:scan/>.

The @MapperScan annotation is used as follows:

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

The annotation works in the same exact way than <mybatis:scan/> we saw in the previous section. It also lets you specify a marker interface or an annotation class through its properties markerInterface and annotationClass. You can also provide an specific SqlSessionFactory or SqlSessionTemplate by using its properties sqlSessionFactory and 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

The MapperScannerConfigurer is a BeanDefinitionRegistryPostProcessor that can be included in a classic xml application context as a normal bean. To set up a MapperScannerConfigurer add the following to the Spring configuration:

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

If you need to specify an specific sqlSessionFactory or sqlSessionTemplate note that bean names are required, not bean references, thus the value attribute is used instead of the usual ref:

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

NOTE sqlSessionFactoryBean and sqlSessionTemplateBean properties were the only option available up to MyBatis-Spring 1.0.2 but given that the MapperScannerConfigurer runs earlier in the startup process that PropertyPlaceholderConfigurer there were frequent errors. For that purpose that properties have been deprecated and the new properties sqlSessionFactoryBeanName and sqlSessionTemplateBeanName are recommended.