Mybatisplus生成代码配置 & p6spy打印sql & mybatis日志打印 & mybatisplus用法

学习链接

mybatisplus官网

MyBatis Plus详细教程

Mybatis Plus 看这篇就够了,通俗易懂,一发入魂

LogBack日志

【mybatis plus源码解析】(一)mybatis plus执行原理,mybatis plus是如何实现自动注入CRUD操作

【mybatis plus源码解析】(二)详解SQL注入器底层原理,mybatis plus是如何实现自动注入CRUD操作

【mybatis plus源码解析】(三)自定义SQL注入器,教你如何自定义扩展BaseMapper接口方法,实现更多查询

代码生成 & p6spy打印sql

1. pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

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

    <groupId>com.zzhua</groupId>
    <artifactId>demo-mybatisplus</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>demo-mybatisplus</name>

    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <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>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.3</version>
        </dependency>

        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.9.1</version>
        </dependency>

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>

        <!--swagger2依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version> 2.7.0</version>
        </dependency>
        <!--swagger-ui第三方依赖-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.6</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2. application.yml

spring:
  datasource:
#    driver-class-name: com.mysql.jdbc.Driver
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql://127.0.0.1:3306/mytest?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: root

3. spy.properties

#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2

注意事项

  • driver-class-name 为 p6spy 提供的驱动类
  • url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
  • 打印出 sql 为 null,在 excludecategories 增加 commit
  • 批量操作不打印 sql,去除 excludecategories 中的 batch
  • 批量操作打印重复的问题请使用 MybatisPlusLogFactory (3.2.1 新增)
  • 该插件有性能损耗,不建议生产环境使用。

4. MyBatisPlusGenCode

package com.zzhua.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.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.Collections;

/**
 * @description
 * @Author: zzhua
 * @Date 2022/8/15
 */
public class MyBatisPlusGenCode {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:p6spy:mysql://127.0.0.1:3306/mytest?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true",
                "root", "root")
                .globalConfig(builder -> {
                    builder.author("zzhua") // 设置作者
                            .fileOverride() // 覆盖已生成文件
                            .enableSwagger() // 开启 swagger 模式
                            .dateType(DateType.ONLY_DATE)
                            .disableOpenDir()
                            .outputDir("D:\\projects\\demo-mybatisplus\\src\\main\\java"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.zzhua") // 设置父包名
                            .moduleName("system") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.xml, "D:\\projects\\demo-mybatisplus\\src\\main\\resources\\mapper")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude("my_user") // 设置需要生成的表名
                            // .addTablePrefix("t_", "c_"); // 设置过滤表前缀
                            .entityBuilder()
                                .enableFileOverride()
                                .enableLombok()
                                // .enableTableFieldAnnotation()
                                .naming(NamingStrategy.underline_to_camel)
                                .idType(IdType.AUTO)
                                .convertFileName(name->name.concat("Entity"))
                            .controllerBuilder()
                                .enableFileOverride()
                                .enableRestStyle()
                            .mapperBuilder()
                                .enableFileOverride()
                                .enableBaseColumnList()
                                .enableBaseResultMap()
                    ;
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();

    }
}


mybatis日志打印

原理分析

在这里插入图片描述

  • 当以上配好确定了日志的构造器后,mybatis中的所有打印日志的地方,都会直接的从Mybatis自己的日志工厂类中获取日志输出对象。

  • 而确定构造器的过程就是查找当前项目中,所能够支持的具体实现。项目一启动,mybatis在LogFactory的静态方法中,就会去按左边的列表,挨个尝试,看哪个能够创建成功,第一个创建成功的,则会设置为确定的构造器。

  • 也可以手动设置Configuration的logImpl,实际上就等价于在修改LogFactory的logConstructor的实现。

