MyBatis-Plus 的進階

MyBatis-Plus 快速入門 – 入門篇鏈接
項目地址 :https://github.com/tyronczt/java-learn/tree/master/Tools/mybatis-plus-high

邏輯刪除的運用

1、在刪除字段上增加 @TableLogic 註解

/**
 * 刪除標識,0未刪除,1已刪除
 * @TableLogic 描述:表字段邏輯處理註解(邏輯刪除)
 */
@TableLogic
private Integer deleted;

2、配置文件中增加刪除標識的值,默認是 0 表示未刪除,1 表示已刪除

mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
      logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)

3、測試方法

 /**
   * 邏輯刪除(TableLogic的使用)
   *
   * ==>  Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0
   * ==> Parameters: 1094592041087729666(Long)
   * <==    Updates: 1
   * 影響行數:1
   */
@Test
public void deleteById() {
    int effectNum = userMapper.deleteById(1094592041087729666L);
    System.out.println("影響行數:" + effectNum);
}

 /**
   * 根據id查詢用戶信息,會增加delete=0的條件
   *
   * ==>  Preparing: SELECT id,deleted,create_time,name,update_time,manager_id,version,email,age FROM user WHERE id=? AND deleted=0
   * ==> Parameters: 1094592041087729666(Long)
   * <==      Total: 0
   * 用戶信息:null
   *
   * 在deleted字段上增加註解:@TableField(select = false) 不顯示deleted字段。
   * ==>  Preparing: SELECT id,create_time,name,update_time,manager_id,version,email,age FROM user WHERE id=? AND deleted=0
   */
@Test
public void selectById() {
    User user = userMapper.selectById(1094592041087729666L);
    System.out.println("用戶信息:" + user);
}

注意事項:自定義方法中 @TableLogic 不會起作用

自動填充的運用

1、在自動填充字段上增加 @TableField(fill = FieldFill.XXX) 註解

/**
 * 創建時間
 */
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

/**
 * 更新時間
 */
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;

2、自定義表對象處理器 MyMetaObjectHandler,實現 MetaObjectHandler 接口

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        System.out.println("---insertFill---");
        setInsertFieldValByName("createTime", LocalDateTime.now(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        System.out.println("---updateFill---");
        setUpdateFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }
}

3、測試方法

/**
 *  新增用戶,會自動填充操作時間
 *
 * ---insertFill---
 * ==>  Preparing: INSERT INTO user ( id, create_time, name, manager_id, email, age ) VALUES ( ?, ?, ?, ?, ?, ? )
 * ==> Parameters: 1260593237899411457(Long), 2020-05-13T23:28:14.124(LocalDateTime), 陳海華(String), 1088250446457389058(Long), [email protected](String), 23(Integer)
 * <==    Updates: 1
 * 影響行數:1
 */
@Test
public void insert() {
    User user = new User();
    user.setAge(23);
    user.setName("陳海華");
    user.setEmail("[email protected]");
    user.setManagerId(1088250446457389058L);
    int effectNum = userMapper.insert(user);
    System.out.println("影響行數:" + effectNum);
}

/**
 * 更新用戶信息,會自動填充更新時間
 *
 * ---updateFill---
 * ==>  Preparing: UPDATE user SET update_time=?, age=? WHERE id=? AND deleted=0
 * ==> Parameters: 2020-05-13T23:37:12.779(LocalDateTime), 24(Integer), 1260593237899411457(Long)
 * <==    Updates: 1
 * 影響行數:1
 */
@Test
public void updateById() {
    User user = new User();
    user.setId(1260593237899411457L);
    user.setAge(24);
    int effectNum = userMapper.updateById(user);
    System.out.println("影響行數:" + effectNum);
}

4、根據實際情況對處理器優化

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        // 優化1:當有自動填充字段時再進行自動填充
        boolean hasSetter = metaObject.hasSetter("createTime");
        if (hasSetter) {
            System.out.println("---insertFill---");
            setInsertFieldValByName("createTime", LocalDateTime.now(), metaObject);
        }
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        // 優化2:當字段值沒有設值時再自動填充
        Object updateTime = getFieldValByName("updateTime", metaObject);
        if (null == updateTime) {
            System.out.println("---updateFill---");
            setUpdateFieldValByName("updateTime", LocalDateTime.now(), metaObject);
        }
    }
}

樂觀鎖插件的運用

