天天看點

Mybatisplus-【3/3】進階操作

一、自定義類

自動填充功能-擴充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

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