还有重要的一点,要留意到:在MappedStatement.Builder中给MappedStatement设置statementLog对象的时候,就是直接使用的LogFactory.getLog(logId),其中的这个logId就是当前sql语句所在的命名空间拼接上当前sql的id,后面很多打印sql相关的日志,其实就是把这个statementLog传来传去的在用statementLog。并且,mybatis的日志打印实际上它还通过动态代理的方式,给原来的对象包了一层,将日志打印的逻辑嵌入到了其中,具体的日志打印交给了以BaseJdbcLogger为抽象父类,子类有:StatementLogger、ConnectionLogger、ResultSetLogger、PreparedStatementLogger打印的。

当以上mybatis日志相关的配置原理理解了之后,我们发现真正的日志打印,mybatis其实并没有帮我们做,它只是写了个类,日志的具体输出就桥接给了真正的日志实现。所以开始初学mybatis时,也是比较头疼的问题,有的时候,能打印出日志,有的时候打印不出来日志,现在就知道为啥了。

示例

在项目里面,我们可以直接配置configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl,将sql直接打印输出在控制台,它里面的实现就是不管什么级别的输出日志,就是调用System.out.println(s) 或 System.err.println(s);直接打印在控制台上。这个实现感觉也不那么好用:它的trace是打开的,也就是说在查询语句中,它老是把Columns:…,Row:…全特么给打印出来,有的时候,数据一多,看着不爽,而且我们没办法改变它里面的属性。我们有2种办法解决这个问题,1:可以自己复制它这个类,然后手动改了它的默认方法,比如把traceEnabled的返回值改为false,然后指定 为我们的实现类。2:换个org.apache.ibatis.logging.slf4j.Slf4jImpl的实现,然后,就可以结合logback来玩了(可参考:LogBack日志
),也可以把sql输出到控制到,把sql日志单独输出到一个文件中也可以,还可以指定日志器的输出级别,特别方便,推荐使用这种方式。

logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">

    <include resource="org/springframework/boot/logging/logback/defaults.xml" />

    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_BASE_PATH" value="logs" />
    <property name="maxFileSize" value="5MB"/>
    <property name="maxHistory" value="30"/>
    <!--<property name="commonPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS}-${PID}-[%X{X-B3-TraceId},%X{X-B3-SpanId},%X{X-B3-ParentSpanId}]-[%thread] %-5level %logger{30} [%file:%line] - %msg%n"/>-->

    <!--控制台日志, 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!--文件日志, 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_BASE_PATH}/vue-springboot.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_BASE_PATH}/%d/xxx.log.%d{yyyy-MM-dd}-%i.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>${maxHistory}</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

	<!-- 在这里可以修改日志级别 -->
    <logger name="com.zzhua.mapper" level="trace" additivity="false">
        <appender-ref ref="STDOUT" />
    </logger>

    <!-- 日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE"/>
    </root>

</configuration>
application.yml
server:
  port: 8085
  servlet:
    context-path:
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/vue-springboot?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root
  servlet:
    multipart:
      max-file-size: 50MB
      max-request-size: 50MB

mybatis-plus:
  mapper-locations: classpath:/mapper/**.xml
  configuration:
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

使用

@Tableld

/**
 * 表主键标识
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableId {

    /**
     * 字段名(该值可无)
     */
    String value() default "";

    /**
     * 主键类型
     * {@link IdType}
     */
    IdType type() default IdType.NONE;
}

@TableField

