Mybatis-spring-boot-starter的使用方法
下面将介绍mybatis-spring-boot-starter的使用方式,该项目于2015年11月发布了1.0.0版本,随后于2016年4月19日发布了1.1版本。
在使用Spring Boot上的MyBatis时,开发者需要自行定义SqlSessionFactoryBean和SqlSessionTemplate的Bean定义,这些都是由mybatis-spring提供的。但是,随着mybatis-spring-boot-starter的出现,这些Bean定义将通过自动配置来解决。
首先,我们将使用mybatis-spring-boot-starter来创建一个简单的CLI(命令行界面)应用程序。
注意:
2019/7/16: 追记
已添加了指向使用JDK 13引入的Text Blocks进行演示的链接(目前为预览版)。2019/7/15: 追记
由于在2019年7月15日发布了版本2.1.0,因此将内容改为基于2.1。2019/5/7: 追记
已添加了mybatis-spring-boot-starter 2.0更改的链接。此外,还添加了关于在2.0.1上使用mybatis.type-aliases-package时可能发生的故障的说明(链接到说明)。2019/5/3: 追记
由于在2019年1月22日发布了版本2.0.0(版本2.0.1在2019年4月发布),因此将内容改为基于2.0。2017/4/10: 追记
由于在2017年4月10日发布了版本1.3.0,因此将内容改为基于1.3。2017/1/4: 追记
由于在2017年1月2日发布了版本1.2.0,因此将内容改为基于1.2。请注意,如果使用版本1.2,则需要在Spring Boot 1.4及以后(准确地说是在Spring Framework 4.3及以上)上运行。(2017/1/4)
另外,关于主要/次要版本升级的更改内容,请参阅以下帖子汇总。
-
- mybatis-spring-boot-starter 2.1の変更点
-
- mybatis-spring-boot-starter 2.0の変更点
-
- mybatis-spring-boot-starter 1.3の変更点
-
- mybatis-spring-boot-starter 1.2の変更点
- mybatis-spring-boot-starter 1.1の変更点
验证版本
-
- MyBatis Spring Boot Starter 2.1.0
-
- MyBatis 3.5.2
-
- MyBatis Spring 2.0.2
-
- Spring Boot 2.1.6.RELEASE
- Spring Framework 5.1.8.RELEASE
验证码
绝对是一个很受欢迎的编程语言。
- https://github.com/kazuki43zoo/qiita-materials/tree/master/mybatis-spring-boot/mybatis-demo
Text Blocks 版本 (JDK 13)
我使用JDK 13早期访问版本发布了一个使用Text Blocks的Mapper方法演示应用程序,并将其放在GitHub上公开。
- https://github.com/kazuki43zoo/mybatis-demo-with-jdk13
华丽的版本
我在2016年7月13日发布了一个关于如何在Groovy中创建Mapper接口的方法的帖子(”使用Groovy创建MyBatis的Mapper(提高SQL的可读性!)”)。
- https://github.com/kazuki43zoo/qiita-materials/tree/groovy-with-mybatis-spring-boot-2.0/mybatis-spring-boot/mybatis-demo
Kotlin版本
我在2016年8月3日发布了一个关于如何在Kotlin上使用MyBatis(mybatis-spring-boot-starter)的帖子(”在Kotlin中使用mybatis-spring-boot-starter”)。
- https://github.com/kazuki43zoo/qiita-materials/tree/master/mybatis-spring-boot/mybatis-kotlin-demo
纯正的简易样本
由于MyBatis Spring Boot提供了一些简单的示例,我建议您一并参考。
- https://github.com/mybatis/spring-boot-starter/tree/master/mybatis-spring-boot-samples
创建开发项目
Spring Boot提供了一个名为“SPRING INITIALIZR”的Web服务,用于生成针对Spring Boot的开发项目。本次我们将使用这个Web服务来创建开发项目。
SPRING INITIALIZR支持Maven和Gradle项目,但本文将创建一个Maven项目。在向导中输入以下值并点击“Generate Project”按钮,即可下载Maven项目。
解压缩下载的Zip文件后,可以生成如下的项目目录结构。
.
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── mybatisdemo
│ │ └── MybatisDemoApplication.java
│ └── resources
│ └── application.properties
└── test
└── java
└── com
└── example
└── mybatisdemo
└── MybatisDemoApplicationTests.java
注意:
只要您的环境支持curl和tar命令,您可以执行以下命令来创建开发项目。
$ curl -s https://start.spring.io/starter.tgz \
-d name=mybatis-demo \
-d artifactId=mybatis-demo \
-d dependencies=mybatis,h2 \
-d baseDir=mybatis-demo \
| tar -xzvf –
我将尝试将已创建的开发项目作为Spring Boot应用程序启动。
$ ./mvnw spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< com.example:mybatis-demo >----------------------
[INFO] Building mybatis-demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) > test-compile @ mybatis-demo >>>
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ mybatis-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ mybatis-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ mybatis-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /private/tmp/mybatis-demo/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ mybatis-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) < test-compile @ mybatis-demo <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) @ mybatis-demo ---
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.6.RELEASE)
2019-07-15 17:25:33.712 INFO 55692 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : Starting MybatisDemoApplication on shimizukazutakanoMacBook-puro.local with PID 55692 (/private/tmp/mybatis-demo/target/classes started by shimizukazuki in /private/tmp/mybatis-demo)
2019-07-15 17:25:33.714 INFO 55692 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : No active profile set, falling back to default profiles: default
2019-07-15 17:25:34.049 WARN 55692 --- [ main] o.m.s.mapper.ClassPathMapperScanner : No MyBatis mapper was found in '[com.example.mybatisdemo]' package. Please check your configuration.
2019-07-15 17:25:34.579 INFO 55692 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : Started MybatisDemoApplication in 1.121 seconds (JVM running for 4.19)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.064 s
[INFO] Finished at: 2019-07-15T17:25:34+09:00
[INFO] ------------------------------------------------------------------------
暫時確認沒有錯誤,所以我們繼續往前走吧。
确认mybatis-spring-boot-starter
如果在向导中选择”MyBatis”,则将添加与Spring Boot版本兼容的mybatis-spring-boot-starter作为依赖项。由于本次选择了Spring Boot 2.1.6,因此将添加mybatis-spring-boot-starter 2.1.0。(另外,如果选择了Spring Boot 1.5系列,则将添加mybatis-spring-boot-starter 1.3.4)
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
mybatis-spring-boot-starter 2.1.0将会自动为您添加以下相关项。
$ ./mvnw dependency:tree
...
[INFO] +- org.mybatis.spring.boot:mybatis-spring-boot-starter:jar:2.1.0:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.1.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:2.1.6.RELEASE:compile
[INFO] | | | \- org.springframework:spring-context:jar:5.1.8.RELEASE:compile
[INFO] | | | +- org.springframework:spring-aop:jar:5.1.8.RELEASE:compile
[INFO] | | | \- org.springframework:spring-expression:jar:5.1.8.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.1.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.1.6.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.11.2:compile
[INFO] | | | | \- org.apache.logging.log4j:log4j-api:jar:2.11.2:compile
[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:1.7.26:compile
[INFO] | | +- javax.annotation:javax.annotation-api:jar:1.3.2:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.23:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-jdbc:jar:2.1.6.RELEASE:compile
[INFO] | | +- com.zaxxer:HikariCP:jar:3.2.0:compile
[INFO] | | \- org.springframework:spring-jdbc:jar:5.1.8.RELEASE:compile
[INFO] | | +- org.springframework:spring-beans:jar:5.1.8.RELEASE:compile
[INFO] | | \- org.springframework:spring-tx:jar:5.1.8.RELEASE:compile
[INFO] | +- org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:jar:2.1.0:compile
[INFO] | +- org.mybatis:mybatis:jar:3.5.2:compile
[INFO] | \- org.mybatis:mybatis-spring:jar:2.0.2:compile
...
创建域对象
这次我们将把Todo类作为域对象来创建。
package com.example.mybatisdemo.domain;
public class Todo {
private int id;
private String title;
private String details;
private boolean finished;
// ... setter and getter
}
注意:
您可以使用IDE的功能生成setter和getter,但使用Lombok可以提高开发效率!
创建Mapper接口
我将创建一个提供对Todo进行CRUD操作的Mapper接口。
package com.example.mybatisdemo.mapper;
import com.example.mybatisdemo.domain.Todo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface TodoMapper {
@Insert("INSERT INTO todo (title, details, finished) VALUES (#{title}, #{details}, #{finished})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(Todo todo);
@Select("SELECT id, title, details, finished FROM todo WHERE id = #{id}")
Todo select(int id);
}
注意:
如果希望将SQL写入XML文件中,可以创建如下的Mapper接口和XML文件。src/main/java/com/example/mybatisdemo/mapper/TodoMapper.java
@Mapper
public interface TodoMapper {
void insert(Todo todo);
Todo select(int id);
}src/main/resources/com/example/mybatisdemo/mapper/TodoMapper.xml
INSERT INTO todo (title, details, finished) VALUES (#{title}, #{details}, #{finished})
创建todo表
在H2嵌入式数据库中创建todo表。如果使用Spring Boot的自动配置创建的DataSource,可以将schema.sql和data.sql文件放置在类路径下,Spring Boot启动时将读取这些文件并执行SQL语句。
CREATE TABLE todo (
id IDENTITY
,title TEXT NOT NULL
,details TEXT
,finished BOOLEAN NOT NULL
);
修改MybatisDemoApplication并启动Spring Boot应用程序
修改MybatisDemoApplication,通过Mapper接口访问数据库。
- 修正前
package com.example.mybatisdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MybatisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisDemoApplication.class, args);
}
}
- 修正後
package com.example.mybatisdemo;
import com.example.mybatisdemo.domain.Todo;
import com.example.mybatisdemo.mapper.TodoMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.Transactional;
@SpringBootApplication
public class MybatisDemoApplication implements CommandLineRunner { // CommandLineRunnerを実装する
public static void main(String[] args) {
SpringApplication.run(MybatisDemoApplication.class, args);
}
private final TodoMapper todoMapper;
public MybatisDemoApplication(TodoMapper todoMapper) {
this.todoMapper = todoMapper; // Mapperをインジェクションする
}
// Spring Boot起動時にCommandLineRunner#runメソッドが呼び出される
@Transactional
@Override
public void run(String... args) throws Exception {
Todo newTodo = new Todo();
newTodo.setTitle("飲み会");
newTodo.setDetails("銀座 19:00");
todoMapper.insert(newTodo); // 新しいTodoをインサートする
Todo loadedTodo = todoMapper.select(newTodo.getId()); // インサートしたTodoを取得して標準出力する
System.out.println("ID : " + loadedTodo.getId());
System.out.println("TITLE : " + loadedTodo.getTitle());
System.out.println("DETAILS : " + loadedTodo.getDetails());
System.out.println("FINISHED : " + loadedTodo.isFinished());
}
}
当你修改了MybatisDemoApplication之后,它将作为一个Spring Boot应用程序启动。
$ ./mvnw spring-boot:run
...
[INFO] --- spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) @ mybatis-demo ---
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.6.RELEASE)
2019-07-15 17:35:04.734 INFO 55816 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : Starting MybatisDemoApplication on shimizukazutakanoMacBook-puro.local with PID 55816 (/private/tmp/mybatis-demo/target/classes started by shimizukazuki in /private/tmp/mybatis-demo)
2019-07-15 17:35:04.736 INFO 55816 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : No active profile set, falling back to default profiles: default
2019-07-15 17:35:05.561 INFO 55816 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-07-15 17:35:05.820 INFO 55816 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-07-15 17:35:06.176 INFO 55816 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : Started MybatisDemoApplication in 1.746 seconds (JVM running for 5.696)
ID : 1
TITLE : 飲み会
DETAILS : 銀座 19:00
FINISHED : false
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.341 s
[INFO] Finished at: 2019-07-15T17:35:06+09:00
[INFO] ------------------------------------------------------------------------
2019-07-15 17:35:06.266 INFO 55816 --- [ Thread-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2019-07-15 17:35:06.270 INFO 55816 --- [ Thread-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
...
已输出标准输出中插入的Todo状态。
顺便提一句,开发项目将会处于以下状态。
.
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── mybatisdemo
│ │ ├── MybatisDemoApplication.java // 修正したファイル
│ │ ├── domain
│ │ │ └── Todo.java // 追加したファイル
│ │ └── mapper
│ │ └── TodoMapper.java // 追加したファイル
│ └── resources
│ ├── application.properties
│ ├── com
│ │ └── example
│ │ └── mybatisdemo
│ │ └── mapper
│ │ └── TodoMapper.xml // 追加したファイル(SQLをXMLに記述する場合のみ)
│ └── schema.sql // 追加したファイル
└── test
└── java
└── com
└── example
└── mybatisdemo
└── MybatisDemoApplicationTests.java
在JUnit上运行MybatisDemoApplication。
下载的开发项目中包含了用于JUnit的测试用例类(MybatisDemoApplicationTests)。
$ ./mvnw test
...
2019-07-15 17:36:55.348 INFO 55839 --- [ main] c.e.m.MybatisDemoApplicationTests : Started MybatisDemoApplicationTests in 1.4 seconds (JVM running for 2.08)
ID : 1
TITLE : 飲み会
DETAILS : 銀座 19:00
FINISHED : false
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.125 s - in com.example.mybatisdemo.MybatisDemoApplicationTests
2019-07-15 17:36:55.688 INFO 55839 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2019-07-15 17:36:55.690 INFO 55839 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.304 s
[INFO] Finished at: 2019-07-15T17:36:56+09:00
[INFO] ------------------------------------------------------------------------
测试似乎是成功的,但在下载状态下没有对测试结果进行断言。
既然如此,我将修改MybatisDemoApplicationTests,以便对测试结果进行断言。
package com.example.mybatisdemo;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.test.context.junit4.SpringRunner;
import static org.hamcrest.Matchers.containsString;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisDemoApplicationTests {
@ClassRule
public static OutputCapture out = new OutputCapture(); // System.outの内容をキャプチャする
@Test
public void contextLoads() {
// System.outした内容を検証する
out.expect(containsString("ID : 1"));
out.expect(containsString("TITLE : 飲み会"));
out.expect(containsString("DETAILS : 銀座 19:00"));
out.expect(containsString("FINISHED : false"));
}
}
当再次执行测试时,可以验证标准输出的内容是否正确。
Mapper接口的扫描机制
似乎我可以使用MyBatis来访问数据库,但是我不知道Mapper接口是如何被扫描的?
如果只使用mybatis-spring的功能,需要使用@MapperScan指定要扫描的基础包。但是,如果使用mybatis-spring-boot-starter,该Spring Boot应用程序(MybatisDemoApplication)所在的包(com.example)下的接口将作为Mapper接口进行扫描,并且该接口上还要标有@org.apache.ibatis.annotations.Mapper(这是在mybatis 3.4中添加的注解)。
注意:
在mybatis-spring-boot-starter 1.0系中,Spring Boot应用程序(MybatisDemoApplication)中的所有位于com.example包下的接口都被扫描为Mapper接口。
尽管这个机制很方便,但也存在一个问题,即未经意的接口会被扫描为Mapper接口并注册到DI容器中。@Mapper注解是为了解决这个问题而添加的标记注解。
如果在Mapper接口上未添加@Mapper注释,则可以使用@MapperScan来显式指定要扫描的基础包。
@MapperScan("com.example.mybatisdemo.mapper") // スキャンするベースパッケージを明示的に指定する
@SpringBootApplication
public class MybatisDemoApplication {
// ...
}
使用类型别名。
在将SQL编写为XML时,我认为有时候想要在parameterType和resultType属性中指定一个简单的类名(别名),而不是类的完整类名(FQCN)。
对于这种情况,您只需要在applicaion.properties中添加以下定义即可。
mybatis.type-aliases-package=com.example.mybatisdemo.domain
对于多个基本包,您可以使用逗号进行分隔进行指定。从2.0.1版本开始,支持通配符(例如:com.example.**.domain)进行指定(mybatis-spring2.0.1版本及以上可用,但推荐使用2.0.2版本及以上)。
重要事项:
据报道,使用2.0.1版本的mybatis.type-aliases-package时,将在特定条件下发生错误。有关错误详细信息,请参阅mybatis-spring-boot-starter 2.0的变更内容。2019/7/15 更新:
该问题在2.1.0版本中已经解决。
新增了一个属性,用于指定注册类型别名的类所属的父类。这是对mybatis-spring中原有支持机制的改进,使其可以在Spring Boot的配置属性中进行指定。
mybatis.type-aliases-super-type=com.example.mybatisdemo.TypeAliasTarget
应用类型处理程序
如果您希望添加MyBatis默认未提供的类型处理器,或者覆盖默认应用的类型处理器,您只需在applicaion.properties文件中添加以下定义。
mybatis.type-handlers-package=com.example.mybatisdemo.typehandler
可以使用逗号分隔多个基本包来指定。从2.0.1版本开始,可以使用通配符(例如:com.example.**.typehandler)。可以使用mybatis-spring 2.0.1+版本,但推荐使用2.0.2+版本。
自2.1.0版本起,可以检测并应用已注册到DI容器中的TypeHandler到MyBatis。
@SpringBootApplication
public class MybatisDemoApplication {
@Bean
MyTypeHandler myTypeHandler(){
return new MyTypeHandler();
}
}
如果要将多个TypeHandler应用于MyBatis,只需分别定义每个Bean。
更改SqlSession的执行模式
默认的运行模式是MyBatis配置的设置值(默认为简单模式),但根据应用程序的类型和要求,可能希望更改运行模式。
在这种情况下,只需在applicaion.properties中添加以下定义即可。当然,也可以更改MyBatis配置的设置值。
mybatis.executor-type=BATCH
显式地加载Mapper XML文件
如果不通过Mapper接口直接使用SqlSession(SqlSessionTemplate)来执行SQL,则需要明确地读取定义了SQL的Mapper XML文件。
在这种情况下,只需要向applicaion.properties添加以下定义即可。
mybatis.mapper-locations=classpath*:/mybatis/sqls/**/*.xml
在中国,您还可以使用逗号分隔符指定多个位置。
mybatis.mapper-locations[0]=classpath*:/mybatis/sqls/aaa/**/*.xml
mybatis.mapper-locations[1]=classpath*:/mybatis/sqls/bbb/**/*.xml
可以以这种方式指定。
另外,如果使用yaml文件而不是properties文件,可以
mybatis:
mapper-locations:
- classpath*:/mybatis/sqls/aaa/**/*.xml
- classpath*:/mybatis/sqls/bbb/**/*.xml
也可以表达为这样的句子。
MyBatis的配置
在1.0版本中,如果想要自定义MyBatis的行为,需要在MyBatis配置文件的标签中添加设置。但从1.1版本开始,可以直接在application.properties中进行设置。此外,从1.3版本(在1.2.1版本中也已经添加)开始,增加了ConfigurationCustomizer接口,可以完全自定义MyBatis的配置。
MyBatis配置文件的使用示例
mybatis.config-location=classpath:/mybatis/mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
<setting name="defaultFetchSize" value="100" />
<setting name="defaultStatementTimeout" value="30" />
</settings>
</configuration>
当然,您可以使用除了之外的标记。
顺便提一下,MyBatis还提供了一个检查MyBatis配置文件是否存在的机制,默认情况下是禁用的。如果您希望启用该机制,请添加以下定义。
mybatis.check-config-location=true
application.properties的示例配置值
您也可以只使用application.properties而不使用MyBatis配置文件来进行设置。
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.default-fetch-size=100
mybatis.configuration.default-statement-timeout=30
另外,不可以同时指定mybatis.configuration.xxxx和mybatis.config-location,如果指定会出错。
注意:
在版本1.1.1之前,IDE无法自动完成mybatis.configuration下的属性,但在版本1.2中已解决。(然而…似乎存在相同的属性键重复的问题)补充于2017年1月8日:
输入自动完成时属性键重复的问题是由于maven-bundle-plugin 2.5.4的错误导致的,如果使用maven-bundle-plugin 3.0.0+进行构建,可以解决此问题。我认为这个问题将在mybatis-spring-boot-starter #gh-127中得到解决,并在版本1.2.1中修复。补充于2017年4月10日:
输入自动完成时属性键重复的问题已在版本1.3(也已向版本1.2.1进行了后移)中解决。
使用ConfigurationCustomizer进行自定义的示例
从版本1.3.0(已经进行了1.2.1的回溯)开始,MyBatis添加了一个回调接口(org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer),用于在Java代码中自定义保存MyBatis配置信息的Bean(org.apache.ibatis.session.Configuration的Bean)。如下所示,如果定义一个实现了该接口的类作为Bean,那么configure方法将被调用回调,通过使用Java代码可以完全自定义MyBatis的配置。
@Configuration
public class MyBatisConfiguration {
@Bean
ConfigurationCustomizer mybatisConfigurationCustomizer() {
return (configuration) -> {
// aplication.propertiesで指定・表現できないカスタマイズコードを実装
configuration.getTypeHandlerRegistry().register(RoundingMode.class, EnumOrdinalTypeHandler.class);
};
}
}
应用外部定义的属性值
MyBatis允许将外部定义的属性值嵌入到MyBatis配置文件和Mapper文件中,可以使用MyBatis配置文件中的元素指定外部定义。(虽然也可以直接在程序中指定,但本帖不涉及此处理。)
- http://www.mybatis.org/mybatis-3/ja/configuration.html#properties
此外,如果使用MyBatis-Spring,可以在MyBatis配置文件的元素中加入外部定义,并且可以通过SqlSessionFactoryBean的configurationProperties属性(java.util.Properties类型)指定。从1.2版本开始,可以使用application.properties(或application.yml)来进行指定。
例如,在MyBatis配置文件中,对以下部分进行了定义…
<properties>
<property name="key" value="value"/>
</properties>
可以在application.properties(或application.yml)文件中进行如下修改:
mybatis.configuration-properties.key=value
注意:
实际上,在版本1.1.1中,通过以下定义也可以实现相同的效果。这种方法可以在版本1.2及更高版本中使用,并且如果存在相同的键名,则优先使用在版本1.2中添加的由mybatis.configuration-properties指定的值。src/main/resources/application.properties
mybatis.configuration.variables.key=value例如,可以在mybatis.configuration.variables中定义与环境无关的值(或默认值),然后在每个配置文件中使用mybatis.configuration-properties根据配置文件的环境逐个覆盖值。
应用插件(拦截器)的设置
MyBatis提供了一个机制(拦截器),用于干预MyBatis核心组件的处理过程。
我认为很少会使用这个机制,但如果想要在MyBatis中应用拦截器,只需添加以下类似的Bean定义。
@SpringBootApplication
public class MybatisDemoApplication {
@Bean
Interceptor myInterceptor(){
return new MyInterceptor();
}
// ...
}
如果想要在MyBatis中应用多个Intereptor,只需分别定义相应的Bean即可。
应用DatabaseIdProvider
MyBatis提供了一种用于识别连接中数据库的机制(DatabaseIdProvider)。
我认为并不经常使用这个机制,但如果想要在MyBatis中应用DatabaseIdProvider,只需添加以下类似的Bean定义即可。
@SpringBootApplication
public class MybatisDemoApplication {
@Bean
VendorDatabaseIdProvider vendorDatabaseIdProvider() {
VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties vendorProperties = new Properties();
vendorProperties.put("PostgreSQL", "postgresql");
vendorProperties.put("H2", "h2");
databaseIdProvider.setProperties(vendorProperties);
return databaseIdProvider;
}
// ...
}
应用语言驱动器
MyBatis 提供了一种机制(LanguageDriver)来改变(自定义)语言以构建动态 SQL。
默认情况下,MyBatis 使用内置的基于 XML 的语言(XMLLanguageDriver),但如果希望更改默认行为,开发者只需添加以下类似的 Bean 定义即可使用自己创建的 LanguageDriver 类(2.1 版本以后可用)。
@Bean
MyLanguageDriver myLanguageDriver() {
return MyLanguageDriver();
}
此外,如果从DI容器中检测到的LanguageDriver Bean只有一个,那么检测到的LanguageDriver将被视为默认的LanguageDriver类(如果检测到多个Bean,则默认的XMLLanguageDriver将直接应用)。如果不想通过自动检测功能更改默认的LanguageDriver类,或者想要更改默认的LanguageDriver类以在多个LanguageDriver共存时使用,请使用下面介绍的mybatis.default-scripting-language-driver属性(添加于2.1版本)来明确指定默认的LanguageDriver类。
默认指定要使用的LanguageDriver。
如果要明确指定默认使用的LanguageDriver,则只需进行以下设置。
mybatis.default-scripting-language-driver={デフォルトのLanguageDriverクラスのFQCN}
这个属性可以从2.1版本开始使用。然而,在2.0版本之前,我们支持使用 mybatis.configuration.default-scripting-language 属性(用于直接向MyBatis提供的配置类设置值),作为指定默认的 LanguageDriver 类的方法。但是,由于与后面提到的“LanguageDriver的自动配置”结合使用时可能无法按预期进行操作,所以这个属性在2.1版本中被废弃(禁止使用)。
LanguageDriver的自动配置
自MyBatis的2.1版本开始,以下三个子模块(LanguageDriver)的自动配置机制得到了支持。
-
- mybatis-velocity
-
- mybatis-freemarker
- mybatis-thymeleaf
当在类路径上添加上述三个子模块的jar文件时,会自动将提供子模块的LanguageDriver类的Bean注册到DI容器中,并应用到MyBatis中。值得注意的是,如果开发者明确地在上述子模块中提供LanguageDriver类的Bean定义,则不会进行自动配置。
警告:
如果满足以下条件,将丧失向下兼容性,需要额外的措施。
1. mybatis-velocity、mybatis-freemarker和mybatis-thymeleaf这三个子模块的jar文件只有一个在类路径上。
2. 默认的LanguageDriver不是由子模块提供的LanguageDriver。如果满足以上条件,因为采用了子模块提供的LanguageDriver类作为默认的LanguageDriver设置机制,所以需要明确指定默认的LanguageDriver,如下所示:
mybatis.default-scripting-language-driver={默认的LanguageDriver类的FQCN}
使用 MyBatis Velocity
当你添加以下的工具后,它会将org.mybatis.scripting.velocity.VelocityLanguageDriver(如果指定mybatis-velocity 2.0之前的版本,则使用org.mybatis.scripting.velocity.Driver)应用到MyBatis中。
<!-- Mybatis Velocityを利用する場合 -->
<dependency>
<groupId>org.mybatis.scripting</groupId>
<artifactId>mybatis-velocity</artifactId>
<version>2.1.0</version>
</dependency>
如果您使用的是MyBatis-velocity 2.1.0或更高版本,您可以利用配置属性机制来自定义MyBatis Velocity和Velocity模板引擎在MyBatis Velocity中的操作方式。
#
# mybatis.scripting-language-driver.velocity.{key} = {value} 形式
#
# Velocity本体の動作をカスタマイズするプロパティ
# mybatis.scripting-language-driver.velocity.velocity-settings.{name} = {value}形式
# {name}に設定可能なプロパティはリファレンスドキュメントを参照してください。
mybatis.scripting-language-driver.velocity.velocity-settings.runtime.custom_directives = com.example.directives.MyDirective
# テンプレートエンジンに渡す追加属性(オブジェクト)を指定するプロパティ
# mybatis.scripting-language-driver.velocity.additional-context-attributes.{name} = {value}
mybatis.scripting-language-driver.velocity.additional-context-attributes.likeEscape = com.example.helpers.LikeEscape
使用 MyBatis FreeMarker
在添加以下的工件后,将org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver应用到MyBatis中。
<dependency>
<groupId>org.mybatis.scripting</groupId>
<artifactId>mybatis-freemarker</artifactId>
<version>1.2.0</version>
</dependency>
如果要使用mybatis-velocity 1.2.0版本或更高版本,可以利用配置属性机制来自定义MyBatis FreeMarker和MyBatis Velocity中所使用的模板引擎FreeMarker的行为。
#
# mybatis.scripting-language-driver.freemarker.{key} = {value} 形式
#
# FreeMarker本体の動作をカスタマイズするプロパティ
# mybatis.scripting-language-driver.freemarker.freemarker-settings.{name} = {value}形式
# {name}に設定可能なプロパティはリファレンスドキュメントを参照してください。
mybatis.scripting-language-driver.freemarker.freemarker-settings.interpolation_syntax = dollar
# MyBatis FreeMarkerの動作をカスタマイズするプロパティ
# mybatis.scripting-language-driver.freemarker.{name} = {value}
# {name}に設定可能なプロパティはリファレンスドキュメントを参照してください。
mybatis.scripting-language-driver.freemarker.template-file.base-dir = sql
使用MyBatis和Thymeleaf
添加以下工件后,将org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver应用于MyBatis。
<dependency>
<groupId>org.mybatis.scripting</groupId>
<artifactId>mybatis-thymeleaf</artifactId>
<version>1.0.1</version>
</dependency>
您可以利用配置属性的机制来自定义Thymeleaf模板引擎的行为,包括在MyBatis Thymeleaf和Thymeleaf中使用。
#
# mybatis.scripting-language-driver.thymeleaf.{key} = {value} 形式
#
# MyBatis Thymeleafの動作をカスタマイズするプロパティ
# mybatis.scripting-language-driver.thymeleaf.{name} = {value}
# {name}に設定可能なプロパティはリファレンスドキュメントを参照してください。
mybatis.scripting-language-driver.thymeleaf.use2way = false
mybatis.scripting-language-driver.thymeleaf.template-file.cache-enabled = false
mybatis.scripting-language-driver.thymeleaf.dialect.like-additional-escape-target-chars = %, _
映射器的延迟初始化控制
从 2.1 版本开始,可以控制在 DI 容器中注册的 Mapper 的初始化时机(在 DI 容器初始化时、注入或使用时)。默认行为是在 DI 容器初始化时初始化所有的 Mapper。
Spring Boot 2.2(在发布时尚未正式发布)中支持Bean的延迟初始化机制,但存在特定条件下调用Mapper方法时会发生错误。为了使用户能够在控制上对Mapper应用延迟初始化,我们添加了这个机制。如果只是为了避免错误,可以在对Mapper进行延迟初始化时取消条件限制。但在开发(测试)阶段,我认为延迟初始化机制是有效的,因此我们独立于MyBatis(mybatis-spring & mybatis-spring-boot-starter)实现了对Mapper的延迟初始化控制。详细信息请参阅“mybatis-spring-boot 2.1.0中新增的mybatis.lazy-initialization”。
注意:
从版本2.1开始,提供了延迟初始化机制,它是MyBatis(mybatis-spring & mybatis-spring-boot-starter)独有的机制,所以在Spring Boot 2.1系列中也可以使用。
使用 @MybatisTest(mybatis-spring-boot-starter-test)
从版本1.3开始,添加了@MybatisTest注解来支持在测试MyBatis功能时需要的Bean定义。这是Spring Boot提供的@DataJpaTest和@JdbcTest的MyBatis版本(当然,用法与Spring Boot提供的注解相同)。
随着这个改动,新建了mybatis-spring-boot-starter-test来解决测试MyBatis功能所需的库问题。因此,如果要对MyBatis提供的功能进行测试,请在pom.xml中添加以下定义。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>
例如,如果要对以下类似的Mapper接口进行测试:
package com.example.mybatisdemo.mapper;
import com.example.mybatisdemo.domain.Todo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface TodoMapper {
@Insert("INSERT INTO todo (title, details, finished) VALUES (#{title}, #{details}, #{finished})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(Todo todo);
@Select("SELECT id, title, details, finished FROM todo WHERE id = #{id}")
Todo select(int id);
}
只需使用@MybatisTest,就可以创建以下类型的测试用例类。
package com.example.mybatisdemo.mapper;
import com.example.mybatisdemo.domain.Todo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mybatis.spring.boot.test.autoconfigure.MybatisTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@MybatisTest // (1)
public class TodoMapperTests {
@Autowired
private TodoMapper todoMapper; // (2)
@Autowired
private NamedParameterJdbcOperations jdbcOperations; // (3)
@Test
public void insert() {
// setup
// none
// perform test and assertions
{
// (4)
Todo newTodo = new Todo();
newTodo.setTitle("飲み会");
newTodo.setDetails("銀座 19:00");
todoMapper.insert(newTodo);
// (5)
Todo actualTodo =
jdbcOperations.queryForObject("SELECT * FROM todo WHERE id = :id",
new MapSqlParameterSource("id", newTodo.getId()),
new BeanPropertyRowMapper<>(Todo.class));
assertThat(actualTodo.getId()).isEqualTo(newTodo.getId());
assertThat(actualTodo.getTitle()).isEqualTo("飲み会");
assertThat(actualTodo.getDetails()).isEqualTo("銀座 19:00");
assertThat(actualTodo.isFinished()).isEqualTo(false);
}
}
@Test
public void select() {
// (6)
// setup
Todo newTodo = new Todo();
newTodo.setTitle("飲み会");
newTodo.setDetails("銀座 19:00");
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcOperations.update(
"INSERT INTO todo (title, details, finished) VALUES(:title, :details, :finished)",
new BeanPropertySqlParameterSource(newTodo), keyHolder);
// perform test and assertions
{
// (7)
Todo actualTodo = todoMapper.select(keyHolder.getKey().intValue());
assertThat(actualTodo.getId()).isEqualTo(keyHolder.getKey().intValue());
assertThat(actualTodo.getTitle()).isEqualTo("飲み会");
assertThat(actualTodo.getDetails()).isEqualTo("銀座 19:00");
assertThat(actualTodo.isFinished()).isEqualTo(false);
}
}
}
@MybatisTest
を付与する。このアノテーションを付与することで、MyBatisを動かすために必要になるAutoConfigureクラスだけが有効になります。デフォルトの動作ではコンポーネントスキャンが無効化されているので、テスト時に必要ないBeanが無題にDIコンテナに登録されることを防ぐことができます。(2)テスト対象のMapperをインジェクションする。@MybatisTest
を付与すると、MyBatis提供のAutoConfigureによってMapperのBeanが生成されます。(3)登録データの検証およびテストデータを登録するために、NamedParameterJdbcTemplate
のBeanをインジェクションする。@MybatisTest
を付与すると、JdbcTemplate
とNamedParameterJdbcTemplate
のBeanがDIコンテナに登録されます。(4)テスト対象のinsert
メソッドを呼び出す。(5)insert
メソッドの呼び出し結果を検証する。ここでは、NamedParameterJdbcOperations
のメソッドを介して登録したデータを取得し、登録したデータの妥当性を検証しています。(6)select
メソッドのテストを行うためにテストデータを登録する。ここでは、NamedParameterJdbcOperations
のメソッドを介してテストデータを登録しています。なお、Spring Framework提供の@Sql
アノテーションなどを使用してテストデータを登録する方法もあります。(7)テスト対象のselect
メソッドを呼び出す。(8)select
メソッドの呼び出し結果を検証する。在这种情况下进行测试时,由于存在于上级包中的Spring Boot应用程序类(标有@SpringBootApplication注释的类)中定义的Bean定义也会被加载,所以可能会注册与MyBatis测试无关的Bean到DI容器中。
如果不想将与MyBatis测试无关的Bean注册到DI容器中,请在测试用例类相同的包中创建一个空的Spring Boot应用程序类。通过这样做,可以禁用上级包中的Spring Boot应用程序类。
package com.example.mybatisdemo.mapper;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MapperTestApplication {
}
请查看官方参考文档以及Spring Boot提供的@DataJpaTest和@JdbcTest的文档,获取更多详细信息。
总结
这次介绍了在使用Spring Boot运行MyBatis时非常便利的mybatis-spring-boot-starter。如果使用MyBatis的默认行为没有问题,就不需要进行任何特别的配置。此外,从版本1.3开始加入了支持测试的新功能(@MybatisTest),可以在进行Mapper和DAO的测试时高效地进行Bean定义(在各种意义上)。
参考网站
-
- https://github.com/mybatis/spring-boot-starter
-
- http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/
-
- http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-test-autoconfigure/
-
- http://www.mybatis.org/mybatis-3/ja/
-
- http://terasolunaorg.github.io/guideline/5.5.1.RELEASE/ja/ArchitectureInDetail/DataAccessDetail/DataAccessMyBatis3.html
-
- http://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-jpa-test
- http://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-jdbc-test