Mybatis-Plus 常用注解总结

发布时间 2023-12-28 23:16:26作者: 进击的davis

在框架的使用中,注解约定大于配置,我们可以轻松通过注解完成很多工作,比如字段改名映射,插入更新的时间写入等,下面的学习内容主要列举了常用的注解。

我们看看官网中列出的注解有哪些[1]:

image.png

本文的注解学习主要内容集中在以下的注解中:

  • @TableName
  • @TableId
  • @TableField
  • @EnumValue
  • @Version
  • @TableLogic

环境:

  • Mybatis-Plus: 3.5.5

@TableName

先看看源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface TableName {
    String value() default "";

    String schema() default "";

    boolean keepGlobalPrefix() default false;

    String resultMap() default "";

    boolean autoResultMap() default false;

    String[] excludeProperty() default {};
}

开放的功能很多,我们平时可能并用不到,主要需要注意的是 value , 我们通过设置 value = "table_xxx" ,注解加在实体类上,映射实体类和数据库表的表名。

@TableId

源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableId {
    String value() default "";

    IdType type() default IdType.NONE;
}

注解字段的,通过设置 value 声明在数据库表的字段名,通过 type 属性声明用哪种类型的命名策略,常见的策略有:

image.png

比如我们常用的自增主键,或者通过指定id,默认使用微博开源的雪花算法生成id,全局唯一且自增id,即:

  • @TableId(type = IdType.AUTO)
  • @TableId(type = IdType.ASSIGN_ID)

@TableField

源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableField {
    String value() default "";

    boolean exist() default true;

    String condition() default "";

    String update() default "";

    FieldStrategy insertStrategy() default FieldStrategy.DEFAULT;

    FieldStrategy updateStrategy() default FieldStrategy.DEFAULT;

    FieldStrategy whereStrategy() default FieldStrategy.DEFAULT;

    FieldFill fill() default FieldFill.DEFAULT;

    boolean select() default true;

    boolean keepGlobalFormat() default false;

    String property() default "";

    JdbcType jdbcType() default JdbcType.UNDEFINED;

    Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;

    boolean javaType() default false;

    String numericScale() default "";
}

该注解提供的功能很多,这里讲几个常用的。

value

通过设置该值,对应到数据库表的字段名。

condition

设定值,进而是否开启如模糊查询。

exist

主要指明字段在不在数据库表中,如果 false 则不在数据库表中。

select

注明查到的字段是否要出现在返回结果中,某些场景中,如 User 表中,可能有 password ,对于这种敏感的字段,是不是可以不用返回呢。

fill

这里涉及自动填充,在哪些场景中会用到呢,如 插入、更新、更改的时间,我们希望自动填入,其原理其实也是通过设置实体的值,进而达到自动填入的功能。

填入时机:

image.png

如果设置了该 fill 属性,则我们需要去实现或者继承 MetaObjectHandler 接口:

package com.example.springbootmybatisplusdemo.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDateTime;

@Configuration
@MapperScan(basePackages = "com.example.springbootmybatisplusdemo.mapper")
public class AutoTimeConfig implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        // method 1
        this.strictInsertFill(metaObject, "created", () -> LocalDateTime.now(), LocalDateTime.class);
        this.strictInsertFill(metaObject, "modified", () -> LocalDateTime.now(), LocalDateTime.class);
        // method 2
        // setFieldValByName("created", LocalDateTime.now(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updated", () -> LocalDateTime.now(), LocalDateTime.class);
    }
}

在上面的实现中,我们实现了 insertFill/updateFill 两个方法,另外对于字段值的填充,在代码中也注明了2种方法。

@EnumValue

源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface EnumValue {
}

这里的使用上,我们需要加到会在数据库表中显示的字段上,比如我们的实体类:

private GenderEnum gender;

GenderEnum 是一个枚举类:

package com.example.springbootmybatisplusdemo.entity;

import com.baomidou.mybatisplus.annotation.EnumValue;

public enum GenderEnum {
    Male(1, "Male"),
    Female(0, "Female");

    @EnumValue
    private final int code;

    private final String desc;

    GenderEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}

通过上述的配置,我们就可以在设置 gender 字段时,通过 GenderEnum 枚举类给定 Male或者Female,最后写入数据库表时,实际是写入的 code 的值,注意我们声明的 1 -> Male,0 -> Female,所以在数据库表中,gender 实际填入的值是 0/1

@Value

该注解主要用于乐观锁,结合拦截件使用:

package com.example.springbootmybatisplusdemo.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.example.springbootmybatisplusdemo.mapper")
public class MybatisPlusConfig {
    // register 乐观锁插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

测试:

package com.example.springbootmybatisplusdemo.test;

import com.example.springbootmybatisplusdemo.entity.UserDemo;
import com.example.springbootmybatisplusdemo.mapper.UserDemoMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class optimisticLockTest {
    @Autowired
    private UserDemoMapper userMapper;

    @Test
    public void lockTest() {
        UserDemo user1 = new UserDemo();
        user1.setId(100L);
        user1.setVersion(1);
        UserDemo user2 = new UserDemo();
        user2.setId(100L);
        user2.setVersion(1);
        userMapper.updateById(user1); // ok
        userMapper.updateById(user2); // fail
    }
}

@TableLogic

该注解用于逻辑删除,真删除是删除数据库表中的记录行,逻辑删除则是标记某个字段的值,如:

@TableLogic(value = "0", delval = "1") 
private Integer deleted;

其中 value 是原值,delval 是删除后填入的值,如删除前,deleted为0,删除后deleted为1,我们在查询数据时注意下这个字段即可。

demo

以下是以上注解在 case 中的使用:

package com.example.springbootmybatisplusdemo.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@TableName(value = "user_demo") // tableName
public class UserDemo {
    @TableId(type = IdType.AUTO) // tableId,自增类型,主键
    private Long id;

    @TableId(type = IdType.ASSIGN_ID) // 该策略使用接口IdentifierGenerator的方法nextId(以实现类为DefaultIdentifierGenerator雪花算法)
    private Long myId;

    @TableField(value = "name", condition = SqlCondition.LIKE) // tableField,开启模糊查询
    private String username;

    @TableField(exist = false) // 指明字段不在数据表中
    private String username1;

    @TableField(select = false) // 字段不出现在返回结果中
    private String password;

    @TableField(fill = FieldFill.INSERT) // 如果继承接口,已经实现了insertFill,需要实现 MetaObjectHandler,见AutoTimeConfig
    private LocalDateTime created;

    @TableField(fill = FieldFill.UPDATE) // 需要实现 MetaObjectHandler
    private LocalDateTime updated;

    @TableField(fill = FieldFill.INSERT_UPDATE) // 需要实现 MetaObjectHandler
    private LocalDateTime modified;

    private GenderEnum gender; // enumValue

    @Version
    private Integer version; // version,乐观锁,集合配置使用,如拦截件

    @TableLogic(value = "0", delval = "1") // 逻辑删除,其实就是更新delete字段的值,0 -> 1, update table_name set deleted=1 where xxx=yyy, or delete from table_name where xxx=yyy
    private Integer deleted;
}

参考: