SpringBoot整合MyBatis-Plus、新版-代码生成器、分页

706 阅读3分钟

一、添加依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.7</version>
    </dependency>

    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.16</version>
    </dependency>

    <!-- swagger -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
        <exclusions>
            <exclusion>
                <artifactId>swagger-models</artifactId>
                <groupId>io.swagger</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- 解决NumberFormatException: For input string: "" 报错 -->
    <dependency>
        <groupId>io.swagger</groupId>
        <artifactId>swagger-models</artifactId>
        <version>1.5.21</version>
        <exclusions>
            <exclusion>
                <artifactId>swagger-annotations</artifactId>
                <groupId>io.swagger</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- swagger-ui.html模式 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>

    <!-- mybatis plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.0</version>
    </dependency>

    <!-- mybatis plus 代码生成器 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.1</version>
    </dependency>
    <!-- 模板引擎 依赖 -->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.3</version>
    </dependency>

    <!--pagehelper分页插件 -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.2.5</version>
        <exclusions>
            <exclusion>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
            </exclusion>
            <exclusion>
                <artifactId>jsqlparser</artifactId>
                <groupId>com.github.jsqlparser</groupId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

<build>
    <!-- install后的jar包名称 -->
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.2.6.RELEASE</version>
            <configuration>
                <fork>true</fork>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <configuration>
                <delimiters>
                    <delimiter>@</delimiter>
                </delimiters>
                <useDefaultDelimiters>false</useDefaultDelimiters>
            </configuration>
        </plugin>
    </plugins>

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

二、配置yml、添加注解

server:
  port: 9000

spring:
  application:
    name: spring-security
  datasource:
    url: jdbc:mysql://xxx.sql.tencentcdb.com:63948/shr_common?serverTimezone=UTC&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: dev
    password: xxx

mybatis-plus:
  mapper-locations: classpath:mapper/**/*.xml
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true
    lazy-loading-enabled: true
    multiple-result-sets-enabled: true
  #    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 配置MyBatis日志,执行sql的时候,将sql打印到控制台
  global-config:
    banner: false
    db-config:
      id-type: auto
      table-underline: true

pagehelper:
  auto-dialect: mysql
  reasonable: true
  support-methods-arguments: true
  page-size-zero: true
  params: count=countSql

并在启动类添加注解

@MapperScan("com.cyun.springsecurity.mapper")
@EnableSwagger2

三、创建代码生成类 CodeGenerator

package com.cyun.springsecurity.generator;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;

import java.util.Collections;

/**
 * 新版-代码生成
 * 官网:https://www.baomidou.com/pages/981406/#%E6%95%B0%E6%8D%AE%E5%BA%93%E9%85%8D%E7%BD%AE-datasourceconfig
 */
public class CodeGenerator {

    public static void main(String[] args) {
        //创建一个代码生成器
        FastAutoGenerator fastAutoGenerator =
                FastAutoGenerator.create("jdbc:mysql://xxx.sql.tencentcdb.com:63948/shr_common",
                        "dev",
                        "xxx");

        // 全局配置(GlobalConfig)
        globalConfig(fastAutoGenerator);
        packageConfig(fastAutoGenerator);
        strategyConfig(fastAutoGenerator);
        templateConfig(fastAutoGenerator);

        fastAutoGenerator.execute(); //执行以上配置
    }

    /**
     * 全局配置(GlobalConfig)
     */
    public static void globalConfig(FastAutoGenerator generator) {
        // 项目路径
        String projectPath = System.getProperty("user.dir");
        generator.globalConfig(builder -> {
            builder
                    // 设置作者,可以写自己名字
                    .author("orange")
                    // 开启 swagger 模式,这个是接口文档生成器,如果开启的话,就还需要导入swagger依赖
                    .enableSwagger()
                    // 覆盖已生成文件
                    .fileOverride()
                    //时间策略
                    .dateType(DateType.TIME_PACK)
                    //注释日期
                    .commentDate("yyyy-MM-dd")
                    // 指定输出目录,一般指定到java目录
                    .outputDir(projectPath + "/src/main/java");
        });
    }