/**
 * 表字段标识
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableField {

    /**
     * 数据库字段值
     * <p>
     * 不需要配置该值的情况:
     * <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 true 时,
     * (mp下默认是true,mybatis默认是false), 数据库字段值.replace("_","").toUpperCase() == 实体属性名.toUpperCase() </li>
     * <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 false 时,
     * 数据库字段值.toUpperCase() == 实体属性名.toUpperCase() </li>
     */
    String value() default "";

    /**
     * 是否为数据库表字段
     * <p>
     * 默认 true 存在,false 不存在
     */
    boolean exist() default true;

    /**
     * 字段 where 实体查询比较条件
     * <p>
     * 默认 {@link SqlCondition#EQUAL}
     */
    String condition() default "";

    /**
     * 字段 update set 部分注入, 该注解优于 el 注解使用
     * <p>
     * 例1:@TableField(.. , update="%s+1") 其中 %s 会填充为字段
     * 输出 SQL 为:update 表 set 字段=字段+1 where ...
     * <p>
     * 例2:@TableField(.. , update="now()") 使用数据库时间
     * 输出 SQL 为:update 表 set 字段=now() where ...
     */
    String update() default "";

    /**
     * 字段验证策略之 insert: 当insert操作时,该字段拼接insert语句时的策略
     * <p>
     * IGNORED: 直接拼接 insert into table_a(column) values (#{columnProperty});
     * NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
     * NOT_EMPTY: insert into table_a(<if test="columnProperty != null and columnProperty!=''">column</if>) values (<if test="columnProperty != null and columnProperty!=''">#{columnProperty}</if>)
     * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL
     *
     * @since 3.1.2
     */
    FieldStrategy insertStrategy() default FieldStrategy.DEFAULT;

    /**
     * 字段验证策略之 update: 当更新操作时,该字段拼接set语句时的策略
     * <p>
     * IGNORED: 直接拼接 update table_a set column=#{columnProperty}, 属性为null/空string都会被set进去
     * NOT_NULL: update table_a set <if test="columnProperty != null">column=#{columnProperty}</if>
     * NOT_EMPTY: update table_a set <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
     * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL
     *
     * @since 3.1.2
     */
    FieldStrategy updateStrategy() default FieldStrategy.DEFAULT;

    /**
     * 字段验证策略之 where: 表示该字段在拼接where条件时的策略
     * <p>
     * IGNORED: 直接拼接 column=#{columnProperty}
     * NOT_NULL: <if test="columnProperty != null">column=#{columnProperty}</if>
     * NOT_EMPTY: <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
     * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL
     *
     * @since 3.1.2
     */
    FieldStrategy whereStrategy() default FieldStrategy.DEFAULT;

    /**
     * 字段自动填充策略
     * <p>
     * 在对应模式下将会忽略 insertStrategy 或 updateStrategy 的配置,等于断言该字段必有值
     */
    FieldFill fill() default FieldFill.DEFAULT;

    /**
     * 是否进行 select 查询
     * <p>
     * 大字段可设置为 false 不加入 select 查询范围
     */
    boolean select() default true;

    /**
     * 是否保持使用全局的 columnFormat 的值
     * <p>
     * 只生效于 既设置了全局的 columnFormat 也设置了上面 {@link #value()} 的值
     * 如果是 false , 全局的 columnFormat 不生效
     *
     * @since 3.1.1
     */
    boolean keepGlobalFormat() default false;

    /**
     * {@link ResultMapping#property} and {@link ParameterMapping#property}
     *
     * @since 3.4.4
     */
    String property() default "";

    /**
     * JDBC类型 (该默认值不代表会按照该值生效),
     * 只生效于 mp 自动注入的 method,
     * 建议配合 {@link TableName#autoResultMap()} 一起使用
     * <p>
     * {@link ResultMapping#jdbcType} and {@link ParameterMapping#jdbcType}
     *
     * @since 3.1.2
     */
    JdbcType jdbcType() default JdbcType.UNDEFINED;

    /**
     * 类型处理器 (该默认值不代表会按照该值生效),
     * 只生效于 mp 自动注入的 method,
     * 建议配合 {@link TableName#autoResultMap()} 一起使用
     * <p>
     * {@link ResultMapping#typeHandler} and {@link ParameterMapping#typeHandler}
     *
     * @since 3.1.2
     */
    Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;

    /**
     * 只在使用了 {@link #typeHandler()} 时判断是否辅助追加 javaType
     * <p>
     * 一般情况下不推荐使用
     * {@link ParameterMapping#javaType}
     *
     * @since 3.4.0 @2020-07-23
     */
    boolean javaType() default false;

    /**
     * 指定小数点后保留的位数,
     * 只生效于 mp 自动注入的 method,
     * 建议配合 {@link TableName#autoResultMap()} 一起使用
     * <p>
     * {@link ParameterMapping#numericScale}
     *
     * @since 3.1.2
     */
    String numericScale() default "";
}
mybatisplus 插入/修改 用法
插入(或新增)
  • userMapper.insert(userEntity)
    • 将会插入userEntity对象中属性值不为null的字段
  • userService.save(new UserEntity())
    • 将会插入userEntity对象中属性值不为null的字段 ,其实就是调用userMapper.insert(userEntity)