悲觀鎖(Pessimistic Locking),悲觀鎖是指在數據處理過程,使數據處於鎖定狀態,一般使用數據庫的鎖機制實現。適合在寫多讀少的併發環境中使用,雖然無法維持非常高的性能,但是在樂觀鎖無法提更好的性能前提下,可以做到數據的安全性。

樂觀鎖相對悲觀鎖而言,它認爲數據一般情況下不會造成衝突,所以在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,如果發現衝突了,則讓返回錯誤信息,讓用戶決定如何去做。

利用數據版本號(version)機制是樂觀鎖最常用的一種實現方式。

1、配置樂觀鎖插件

/**
 * 樂觀鎖插件
 */
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
	return new OptimisticLockerInterceptor();
}

2、在版本字段上增加 @Version 註解

@Version
private Integer version;

3、測試栗子

/**
  * 樂觀鎖-更新用戶數據
  * 
  * ==>  Preparing: UPDATE user SET update_time=?, version=?, age=? WHERE id=? AND version=? AND deleted=0
  * ==> Parameters: 2020-05-17T20:42:39.527(LocalDateTime), 2(Integer), 29(Integer), 1260593237899411457(Long), 1(Integer)
  * <==    Updates: 1
  * 影響行數:1
  */
@Test
public void updateById() {
    // 模擬從數據庫取出版本信息
    int version = 1;

    User user = new User();
    user.setId(1260593237899411457L);
    user.setVersion(version);
    user.setAge(29);
    int effectNum = userMapper.updateById(user);
    System.out.println("影響行數:" + effectNum);
}

執行 SQL 分析打印

1、Maven:

<dependency>
  <groupId>p6spy</groupId>
  <artifactId>p6spy</artifactId>
  <version>最新版本</version>
</dependency>

2、application.yml 配置:

spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:h2:mem:test
    ...

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
#日誌輸出到文件中
logfile=tyron.log
# 使用日誌系統記錄 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

Sql 注入器

1.1、創建定義方法的類

@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
    // 執行的SQL
    String sql = "delete from " + tableInfo.getTableName();
    // Mapper接口方法名
    String method = "deleteAll";
    SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
    return addDeleteMappedStatement(mapperClass, method, sqlSource);
}

1.2、創建注入器

@Component
public class MySqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new DeleteAllMethod());
        return methodList;
    }
}

1.3、在Mapper中加入自定義方法

/**
 * 刪除所有數據
 *
 * @return 影響的行數
 */
int deleteAll();

1.4、測試

/**
 * 刪除所有行數
 *
 * ==>  Preparing: delete from user
 * ==> Parameters:
 * <==    Updates: 7
 * 影響的行數:7
 */
@Test
public void deleteAll() {
    int effectNums = userMapper.deleteAll();
    System.out.println("影響的行數:" + effectNums);
}

2.1、將自帶批量插入注入器添加到methodList中

@Component
public class MySqlInjector extends DefaultSqlInjector {

   @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new DeleteAllMethod());
        methodList.add(new InsertBatchSomeColumn(t -> !t.isLogicDelete() && !t.getColumn().equals("age")));// 自帶批量插入注入器(邏輯刪除字段和年齡字段不填充)
        return methodList;
    }
}

2.2、在Mapper中加入自帶方法

 /**
  * 批量插入用戶
  *
  * @param list 列表
  * @return
  */
int insertBatchSomeColumn(List<User> list);

2.3、測試

 @Test
 public void insertBatchSomeColumn() {
     User user1 = new User();
     user1.setName("黃呼呼");
     user1.setEmail("[email protected]");
     user1.setManagerId(1087982257332887553L);
     user1.setAge(33);

    User user2 = new User();
    user2.setName("張云云");
    user2.setEmail("[email protected]");
    user2.setManagerId(1087982257332887553L);
    user2.setAge(23);
    int effectNums = userMapper.insertBatchSomeColumn(Arrays.asList(user1, user2));
    System.out.println("影響的行數:" + effectNums);
}

同理的自帶注入器還有:

  • LogicDeleteByIdWithFill:根據 id 邏輯刪除數據,並帶字段填充功能。注意入參是 entity !!! ,如果字段沒有自動填充,就只是單純的邏輯刪除;
  • AlwaysUpdateSomeColumnById :根據 ID 更新固定的那幾個字段(但是不包含邏輯刪除)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章