当前日期: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(); } }
参考: