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

一、添加依赖

<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>

    <!-- 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>

二、配置yml、添加注解

server:
  port: 9000

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
logging:
  config: classpath:logback-spring.xml
  level:
    com.shr.patrol.mapper: debug

并在启动类添加注解,如果在 mapper 文件中添加了 @Mapper 注解,下面的注解可以不加,否则会报重复扫描;

@MapperScan("com.shr.patrol")

三、创建代码生成类 CodeGenerator

/**
 * 新版-代码生成
 * 官网: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 {

    private static final String MYSQL_URL = "jdbc:mysql://nj-cdb-mw5khn49.sql.tencentcdb.com:63948/xsw_db";
    private static final String MYSQL_USERNAME = "dev";
    private static final String MYSQL_PASSWORD = "shrdb2021A@";

    // 父包名
    private static final String PARENT_PACKAGE = "com.shr.patrol";

    public static void main(String[] args) {
        //创建一个代码生成器
        FastAutoGenerator fastAutoGenerator =
                FastAutoGenerator.create(MYSQL_URL, MYSQL_USERNAME, MYSQL_PASSWORD);

        // 全局配置(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(PARENT_PACKAGE)
                    // 设置父包模块名,这里一般不设置
                    .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("biz_weight_log") // 设置需要生成的表名
                            .addTablePrefix("biz_"); // 设置过滤表前缀
//                            .addInclude("tbl_found") // 设置需要生成的表名

            // 开启 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

六、配置分页功能

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日志配置【控制台输出】

  1. 修改yml

  2. 日志效果

9.2、通过日志框架配置【控制台输出】

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

  1. 修改yml

  2. 日志效果

9.3、通过xml配置【日志文件输出】

推荐:适合用于生产环境

  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>
  1. 修改yml

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

  1. 日志效果

9.4、通过拦截器自定义日志输出【实现sql美化拼接】

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、日志效果
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值