Mybatis is a popular orm framework. It can maintain SQL statements in XML and is very flexible. This is the key for many developers to choose it as an orm framework.
Using mybatis in spring boot applications is easy. Only a few steps are required.
This article focuses on “How to integrate mybatis in spring boot”. It does not explain the detailed usage of mybatis. If you have questions, you can refer to the official mybatis documentation.
Create a Maven project
pom
The essential dependencies are: mysql driver
, database connection pool
, mybatis-spring-boot-starter
.
It is easier to use the officially provided mybatis-spring-boot-starter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.springcloud</groupId>
<artifactId>springboot-mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<encoding>UTF-8</encoding>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- MYSQL driver is required -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Using the high-performance HikariCP database connection pool -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<!-- MyBatis starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>
</project>
|
mapper Interface definition
Create a Mapper interface whose implementation will be dynamically generated during application runtime. Just as a demo, so it’s not too complicated. Only a simple query method now
is provided. Gets the time on the database server.
1
2
3
4
5
6
7
8
|
package io.springcloud.mybatis.mapper;
import java.time.LocalDateTime;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface FooMapper {
LocalDateTime now();
}
|
mapper xml files
I put the xml file in the src/main/resources/mapper
directory.
1
2
3
4
5
6
7
8
9
|
<!-- foo-mapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.springcloud.mybatis.mapper.FooMapper">
<select id="now" resultType="java.time.LocalDateTime">
SELECT NOW();
</select>
</mapper>
|
application.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
server:
port: 80
logging:
# Set the log level of the package where the mapper interface is located to DEBUG, so that you can see more detailed SQL execution logs.
level:
"io.springcloud.mybatis.mapper": "DEBUG"
spring:
# MYSQL data source configuration. No need to explain too much.
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root
password: root
mybatis:
# Set the xml configuration file address for mybatis.
# config-location: "classpath:mybatis/mybatis-config.xml"
# Loads all files in the 'mapper' directory (regardless of directory hierarchy) with names ending in '-mapper.xml'.
mapper-locations:
- "classpath:mapper/**/*-mapper.xml"
|
mybatis.config-location
is used to set the path to the xml configuration file for mybatis. The configuration file allows you to configure mybatis plugins, cache, etc. For demo projects, this configuration is not required. If you want to know more, you can refer to the official documentation.
mybatis.mapper-locations
is a key configuration. It specifies the path to load the xml
file for the mapper interface (wildcards can be used). If the configuration is incorrect and the xml file is not loaded correctly, then an exception will occur at runtime: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)...
If the xml
file is not in the current project, for example in another jar package. Then you need to use classpath*:
as path prefix.
Mybatis has more configuration items here, which you can view through the configuration class org.mybatis.spring.boot.autoconfigure.MybatisProperties
or the official documentation.
annotation drivers
Set the package where the mapper
interface resides (there can be more than one) via the @MapperScan
annotation.
An annotation class can also be set via the annotationClass
attribute. For example annotationClass = Mapper.class
. Then only the interfaces annotated with @Mapper
will be loaded.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package io.springcloud.mybatis;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages = { "io.springcloud.mybatis.mapper" }, annotationClass = Mapper.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
|
test
After everything is ready to go. Verify that everything is working by running a test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
package io.springcloud.test;
import java.time.format.DateTimeFormatter;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import io.springcloud.mybatis.Application;
import io.springcloud.mybatis.mapper.FooMapper;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
static final Logger LOGGER = LoggerFactory.getLogger(ApplicationTest.class);
@Autowired
FooMapper fooMapper;
@Test
@Transactional(readOnly = true)
@Rollback(false)
public void test () {
LOGGER.info("result={}", this.fooMapper.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
}
|
The key logs output from the console are as follows:
1
2
3
4
5
6
7
8
|
2022-03-10 18:55:59.606 INFO 10860 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2022-03-10 18:56:00.300 INFO 10860 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2022-03-10 18:56:00.311 INFO 10860 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@457c9034 testClass = ApplicationTest, testInstance = io.springcloud.test.ApplicationTest@c7ba306, testMethod = test@ApplicationTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@345f69f3 testClass = ApplicationTest, locations = '{}', classes = '{class io.springcloud.mybatis.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=0}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@c730b35, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@22555ebf, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@10683d9d, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@662ac478, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3d285d7e, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@3c407114], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> false, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.jdbc.support.JdbcTransactionManager@118fbaf0]; rollback [false]
2022-03-10 18:56:01.061 DEBUG 10860 --- [ main] i.s.mybatis.mapper.FooMapper.now : ==> Preparing: SELECT NOW();
2022-03-10 18:56:01.100 DEBUG 10860 --- [ main] i.s.mybatis.mapper.FooMapper.now : ==> Parameters:
2022-03-10 18:56:01.141 DEBUG 10860 --- [ main] i.s.mybatis.mapper.FooMapper.now : <== Total: 1
2022-03-10 18:56:01.146 INFO 10860 --- [ main] io.springcloud.test.ApplicationTest : result=2022-03-10 18:56:01
2022-03-10 18:56:01.161 INFO 10860 --- [ main] o.s.t.c.transaction.TransactionContext : Committed transaction for test: [DefaultTestContext@457c9034 testClass = ApplicationTest, testInstance = io.springcloud.test.ApplicationTest@c7ba306, testMethod = test@ApplicationTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@345f69f3 testClass = ApplicationTest, locations = '{}', classes = '{class io.springcloud.mybatis.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=0}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@c730b35, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@22555ebf, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@10683d9d, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@662ac478, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3d285d7e, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@3c407114], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> false, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]
|
Everything is OK.
Summary
As you can see, it’s quite simple. Let’s summarize the key steps.
- Add the required dependencies (database driver, data source, mybats starter).
- Set the
mybatis.mapper-locations
property in the application
configuration file. This is used to set the load path of the xml file for the mapper interface.
- Set the package path of the mapper interface by annotating
@MapperScan
.