MyBatis 为一个优秀的 ORM 框架,而MyBatis Plus 作为作为它的一个增强工具,为了简化开发、提升效率。我们一起看看如何整合 MyBatis Plus 到我们的 Spring-boot 项目中。
一、Spring-boot 整合MyBatis Plus 步骤
1. 准备
准备阶段主要是学习和了解 MyBatis 及 MyBatis Plus 是什么、做什么用。可以直接看官方文档。
Mybatis
Mybatis 官网:https://mybatis.org/mybatis-3/zh/index.html
Mybatis Generator 官网:https://mybatis.org/generator/configreference/table.html
Mybatis 学习资料:http://www.mybatis.cn/archives/706.html
Mybatis 本身是有代码生成器,再配合扩展自定义 Mapper.xml,还有一些其他如分页插件之类是完完全可以满足各种特殊场景。扩展 Mapper 文件方法可以参考:https://blog.csdn.net/ytzzh0726/article/details/84701786
但是对于一些特殊场景想要更加方便些,可以使用 Mybatis plus。
Mybatis plus
Mybatis plus 官方文档:https://baomidou.com/pages/24112f/
Mybatis plus 通过代码生成器可以自动生成代码中的 controller 、service、mapper及xml。同时 service 和 mapper 继承了 baseservice、 basemapper类对于增删改查这些基本操作方法不需要开发直接可以使用。
2. 添加依赖
添加 Mybatis plus 的依赖 需要添加 mybatis-plus-boot-starter 和 mybatis-plus-generator 以及Generator 对应所选择的代码模板的框架依赖。
非常重要的避坑提示:
1.代码生成器介绍中Generator 和 Mybatis plus 用相同版本号,所以后面代码生成器有新、旧两种,选择时直接根据自己选定的依赖的版本决定。
2.mybatis-spring-boot-starter相关的 Mybatis 用于自动装配starter 确保依赖中没有。它的功能和mybatis-plus-boot-starter是一样的,所以会冲突,否则后面问题一大堆。如果有间接依赖,直接排除。
3.mybatis-plus-boot-starter本身会依赖一个 Mybatis 版本,可以直接查看 mybatis-plus-boot-starter 的 pom 确定依赖版本。确保当前环境引入版本与 mybatis-plus-boot-starter 依赖的一致,否则会有很多问题。常见出现问题情况有:
1)当前 pom 显示定义依赖了 Mybatis 其他版本,导致冲突。
2)当前项目的父 pom 中有指定 Mybatis 版本,这种很不容易察觉,会以为mybatis-plus-boot-starter本身依赖的就是父 pom 中版本。如果是父 pom 指定的不一致,当前 pom中要明确指定。
3)mybatis-plus-boot-starter依赖的 Mybatis 不止mybatis还有mybatis-spring,所以要确保 mybatis-spring 的版本依赖也是和 Mybatis 一致否则一样会有问题。如下:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
<scope>compile</scope>
</dependency>
3. 配置数据源和 MyBatis 配置
最简单的 Spring-boot 自动装配,只需要在 properties 文件中配置数据源和配置就行,参考官方:
https://baomidou.com/pages/3b5af0/#spring-boot-%E5%B7%A5%E7%A8%8B
对于数据源 datasource、 SqlSessionFactory需要自定义的,可参考上面链接 spring 部分类似,如果使用 spring-boot 注解可参考下面:
重要提示:
- MybatisSqlSessionFactoryBean被是 MyBatis plus 复制的 MyBatis 的SqlSessionFactoryBean修改的,配置时要用MyBatis plus 的否则,会发现运行时有问题。
- MybatisSqlSessionFactoryBean可以如果需要其他特性,比如分页插件等,可以设置 plugin 等。这些配置都可以参考源代码: com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration#sqlSessionFactory
Spring-boot 注解自定义数据源、SqlSessionFactory 示例:
@Configuration
@EnableTransactionManagement
@MapperScan("com.xxx.xxx.xxx.dao.mapper")
public class DBConfig {
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource")
@Primary
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
//如需要,可以定义其他特性
return factoryBean.getObject();
}
}
// 如需自定义插件,在SqlSessionFactory中设置factoryBean.setPlugins(..);
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
DataDeleteRecorderInnerInterceptor deleteRecorderInterceptor = new DataDeleteRecorderInnerInterceptor();
interceptor.addInnerInterceptor(deleteRecorderInterceptor);
return interceptor;
}
4. 配置代码生成器并生成代码
代码生成器可以生成controoler、service、mapper、entity、mapper.xml 这几类代码文件及对应包,也可以选择某一部分不生成,具体参考下面样例代码。
提示:
- 代码生成器版本选择,保持和 MyBatis plus 的 starter 版本一致,再据此选择新、旧代码生成器。
- 以下生成器代码中会指定代码模板引擎,需要把模板引擎依赖加到环境。如:.templateEngine(new FreemarkerTemplateEngine())则依赖Freemarker。
- 代码生成器旧,样例代码已经过时,后期也不建议使用。如果要使用,单独找对应生成器代码。
- 代码生成器新,有句话“如果是新项目或者不支持的数据库类型可以使用元数据查询的方式来进行生成”,所以直接使用元数据查询就行。特殊老旧项目升级的,可以自行选择。SQLQuery (SQL查询) 以及被标记为“后期不再维护”。
代码生成器(新)使用代码模板
在网上找的直接拷贝使用即可,注释说明很详细。
public class MyBatisPlusGenerator {
public static void main(String[] args) {
//手动配置数据源
String url="jdbc:mysql://localhost:3306/test"; //注意修改数据库名
String name="test";
String password="test";
//数据库表的设置
List<String> listTable = Arrays.asList(
"t_user",
"t_order"; // 设置需要自动代码生成的表名
List<String> listTableSuffix = Arrays.asList(""); //设置 过滤 表的后缀,符合的会自动生成
List<String> listTablePrefix = Arrays.asList(""); //设置 过滤 表的前缀,符合的会自动生成
//基本信息
String author = "mybatis plus generator"; //作者
String parent = "com.abc.efg"; //父包名
String module = "dao"; //模块包名 子包名, 配置后会把代码生成到com.abc.efg.dao包下
//已封装好,无需更改。可按照需求进行注释
//1、配置数据源
FastAutoGenerator.create(url, name, password)
//2、全局配置
.globalConfig(builder -> {
builder.author(author) // 设置作者名
.outputDir(System.getProperty("user.dir") + "/src/main/java") //设置输出路径:项目的 java 目录下【System.getProperty("user.dir")意思是获取到项目所在的绝对路径】
//.commentDate("yyyy-MM-dd") //注释日期
.dateType(DateType.ONLY_DATE) //定义生成的实体类中日期的类型 TIME_PACK=LocalDateTime;ONLY_DATE=Date;
//.fileOverride() //覆盖之前的文件 覆盖已有文件(已迁移到策略配置中,3.5.4版本会删除此方法)
//.enableSwagger() //开启 swagger 模式
.disableOpenDir(); //禁止打开输出目录,默认打开
})
// 建议: 如果是已知数据库(无版本兼容问题下)请继续按照原有的SQL查询方式继续使用(见如下代码),如果是 新项目 或者不支持的数据库类型可以使用元数据查询的方式来进行生成.
// 元数据查询的方式: tinyint 默认转换为 Byte,如需转为 Integer需要配置. dbQuery 后期不再维护.
// MYSQL 示例 切换至SQL查询方式,需要指定好dbQuery与typeConvert来生成.
//.dataSourceConfig(builder -> builder.databaseQueryClass(SQLQuery.class).typeConvert(new MySqlTypeConvert()).dbQuery(new MySqlQuery()))
.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.TINYINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
}))
//3、包配置
.packageConfig(builder -> {
builder.parent(parent) // 设置父包名
.moduleName(module) //设置模块包名
.entity("entity") //pojo 实体类包名
.service("service") //Service 包名
.serviceImpl("service.impl") // ***ServiceImpl 包名
.mapper("mapper") //Mapper 包名
.xml("mapper.xml") //Mapper XML 包名
.controller("controller") //Controller 包名 不生成 controller 则不填
.pathInfo(Collections.singletonMap(OutputFile.mapper.xml, System.getProperty("user.dir")+"/src/main/resources/mapper")); //配置 mapper.xml 路径信息:项目的 resources 目录下
})
//4、策略配置
.strategyConfig(builder -> {
builder
.enableCapitalMode() //开启大写命名
.enableSkipView() //创建实体类的时候跳过视图
.addInclude(listTable) // 设置需要生成的数据表名
.addTableSuffix(listTableSuffix) //设置 过滤 表的后缀
.addTablePrefix(listTablePrefix) // 设置 过滤 表的前缀
//4.1、实体类策略配置
.entityBuilder()
.enableChainModel() //开启链式模型
.formatFileName("%sEntity")//格式实体类名 userEntity
.enableFileOverride() //开启 覆盖原有文件
//.disableSerialVersionUID() //默认是开启实体类序列化,可以手动disable使它不序列化。由于项目中需要使用序列化就按照默认开启了
//.superClass()//写一个父类支持 序列化 Cloneable
.enableTableFieldAnnotation() // 开启生成实体时生成字段注解
.enableLombok() //开启 Lombok
// .versionColumnName("version") //乐观锁字段名(数据库)
// .versionPropertyName("version") //乐观锁属性名(实体)
// .logicDeleteColumnName("deleted") //逻辑删除字段名(数据库)
// .logicDeletePropertyName("deleteFlag") //逻辑删除属性名(实体)
.naming(NamingStrategy.underline_to_camel) //数据库表映射到实体的命名策略:默认是下划线转驼峰命。这里可以不设置
.columnNaming(NamingStrategy.underline_to_camel) //数据库表字段映射到实体的命名策略:下划线转驼峰命。(默认是和naming一致,所以也可以不设置)
.addTableFills(
new Column("create_time", FieldFill.INSERT),
new Column("modify_time", FieldFill.INSERT_UPDATE)
) //添加表字段填充,"create_time"字段自动填充为插入时间,"modify_time"字段自动填充为插入修改时间
.idType(IdType.AUTO) //设置主键自增
//4.2、Controller策略配置
.controllerBuilder()
.enableHyphenStyle() //开启驼峰连转字符
.formatFileName("%sController") //格式化 Controller 类文件名称,%s进行匹配表名,如 UserController
.enableRestStyle() //开启生成 @RestController 控制器
//4.3、service 策略配置
.serviceBuilder()
.formatServiceFileName("%sDao") //格式化 service 接口文件名称,%s进行匹配表名,如 UserService
.formatServiceImplFileName("%sDaoImpl") //格式化 service 实现类文件名称,%s进行匹配表名,如 UserServiceImpl
//4.4、Mapper策略配置
.mapperBuilder()
.enableFileOverride() //开启覆盖原有文件 有自定义 SQL 时,覆盖后记得对比文件还原自定义 SQL
.superClass(BaseMapper.class) //设置父类
.enableBaseResultMap() //启用 BaseResultMap 生成
.enableBaseColumnList() //启用 BaseColumnList
//.enableMapperAnnotation() //开启 @Mapper 注解 deprecated 有@MapperScan 就行
.formatXmlFileName("%sMapper") //格式化Xml文件名称
.formatMapperFileName("%sMapper"); //格式化Mapper文件名称
})
//5、模板
//.templateEngine(new VelocityTemplateEngine())
/*
模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker(以下两个引擎用哪个就保留哪个)
.templateEngine(new BeetlTemplateEngine())
.templateEngine(new FreemarkerTemplateEngine())
*/
.templateEngine(new FreemarkerTemplateEngine()) //本人选择了Freemarker
.templateConfig(builder -> builder.controller(""))// 清空 controller 模版,则不生成 controller
//6、执行
.execute();
}
5. 使用代码
对于基本数据库操作,直接依赖 service 就可以调用。对于自定义非基本操作,可以使用条件构造器 Wrapper,如果太复杂查询依然不满足,则使用 MyBatis 原生注解 SQL 方式或者在 mapper.xml文件中定义 SQL 查询。
条件构造器 Wrapper 的介绍:
基类:AbstractWrapper
常使用 Wrapper 一般都是AbstractWrapper子类:
- QueryWrapper 用于构造查询条件,也就是 select 语句中的条件,但是字段名称都是字符串指定,特定情况下使用。可以直接 new 获取。
- LambdaQueryWrapper 也用于构造查询条件,但字段名可以通过 lambda 指定,比较方便。可以直接 new 获取,也可以再QueryWrapper对象中通过lambda()方法获得直接使用。
- UpdateWrapper 用于构造 更新语句的条件。
- Wrappers 工具类,用于 new 其他 Wrapper 的对象。
官方条件构造器参考:https://baomidou.com/pages/10c804/
条件构造器使用示例:
public UserEntity getUserByStatus(Integer type, long userId, int status){
LambdaQueryWrapper<UserEntity> lambdaQuery = Wrappers.lambdaQuery();
lambdaQuery
.eq(UserEntity::getUserId, userId)
.eq(UserEntity::getStatus, status)
.eq(UserEntity::getType, type);
List<UserEntity> userList = list(lambdaQuery);
if(userList!=null && userList.size()>0) {
return userList.get(0);
}
return null;
}
二、常见问题
- MyBatis 依赖冲突
参考:https://blog.csdn.net/HO1_K/article/details/124427577
mybatis还有mybatis-spring 都确认是否为 MyBatis 依赖的版本。注意检查父 POM 是否有指定。 - 自定义 SqlSessionFactory
使用MybatisSqlSessionFactoryBean ,可以参考前面集成说明。 - 开启 MyBatis SQL 打印
直接参考官网:https://mybatis.org/mybatis-3/zh/logging.html