不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2023-11-19 15:46:21  修改时间:2024-05-07 19:27:09  分类:Java框架/系统  编辑

当前日期:2023-11-19 

软件版本信息:

  1. Spring Boot:2.7.17
  2. MyBatis-Flex:1.7.3
  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 示例,参考如下:

  1. 【Spring Boot】MyBatis引入 分页插件pagehelper,及操作示例
  2. 【MyBatis】传递多个参数的5种方式
  3. 【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();
    }
}

 

 

参考:

  1. 与 PageHelper 集成出现错误
  2. 和pagehelper冲突,但是我需要对mapper.xml中的结果进行分页有什么方法吗?以及是否有对Map结构自动转小驼峰的方法或配资呢?