    /**
     * 包配置(PackageConfig)
     */
    public static void packageConfig(FastAutoGenerator generator) {
        // 项目路径
        String projectPath = System.getProperty("user.dir");
        generator.packageConfig(builder -> {
            // 设置父包名
            builder.parent("com.cyun.springsecurity")
                    // 设置父包模块名,这里一般不设置
                    .moduleName("")
                    // 设置mapperXml生成路径,这里是Mapper配置文件的路径,建议使用绝对路径
                    .pathInfo(
                            Collections.singletonMap(
                                    OutputFile.mapperXml,
                                    projectPath + "/src/main/resources/mapper")
                    );
        });
    }

    /**
     * 策略配置(StrategyConfig)
     */
    public static void strategyConfig(FastAutoGenerator generator) {
        generator.strategyConfig(builder -> {
            builder.addInclude("user"); // 设置需要生成的表名
//                            .addInclude("tbl_found") // 设置需要生成的表名
//                            .addInclude("tbl_identify") // 设置需要生成的表名
//                            .addInclude("tbl_lost") // 设置需要生成的表名
//                            .addInclude("tbl_school") // 设置需要生成的表名
//                            .addInclude("tbl_status") // 设置需要生成的表名
//                            .addInclude("tbl_user") // 设置需要生成的表名
//                            .addTablePrefix("tbl_"); // 设置过滤表前缀

            // 开启 lombok 模型
            builder.entityBuilder().enableLombok();
            builder.serviceBuilder()
                    // 设置service的命名策略,没有这个配置的话,生成的service和serviceImpl类前面会有一个I,比如IUserService和IUserServiceImpl
                    .formatServiceFileName("%sService")
                    .formatServiceImplFileName("%sServiceImpl");
            builder.controllerBuilder()
                    // 开启生成@RestController 控制器,不配置这个默认是Controller注解,RestController是返回Json字符串的,多用于前后端分离项目。
                    .enableRestStyle();
            builder.mapperBuilder()
                    //开启 @Mapper 注解,也就是在dao接口上添加一个@Mapper注解,这个注解的作用是开启注解模式,就可以在接口的抽象方法上面直接使用@Select和@Insert和@Update和@Delete注解。
                    .enableMapperAnnotation();
        });
    }

    /**
     * 模板配置(TemplateConfig)
     */
    public static void templateConfig(FastAutoGenerator generator) {
        // .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
        generator
                .templateEngine(new VelocityTemplateEngine())
                .templateConfig(builder -> {
                    builder.controller("/templates/controller.java");
                });
    }

}

四、创建统一响应结果返回对象

4.1、返回对象R

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class R<T> {

    /**
     * 状态码
     */
    private Integer code;

    /**
     * 返回信息
     */
    private String msg;

    /**
     * 数据
     */
    private T data;

    public static <T> R<T> response(Integer code, String msg, T data) {
        R<T> result = new R<>();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }

    public static <T> R<T> success() {
        return response(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getMsg(), null);
    }

    public static <T> R<T> success(T data) {
        return response(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getMsg(), data);
    }

    public static <T> R<T> success(String msg, T data) {
        return response(ResultCodeEnum.SUCCESS.getCode(), msg, data);
    }

    public static <T> R<T> fail() {
        return response(ResultCodeEnum.FAIL.getCode(), ResultCodeEnum.FAIL.getMsg(), null);
    }

    public static <T> R<T> fail(String msg) {
        return response(ResultCodeEnum.FAIL.getCode(), msg, null);
    }

    public static <T> R<T> fail(String msg, T data) {
        return response(ResultCodeEnum.FAIL.getCode(), msg, data);
    }

    public static <T> R<T> response(ResultCodeEnum resultCodeEnum,T data) {
        return response(resultCodeEnum.getCode(), resultCodeEnum.getMsg(), data);
    }
}