修改(或更新)
  • userMapper.updateById(userEntity)
    • 以where id = #{userEntity.id} 为条件,userEntity对象中 属性值不为null的字段作为set,组成sql
  • userMapper.update(userEntity, new UpdateWrapper<userEntity>().lambda().eq( UserEntity::getNickname, "zzhua"));
    • 以后面updateWrapper指定的字段作为where条件,并且以userEntity对象中 属性值不为null的字段作为set,组成sql
    • 后面的updateWrapper不仅可以指定作为where条件的字段,还可以设置sql字段的值(这也会拼接到最终的sql上,并且这如果和userEntity对象的属性重复了,这里的顺序会在后面,所以会以这里的生效),可以考虑使用QueryWrapper,这样就没有设置sql字段的方法了。
    • 注意上面Wrapper需要设置表对应的实体类 作为泛型
  • userService.updateById(userEntity)
    • 将会以where id = #{userEntity.id} 为条件,插入userEntity对象中属性值不为null的字段 ,其实就是调用userMapper.insert(userEntity)
  • userService.update(user, new UpdateWrapper<User>().lambda().eq( User::getNickname, "zzhua"))
    • 其实就是调用userMapper.update(userEntity, new UpdateWrapper<userEntity>().lambda().eq( UserEntity::getNickname, "zzhua")),所以用法完全与它相同
  • userService.update(new UpdateWrapper<User>().lambda().eq(User::getNickname, "zzhua195").set(User::getIsV, 10))
    • 其实就是调用 userService.update(user,updateWrapper)),只不过这里的user是null,即等价于:userService.update(null , updateWrapper))
    • 直接以UpdateWrapper调用方法确定更新的where条件 和 使用set更新字段的值
  • userService.lambdaUpdate().set(User::getIsV, 55).eq(User::getNickname, "zzhua195").update()
    • 以链式的方式使用mybatisplus
    • 这种写法有两次出现update()方法哦,但不是同一个update()方法 ,前面的update()方法是开启链式调用的开端,后面的update一定要调用,否则不会执行sql
    • 最后面的update方法中可以传入一个userEntity对象,其中userEntity对象中的属性值不为null的属性将会设置作为更新的字段。当然,也可以不传入。如果传入了一个userEntity对象,并且遇到与前面设置的字段有冲突,最后都会加入到sql中,但是前面设置的会出现在sql的后面位置,因此,前面设置更优先。
  • userService.update().eq("nickname", "zzhua195").set("avatarUrl", "urll").update()
    • 以链式的方式使用mybatisplus,这种用法需要把字段名写准确(不能写错,因此不推荐使用,万一后面改字段名了,就废了),可以简单理解为:不带lambda的话,那就要写具体的数据库字段名
    • 这种写法有两次出现update()方法哦,但不是同一个update()方法 ,前面的update()方法是开启链式调用的开端,后面的update一定要调用,否则不会执行sql
    • 最后面的update方法中可以传入一个userEntity对象,其中userEntity对象中的属性值不为null的属性将会设置作为更新的字段。当然,也可以不传入。如果传入了一个userEntity对象,并且遇到与前面设置的字段有冲突,最后都会加入到sql中,但是前面设置的会出现在sql的后面位置,因此,前面设置更优先。
插入或修改
  • userService.saveOrUpdate(userEntity)
    • 先去获取userEntity的主键,如果主键为null或者为空字符串(即没有指定主键),那么就一定是插入,使用baseMapper.insert(userEntity)插入数据。如果指定了主键,那么会调用baseMapper.selectById(id)方法,根据主键查询是否存在,如果不存在,则执行baseMapper.insert(userEntity)插入数据,如果存在,则执行baseMapper.updateById(userEntity)
  • userService.saveOrUpdate(userEntity, new UpdateWrapper<UserEntity>().lambda().eq(UserEntity::getNickname, "zzhua195").set(UserEntity::getCreateTime, new Date()))
    • 先尝试调用baseMapper.update(entity,updateWrapper),如果这个结果返回的是的数量大于等于1,那么直接返回true,这个saveOrUpdate就执行完了。但是如果这个结果返回的数量没有超过1(未更新到数据),那么就会去执行userService.saveOrUpdate(userEntity),注意这个时候就跟updateWrapper没什么关系了。

