Mybatisplus-【3/3】高级操作

目录

一、自定义类

自动填充功能-扩展MetaObjectHandler

类型处理器-扩展TypeHandler

自定义ID生成器-扩展IdentifierGenerator

二、插件

动态获取表名称-DynamicTableNameInnerInterceptor插件

防止全表更新或删除操作-BlockAttackInnerInterceptor插件

乐观锁-OptimisticLockerInnerInterceptor插件

分页-PaginationInnerInterceptor插件

三、注解

逻辑删除-@TableLogic

实体映射-@ResultMap


这部分功能不是太常用,但一旦有合适的场景会省很多麻烦,同时也要和开发同学制定好规范,主要的使用有几下几类场景

一、自定义类

自动填充功能-扩展MetaObjectHandler

//这块一般会做成一个BaseEntity.java类
public class User {
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String operator;
}
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.strictInsertFill(metaObject, "operator", String.class, "Jetty");
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.strictUpdateFill(metaObject, "operator", String.class, "Tom");
    }
}
this.strictUpdateFill(metaObject, "updateDatetime1", LocalDateTime.class, LocalDateTime.now())
    .strictUpdateFill(metaObject, "updateDatetime", LocalDateTime.class, LocalDateTime.now());

类型处理器-扩展TypeHandler

@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    /**
     * 注意!! 必须开启映射注解
     *
     * @TableName(autoResultMap = true)
     * <p>
     * 以下两种类型处理器,二选一 也可以同时存在
     * <p>
     * 注意!!选择对应的 JSON 处理器也必须存在对应依赖包
     */
    @TableField(typeHandler = WalletListTypeHandler.class)
    private List<Wallet> wallets;
    @TableField(typeHandler = FastjsonTypeHandler.class) //这是mybatis默认的
    private OtherInfo otherInfo;
}
/**
 * 自定义复杂类型处理器<br/>
 * 不要问我为什么要重写 parse 因为顶层父类是无法获取到准确的待转换复杂返回类型数据
 */
public class WalletListTypeHandler extends JacksonTypeHandler {
    public WalletListTypeHandler(Class<?> type) {
        super(type);
    }
    @Override
    protected Object parse(String json) {
        try {
            return getObjectMapper().readValue(json, new TypeReference<List<Wallet>>() {
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

自定义ID生成器-扩展IdentifierGenerator

@Data
public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
//下面这个类不需要配置,只要用spring注解后,自动起作用
@Slf4j
@Component
public class CustomIdGenerator implements IdentifierGenerator {
    private final AtomicLong al = new AtomicLong(1);
    @Override
    public Long nextId(Object entity) {
        //可以将当前传入的class全类名来作为bizKey,或者提取参数来生成bizKey进行分布式Id调用生成.
        String bizKey = entity.getClass().getName();
        log.info("bizKey:{}", bizKey);
        MetaObject metaObject = SystemMetaObject.forObject(entity);
        String name = (String) metaObject.getValue("name");
        final long id = al.getAndAdd(1);
        log.info("为{}生成主键值->:{}", name, id);
        return id;
    }
}
id BIGINT(20) NOT NULL COMMENT '主键ID',
---------------------以上适用用ID,下面的例子适用于string方式----------------------------------------
/**
 * 字符串自增 ID
 */
@TableId(type = IdType.AUTO)
private String id;
id VARCHAR(30) AUTO_INCREMENT COMMENT '主键ID',
---------------------这个例子稍复杂一点,它可为各别表指定主键生成方式------------------------------------------
@Data
@KeySequence("SEQ_USER")
@TableName("user")
public class User {
    @TableId(value = "id", type = IdType.INPUT)
    private Long id;
}
CREATE TABLE user
(
   id BIGINT(20) NOT NULL COMMENT '主键ID',
   name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
   age INT(11) NULL DEFAULT NULL COMMENT '年龄',
   email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
   PRIMARY KEY (id)
);
CREATE SEQUENCE IF NOT EXISTS SEQ_USER START WITH 10086 INCREMENT BY 1;
@Configuration
public class MpConfig {
    @Bean
    public GlobalConfig globalConfiguration() {
        GlobalConfig conf = new GlobalConfig();
        conf.setDbConfig(new GlobalConfig.DbConfig().setKeyGenerators(Arrays.asList(
                // h2 1.x 的写法(默认 2.x 的写法)
                new IKeyGenerator() {
                    @Override
                    public String executeSql(String incrementerName) {
                        return "select " + incrementerName + ".nextval";
                    }
                    @Override
                    public DbType dbType() {
                        return DbType.POSTGRE_SQL;
                    }
                }
        )));
        return conf;
    }
}

二、插件

动态获取表名称-DynamicTableNameInnerInterceptor插件

@Configuration
@MapperScan("com.baomidou.mybatisplus.samples.dytablename.mapper")
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
        dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> {
            // 获取参数方法
            Map<String, Object> paramMap = RequestDataHelper.getRequestData();
            paramMap.forEach((k, v) -> System.err.println(k + "----" + v));
            String year = "_2018";
            int random = new Random().nextInt(10);
            if (random % 2 == 1) {
                year = "_2019";
            }
            return tableName + year;
        });
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
        // 3.4.3.2 作废该方式
        // dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);
        return interceptor;
    }
}
/**
 * 请求参数传递辅助类
 */
public class RequestDataHelper {
    /**
     * 请求参数存取
     */
    private static final ThreadLocal<Map<String, Object>> REQUEST_DATA = new ThreadLocal<>();
    /**
     * 设置请求参数
     *
     * @param requestData 请求参数 MAP 对象
     */
    public static void setRequestData(Map<String, Object> requestData) {
        REQUEST_DATA.set(requestData);
    }
    /**
     * 获取请求参数
     *
     * @param param 请求参数
     * @return 请求参数 MAP 对象
     */
    public static <T> T getRequestData(String param) {
        Map<String, Object> dataMap = getRequestData();
        if (CollectionUtils.isNotEmpty(dataMap)) {
            return (T) dataMap.get(param);
        }
        return null;
    }
    /**
     * 获取请求参数
     *
     * @return 请求参数 MAP 对象
     */
    public static Map<String, Object> getRequestData() {
        return REQUEST_DATA.get();
    }
}
@Test
void test() {
    RequestDataHelper.setRequestData(new HashMap<String, Object>() {{
        put("id", 123);
        put("hello", "tomcat");
        put("name", "汤姆凯特");
    }});
    // 自己去观察打印 SQL 目前随机访问 user_2018  user_2019 表
    for (int i = 0; i < 6; i++) {
        User user = userMapper.selectById(1);
        System.err.println(user.getName());
    }
}

防止全表更新或删除操作-BlockAttackInnerInterceptor插件

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
    }
}