4.2、枚举类

@NoArgsConstructor
@AllArgsConstructor
@Getter
public enum ResultCodeEnum {
    SUCCESS(200, "操作成功"),
    LOGOUT_SUCCESS(200, "注销成功"),
    AUTHENTICATION_SUCCESS(200, "登录成功"),

    FAIL(-1, "操作失败"),

    NOT_AUTHENTICATION(401, "未认证请求"),
    ACCESS_DENIED(403, "权限不足,访问被拒绝");

    private int code;

    private String msg;

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

五、添加模板

5.1、找到源码的生成模板文件

5.2、controller.java.vm 文件存放位置

5.3、编写 controller.java.vm

package ${package.Controller};

##自动生成代码模板
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};

import org.springframework.web.bind.annotation.RequestMapping;
#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end

/**
 * <p>
 * $!{table.comment} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
@Api(tags = "${table.comment}接口")
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end

#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end

    @Autowired private ${table.serviceName} ${table.entityPath}Service;

    @ApiOperation("分页")
    @GetMapping("/page")
    public R<Page<${entity}>> findPage(@RequestParam Integer pageNum,
                                    @RequestParam Integer pageSize) {
        QueryWrapper<${entity}> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");
        return R.success(${table.entityPath}Service.page(new Page<>(pageNum, pageSize), queryWrapper));
    }

    @ApiOperation("保存")
    @PostMapping("/save")
    public R save(@RequestBody ${entity} ${table.entityPath}) {
        return R.success(${table.entityPath}Service.saveOrUpdate(${table.entityPath}));
    }

    @ApiOperation("删除")
    @DeleteMapping("/{id}")
    public R delete(@PathVariable Integer id) {
        return R.success(${table.entityPath}Service.removeById(id));
    }

    @ApiOperation("列表")
    @GetMapping("/findAll")
    public R<List<${entity}>> findAll() {
        return R.success(${table.entityPath}Service.list());
    }

    @ApiOperation("获取")
    @GetMapping("/{id}")
    public R<${entity}> findOne(@PathVariable Integer id) {
        return R.success(${table.entityPath}Service.getById(id));
    }

}

#end

六、生成代码

public class CodeGenerator {

    public static void main(String[] args) {
        //创建一个代码生成器
        FastAutoGenerator fastAutoGenerator =
                FastAutoGenerator.create("jdbc:mysql://xxx.sql.tencentcdb.com:63948/shr_common",
                        "dev",
                        "xxx");

        // 全局配置(GlobalConfig)
        globalConfig(fastAutoGenerator);
        packageConfig(fastAutoGenerator);
        strategyConfig(fastAutoGenerator);
        templateConfig(fastAutoGenerator);

        fastAutoGenerator.execute(); //执行以上配置
    }

七、配置Swagger

package com.cyun.springsecurity.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
   /**
     * 创建API应用 apiInfo() 增加API相关信息
     * 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现,
     * 本例采用指定扫描的包路径来定义指定要建立API的目录。
     *
     * @return
     */
    @Bean
    public Docket createRestApi() {
        // 选择那些路径和api会生成document
        return new Docket(DocumentationType.SWAGGER_2)
                .pathMapping("/")
                .select()
                // 对所有api进行监控
                .apis(RequestHandlerSelectors.any())
                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
                .build()
                // 配置单个请求的Token
//              .globalOperationParameters(setHeaderToken())
                .apiInfo(apiInfo())
                // 配置全局Token
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts());
    }

    /**
     * 配置单个请求的Token
     *
     * @return
     */
    private List<Parameter> setHeaderToken() {
        ParameterBuilder tokenPar = new ParameterBuilder();
        List<Parameter> pars = new ArrayList<>();
        // name属性表示header中的key
        tokenPar.name("Authorization").description("token").modelRef(new ModelRef("string")).parameterType("header")
                .required(false).build();
        pars.add((Parameter) tokenPar.build());
        return pars;
    }

    private List<ApiKey> securitySchemes() {
        List<ApiKey> apiKeyList= new ArrayList<>();
        // name表示这个ApiKey对象的名称,这个Token要和下面的Token对应 才能全局使用token
        // keyname表示header中的key
        apiKeyList.add(new ApiKey("Token", "Authorization", "header"));
        return apiKeyList;
    }

    private List<SecurityContext> securityContexts() {
        List<SecurityContext> securityContexts=new ArrayList<>();
        securityContexts.add(
                SecurityContext.builder()
                        .securityReferences(defaultAuth())
                        .forPaths(PathSelectors.regex("^(?!auth).*$"))
                        .build());
        return securityContexts;
    }

    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences=new ArrayList<>();
        securityReferences.add(new SecurityReference("Token", authorizationScopes));
        return securityReferences;
    }
 
    /**
     * 创建该API的基本信息(这些基本信息会展现在文档页面中)
     * 
     * @return
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("SpringBoot整合Swagger")
                .description("SpringBoot整合Swagger,详细信息......")
                .version("1.0.0")
                //文档制作人、个人主页地址、邮箱
                .contact(new Contact("Orange", "https://www。baidu.com", "[email protected]"))
                .build();
    }
 
}

八、配置分页功能

package com.cyun.springsecurity.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

九、SQL日志配置

9.1、通过Mybatis日志配置

9.1.1、修改yml
9.1.2、日志效果

9.2、通过日志框架配置

推荐:适合用于开发,在控制台输出

9.2.1、修改yml
9.2.2、日志效果

9.3、通过xml配置(推荐)

推荐:适合用于生产环境

9.3.1、编写xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">

   <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
   <springProperty scope="context" name="log.path" source="spring.application.name"/>

   <!-- 日志输出格式 -->
   <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />

    <!-- 控制台输出 -->
   <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
         <pattern>${log.pattern}</pattern>
      </encoder>
   </appender>

    <!-- 系统日志输出 -->
   <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--       <file>${log.path}.log</file>-->
        <!-- 循环政策:基于时间创建日志文件 -->
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
         <fileNamePattern>logs/${log.path}/${log.path}_%d{yyyy-MM-dd}.%i.log</fileNamePattern>
         <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过10M,日志文件会以索引0开始,名日志文件,例如log-error-2013-12-21.0.log -->
         <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>2MB</maxFileSize>
         </timeBasedFileNamingAndTriggeringPolicy>
         <!-- 日志最大的历史 30天 -->
         <maxHistory>30</maxHistory>
      </rollingPolicy>
      <encoder>
         <pattern>${log.pattern}</pattern>
      </encoder>
      <!--<filter class="ch.qos.logback.classic.filter.LevelFilter">
            &lt;!&ndash; 过滤的级别 &ndash;&gt;
            <level>INFO</level>
            &lt;!&ndash; 匹配时的操作:接收(记录) &ndash;&gt;
            <onMatch>ACCEPT</onMatch>
            &lt;!&ndash; 不匹配时的操作:拒绝(不记录) &ndash;&gt;
            <onMismatch>DENY</onMismatch>
        </filter>-->
   </appender>

    <!-- 系统模块日志级别控制  -->
   <logger name="com.shr" level="info" />
   <!-- Spring日志级别控制  -->
   <logger name="org.springframework" level="warn" />
    <!--myibatis log configure-->
   <logger name="com.apache.ibatis" level="debug"/>
    <logger name="java.sql.Connection" level="info"/>
    <logger name="java.sql.Statement" level="debug"/>
    <logger name="java.sql.PreparedStatement" level="info"/>

   <root level="info">
      <appender-ref ref="console" />
   </root>
   
   <!--系统操作日志-->
    <root level="info">
        <appender-ref ref="file_info" />
    </root>
</configuration>
9.3.2、修改yml

须同时开启 日志写入文件 及 开启控制台日志

9.3.3、日志效果

9.4、通过拦截器自定义日志输出(推荐)

9.4.1、定义一个拦截器及配置类

拦截器

package com.cyun.springsecurity.interceptor;

import com.cyun.springsecurity.config.SqlLogConfig;
import lombok.extern.slf4j.Slf4j;
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.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Properties;

/**
 * mybatis拦截器
 */