FieldStrategy

IGNORED
NOT_NULL
NOT_EMPTY
DEFAULT
NEVER
/**
 * 字段策略枚举类
 * <p>
 * 如果字段是基本数据类型则最终效果等同于 {@link #IGNORED}
 *
 * @author hubin
 * @since 2016-09-09
 */
public enum FieldStrategy {
    /**
     * 忽略判断
     */
    IGNORED,
    /**
     * 非NULL判断
     */
    NOT_NULL,
    /**
     * 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
     */
    NOT_EMPTY,
    /**
     * 默认的,一般只用于注解里
     * <p>1. 在全局里代表 NOT_NULL</p>
     * <p>2. 在注解里代表 跟随全局</p>
     */
    DEFAULT,
    /**
     * 不加入 SQL
     */
    NEVER
}

FieldFill

/**
 * 字段填充策略枚举类
 *
 * <p>
 * 判断注入的 insert 和 update 的 sql 脚本是否在对应情况下忽略掉字段的 if 标签生成
 * <if test="...">......</if>
 * 判断优先级比 {@link FieldStrategy} 高
 * </p>
 */
public enum FieldFill {
    /**
     * 默认不处理
     */
    DEFAULT,
    /**
     * 插入时填充字段
     */
    INSERT,
    /**
     * 更新时填充字段
     */
    UPDATE,
    /**
     * 插入和更新时填充字段
     */
    INSERT_UPDATE
}
自动填充字段

在数据表的设计中,经常需要加一些字段,如:创建时间,最后修改时间等。

  • 一种是在数据库中设置创建时间的默认值为CURRENT_TIMESTAMP,更新时间勾选根据当前时间戳更新
  • 现在可以使用mybatisplus提供的字段填充功能来实现了
UserEntity
@Data
@TableName(value = "`user`")
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;

    @TableField(value = "nickname")
    private String nickname;

    @TableField(value = "is_v")
    private Integer isV;

    @TableField(value = "avatar_url")
    private String avatarUrl;

    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private Date createTime;

    @TableField(value = "update_time",fill = FieldFill.UPDATE)
    private Date updateTime;

}
定义MetaObjectHandler
@Bean
public MetaObjectHandler metaObjectHandler() {
   return new MetaObjectHandler() {
       @Override
       public void insertFill(MetaObject metaObject) {
           this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
       }

       @Override
       public void updateFill(MetaObject metaObject) {
           this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
       }
   };

}
使用

// 插入数据,自动设置create_time (这里全部为null,因此只会插入create_time字段)
userService.save(new UserEntity());

// 相当于调用userService(null, updateWrapper); 这种情况不会自动设置updateTime
userService.update(new UpdateWrapper<UserEntity>().lambda().eq(UserEntity::getNickname,"zzhua").set(UserEntity::getAvatarUrl,"uuurrrlll"));

// 更新数据, 自动设置updateTime
userService.update(new User(), new UpdateWrapper<User>().lambda().eq(User::getNickname, "zzhua").set(User::getAvatarUrl, "uuurrrlll"));
注意事项
  • 填充原理是直接给entity的属性设置值!!!
  • 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null
  • MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充(也就是,如果我们设置了的话,那自动填充不会干涉到我们的操作,)
  • 字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
  • 填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component或@Bean注入
  • 要想根据注解FieldFill.xxx和字段名以及字段类型来区分必须使用父类的strictInsertFill或者strictUpdateFill方法
  • 不需要根据任何来区分可以使用父类的fillStrategy方法
  • update(T t,Wrapper updateWrapper)时t不能为空,否则自动填充失效
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值