乐观锁-OptimisticLockerInnerInterceptor插件

@Configuration
public class MybatisPlusOptLockerConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}
@Data
public class User {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @Version
    private Integer version; //注意这个version注解
}

分页-PaginationInnerInterceptor插件

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

三、注解

逻辑删除-@TableLogic

这个完全是用注解实现的,注释是数据库DDL定义

@TableLogic
private Integer deleted; //deleted INT(11) NOT NULL DEFAULT 0,
@TableLogic(delval = "null", value = "1") 
private Integer deleted;//deleted INT(11),
@TableLogic(delval = "now()", value = "null")
private LocalDateTime delTime; //del_time TIMESTAMP,

实体映射-@ResultMap

@ResultMap("userChildrenMap")
@Select("<script>select u.id,u.name,u.email,u.age,c.id as \"c_id\",c.name as \"c_name\",c.user_id as \"c_user_id\" " +
        "from user u " +
        "left join children c on c.user_id = u.id " +
        "<where>" +
        "<if test=\"selectInt != null\"> " +
        "and u.age = #{selectInt} " +
        "</if> " +
        "<if test=\"selectStr != null and selectStr != ''\"> " +
        "and c.name = #{selectStr} " +
        "</if> " +
        "</where>" +
        "</script>")
MyPage<UserChildren> userChildrenPage(MyPage<UserChildren> myPage);
<resultMap id="userChildrenMap" type="com.baomidou.mybatisplus.samples.pagination.model.UserChildren">
    <id column="id" property="id"/>
    <result column="age" property="age"/>
    <result column="email" property="email"/>
    <result column="name" property="name"/>
    <collection property="c" ofType="com.baomidou.mybatisplus.samples.pagination.entity.Children" columnPrefix="c_">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="user_id" property="userId"/>
    </collection>
</resultMap>
@Data
@TableName(resultMap = "m_b") // 对应xml里的 id
-----------------------------------------------
public class Man {}
<resultMap id="m_b" type="com.baomidou.mybatisplus.samples.resultmap.entity.Man">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="laoPoId" column="lao_po_id"/>
</resultMap>
-----------------------------------------------
<select id="selectLinkById" resultMap="m_r">
    select *
    from man
    where id = #{id}
</select>