@Component
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MybatisInterceptor implements Interceptor {

    @Autowired
    private SqlLogConfig sqlLogConfig;

    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (!sqlLogConfig.isEnabled()){
            return invocation.proceed();
        }
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = null;
        if (invocation.getArgs().length > 1) {
            parameter = invocation.getArgs()[1];
        }
        String sqlId = mappedStatement.getId();
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        Configuration configuration = mappedStatement.getConfiguration();

        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = invocation.proceed();
        } finally {
            try {
                long sqlCostTime = System.currentTimeMillis() - startTime;
                String sql = getSql(configuration, boundSql);
                formatSqlLog(mappedStatement.getSqlCommandType(), sqlId, sql, sqlCostTime, result);
            } catch (Exception ignored) {

            }
        }
        return result;
    }


    private String getSql(Configuration configuration, BoundSql boundSql) {
        // 输入sql字符串空判断
        String sql = boundSql.getSql();
        if (StringUtils.isBlank(sql)) {
            return "";
        }

        //美化sql
        sql = beautifySql(sql);

        //填充占位符, 目前基本不用mybatis存储过程调用,故此处不做考虑
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (!parameterMappings.isEmpty() && parameterObject != null) {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = this.replacePlaceholder(sql, parameterObject);
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = replacePlaceholder(sql, obj);
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = replacePlaceholder(sql, obj);
                    }
                }
            }
        }
        return sql;
    }

    private String beautifySql(String sql) {
        return sql.replaceAll("[\s\n ]+", " ");
    }

    private String replacePlaceholder(String sql, Object parameterObject) {
        String result;
        if (parameterObject instanceof String) {
            result = "'" + parameterObject.toString() + "'";
        } else if (parameterObject instanceof Date) {
            result = "'" + getDate2String((Date) parameterObject) + "'";
        } else {
            result = parameterObject.toString();
        }
        return sql.replaceFirst("\?", result);
    }

    private String getDate2String(Date parameterObject) {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(parameterObject);
    }

    private void formatSqlLog(SqlCommandType sqlCommandType, String sqlId, String sql, long costTime, Object obj) {
        String log1 = String.format("DAO [%s]\n[%dms] ===> %s\n", sqlId, costTime, sql);
        if (sqlCommandType == SqlCommandType.UPDATE || sqlCommandType == SqlCommandType.INSERT || sqlCommandType == SqlCommandType.DELETE) {
            log1 += "Count ===> " + obj;
        }
        if (costTime > 0) {
            log.info(log1);
        }
    }
}

配置类

package com.cyun.springsecurity.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component("sqlLogConfig")
@ConfigurationProperties(prefix = "sqllog")
@Data
public class SqlLogConfig {

    /**
     * 是否开启记录SQL日志,默认为false.
     */
    private boolean enabled;

    /**
     * 记录执行时间超过多少毫秒的语句,默认0,记录所有语句.
     */
    private int minCost;
    
}
9.4.2、配置yml

新增配置

logging:
  config: classpath:logback-spring.xml
   # 这个配置要去掉,不然会以两种方式同时输出日志
#  level:
#    com.cyun.springsecurity.mapper: debug # 指定DAO层打印SQL日志

#################### sqllog ####################
# 记录sql
sqllog:
  enabled: true
  # 耗时下限ms
  min-cost: 0
9.4.3、添加8.3中的写入文件配置
9.4.4、日志效果

十、条件构造器(Wrapper,定义 where 条件)

使用案例:blog.csdn.net/qq_45225798…