当前日期:2023-11-19
软件版本信息:
- Spring Boot:
2.7.17- MyBatis-Flex:
1.7.3- pagehelper:
6.0.0
配置目标:
让Spring Boot集成 MyBatis-Flex,同时支持 MyBatis 原生写法,包括分页查询
一、Spring Boot 集成 MyBatis-Flex
1.配置 pom.xml
(1) 配置节点<properties>
<mybatis-flex.version>1.7.3</mybatis-flex.version>
(2) 配件节点<dependencies>
<!--++++++++++++++++++++++++++++++++++++++++++++++++++++-->
<!-- MyBatis-Flex -->
<!--++++++++++++++++++++++++++++++++++++++++++++++++++++-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>${mybatis-flex.version}</version>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>${mybatis-flex.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-codegen</artifactId>
<version>${mybatis-flex.version}</version>
</dependency>
2.配置 application.yml
mybatis-flex:
datasource:
ds1:
url: jdbc:mysql://106.52.xx.xxx:3306/albert_test?useUnicode=true&useSSL=false&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&serverTimezone=Asia/Shanghai&useInformationSchema=true
username: albert_user_test
password: CX0uw@76zzTgwYzv
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 2
maximum-pool-size: 10
auto-commit: true
connection-test-query: select 1
3.配置启动类
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描Mapper接口文件夹:
@SpringBootApplication
@MapperScan("com.wanma.apps.mapper")
public class MybatisFlexTestApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisFlexTestApplication.class, args);
}
}
4.配置打印SQL日志
package com.wanma.framework_api.config;
import com.mybatisflex.core.audit.AuditManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisFlexConfig {
private static final Logger logger = LoggerFactory
.getLogger("mybatis-flex-sql");
public MyBatisFlexConfig() {
// 开启审计功能
AuditManager.setAuditEnable(true);
// 打印SQL字符串
// 设置SQL审计收集器
AuditManager.setMessageCollector(auditMessage -> {
logger.info("【执行SQL】耗时: {}ms, SQL语句:\n{}", auditMessage.getElapsedTime(), auditMessage.getFullSql());
}
);
}
}
5.通过代码生成器mybatis-flex-codegen,批量生成 实体类、mapper文件、服务类等项目文件
package com.wanma.framework_api.tool;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.codegen.Generator;
import com.mybatisflex.codegen.config.GlobalConfig;
import com.zaxxer.hikari.HikariDataSource;
/**
* 模型生产器
*/
public class MyBatisFlexCodegenTool {
/**
* 主方法
*/
public static void main(String[] args) {
// 配置数据源
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://106.52.xx.yy:53306/albert_elasticsearch?useUnicode=true&useSSL=false&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai");
dataSource.setUsername("user_albert_elasticsearch");
dataSource.setPassword("tQcZgpt4VKm7LUk1");
// 创建配置内容,两种风格都可以。
GlobalConfig globalConfig = createGlobalConfigUseStyle1();
// 通过 datasource 和 globalConfig 创建代码生成器
Generator generator = new Generator(dataSource, globalConfig);
// 生成代码
generator.generate();
}
public static GlobalConfig createGlobalConfigUseStyle1() {
// 创建配置内容
GlobalConfig globalConfig = new GlobalConfig();
String author = "Albert";
String outDir = "d:/123";
// String outDir = null; // 如果为空,则在当前项目java目录下
String packageName = "com.wanma.apps"; // 基础包名
// 设置只生成哪些表,setGenerateTable 未配置时,生成所有表
globalConfig.setGenerateTable(
"sys_user",
"article",
"article_category",
"article_content"
);
String packagePath = null;
String xmlPath = null;
if (StrUtil.isNotEmpty(outDir)) {
packagePath = outDir + "/java";
xmlPath = outDir + "/resources/mapper/apps";
}
// 设置作者
globalConfig.setAuthor(author);
// 设置生成路径、基础包名
globalConfig.getPackageConfig()
.setSourceDir(packagePath)
.setMapperXmlPath(xmlPath)
.setBasePackage(packageName);
// 设置逻辑假删除字段
globalConfig.setLogicDeleteColumn("is_del");
// 设置生成 Entity 实体类,并启用 Lombok
globalConfig.enableEntity()
.setWithLombok(true)
.setWithSwagger(true)
.setOverwriteEnable(true);
// 设置生成 Mapper 映射类
globalConfig.enableMapper();
// 设置生成 Mapper XML 文件
globalConfig.enableMapperXml();
// 设置生成 Service 服务类
globalConfig.enableService().setClassPrefix("I");
// 设置生成 ServiceImpl 服务类
globalConfig.enableServiceImpl();
return globalConfig;
}
}
6.添加测试类,进行功能测试
package com.wanma;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.annotation.UseDataSource;
import com.wanma.apps.entity.User;
import com.wanma.apps.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class RunTest {
@Autowired
UserMapper userMapper;
@UseDataSource("ds1")
@Test
void test_01() {
// User user = userMapper.selectOneById(1);
// System.out.println(user);
User user = new User();
user.setUsername("张三2");
user.setPassword("1234567");
user.setAddTime(LocalDateTimeUtil.now());
userMapper.insert(user);
}
}
二、Spring Boot 集成 MyBatis 分页插件 pagehelper
有些比较复杂的SQL,使用MyBatis原生操作,貌似更简单。
需要注意的是:MyBatis-Flex 作为 MyBatis 的增强框架进行代码开发,并不会影响原有的 MyBatis 的任何功能。
即原来是怎么使用MyBatis的,现在还是原方式地使用!!!
1.配置pom.xml
(1) 配置节点<properties>
<mybatis-pagehelper.version>6.0.0</mybatis-pagehelper.version>
(2) 配件节点<dependencies>
<!--++++++++++++++++++++++++++++++++++++++++++++++++++++-->
<!-- MyBatis分页插件 -->
<!--++++++++++++++++++++++++++++++++++++++++++++++++++++-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${mybatis-pagehelper.version}</version>
</dependency>
2.配置 application.yml
# MyBatis分页插件 pagehelper: helperDialect: mysql reasonable: true supportMethodsArguments: true params: count=countSql
3.配置拦截器
package com.wanma.framework_api.config;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.github.pagehelper.PageInterceptor;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.io.Serial;
import java.util.TimeZone;
@Configuration
public class ApplicationConfig {
// 使用自动配置无效,
// 需要手动注入bean,以让
// com.mybatisflex.spring.boot.MybatisFlexAutoConfiguration#MybatisFlexAutoConfiguration
// 正常获取到com.github.pagehelper.PageInterceptor
@Bean
public PageInterceptor pageInterceptor() {
return new PageInterceptor();
}
}
4.Spring Boot 使用 MyBatis 示例,参考如下:
5.小优化:下划线转小驼峰
背景:当返回的对象是
Map对象时, 其key为数据库字段名,通常是 下划线 命名方式,可以自定义一个拦截器,使其自动由下划线转小驼峰。
(1) 新建 自定义拦截器类
package com.wanma.framework_api.interceptor;
import com.github.pagehelper.PageInterceptor;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
public class MyPageInterceptor extends PageInterceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
var rs = super.intercept(invocation);
if (rs instanceof List<?> rsList && rsList.size() >= 1 && rsList.get(0) instanceof Map) {
var rsListMap = (List<Map>) rsList;
var sb = new StringBuilder();
for (int i = 0; i < rsList.size(); i++) {
var map = (Map) rsList.get(i);
Map mapNew = new HashMap((int) Math.ceil(map.size() / 0.75));
for (Object o : map.entrySet()) {
var e = (Map.Entry) o;
mapNew.put(underlineToCamel((String) e.getKey(), sb), e.getValue());
}
rsListMap.set(i, mapNew);
}
}
return rs;
}
/**
* 下划线转小驼峰
*/
public static String underlineToCamel(String key, StringBuilder sb) {
if (StringUtils.isEmpty(key)) {
return "";
}
int len = key.length();
if (sb == null) {
sb = new StringBuilder(len);
} else {
sb.setLength(0);
}
for (int i = 0; i < len; i++) {
char c = Character.toLowerCase(key.charAt(i));
if (c == '_') {
if (++i < len) {
sb.append(Character.toUpperCase(key.charAt(i)));
}
} else {
sb.append(c);
}
}
return sb.toString();
}
}
(2) 重新配置拦截器
package com.wanma.framework_api.config;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.github.pagehelper.PageInterceptor;
import com.wanma.framework_api.interceptor.MyPageInterceptor;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.io.Serial;
import java.util.TimeZone;
@Configuration
public class ApplicationConfig {
// 使用自动配置无效,
// 需要手动注入bean,以让
// com.mybatisflex.spring.boot.MybatisFlexAutoConfiguration#MybatisFlexAutoConfiguration
// 正常获取到com.github.pagehelper.PageInterceptor
@Bean
public PageInterceptor pageInterceptor() {
// return new PageInterceptor();
// ------------------------------
// 转小驼峰用
return new MyPageInterceptor();
}
}
参考: