天天看點

mybatisplus @tablefield注解_基于mybatis plus講解一些知識點

mybatisplus @tablefield注解_基于mybatis plus講解一些知識點

關注我,精彩文章第一時間推送給你

mybatisplus @tablefield注解_基于mybatis plus講解一些知識點

mybatis plus之主鍵生成政策

1.自增政策

@TableId(value = "id", type = IdType.AUTO)
private String id;
           

2.雪花生成器(推)

java @TableId(value = "id", type = IdType.ASSIGN_ID) private String id;

3.UUID

@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
           
  • 自3.3.0開始,預設使用雪花算法+UUID(不含中劃線)
mybatisplus @tablefield注解_基于mybatis plus講解一些知識點

4.Sequence主鍵

@KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class)
public class YourEntity {

    @TableId(value = "ID_STR", type = IdType.INPUT)
    private String idStr;

}
           
主鍵生成政策必須使用INPUT 支援父類定義@KeySequence子類繼承使用

内置支援:

  • DB2KeyGenerator
  • H2KeyGenerator
  • KingbaseKeyGenerator
  • OracleKeyGenerator
  • PostgreKeyGenerator
如果内置支援不滿足你的需求,可實作IKeyGenerator接口來進行擴充.

SpringBoot方式一:配置類

@Bean
public IKeyGenerator keyGenerator() {
    return new H2KeyGenerator();
}
           

SpringBoot方式二:通過MybatisPlusPropertiesCustomizer自定義

@Bean
public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
    return plusProperties -> plusProperties.getGlobalConfig().getDbConfig().setKeyGenerator(new H2KeyGenerator());
}
           

mybatis plus之自動填充字段功能

  • 個人比較喜歡這個功能,尤其是用于

    create_time

    update_time

    兩個字段的自動填充。
/**
     * 注意:設定此字段為自動填充字段,即添加記錄的時候自動添加建立時間
     * 需要配置實作接口 MetaObjectHandler
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /**
     * 注意:設定此字段為自動填充字段,添加或更新記錄時候,此字段自動填充
     * 需要配置實作接口 MetaObjectHandler
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
           
  • 需要實作接口

    MetaObjectHandler

    中繼資料處理器接口
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 實作添加的時候自動填充的字段
     * 添加記錄的時候自動設定建立時間和修改時間為目前時間
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
        this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }

    /**
     * 修改的時候自動填充的字段, 填充為目前時間
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }
}
           

mybatis plus之樂觀鎖

  • 首先了解一下什麼是樂觀鎖?什麼是悲觀鎖?
樂觀鎖與悲觀鎖都是用來解決并發情況下寫操作可能會導緻的 丢失更新 的問題。
  • 什麼是并發操作下造成的讀的問題 ?
首先來說如果不考慮事務的隔離性,會産生幾個讀的問題, 髒讀、不可重複讀、幻讀 髒讀 :讀到未送出的資料,也就是事務A讀到事務B更改後的資料,事務B異常復原了,是以事務A讀到的是髒資料。 不可重複讀 :例如事務A讀到小李30歲,這時候事務B把小李改成20歲送出了,事務A未結束又讀了一遍,發現小李變成了20歲。 幻讀 :例如事務A讀員工數量200人,這時候事務B添加了5條員工資料并送出,這時候事務A未結束,又讀取了一遍員工數量,發現變成了205條。
  • 額、上面簡單總結一下,不考慮事務的隔離級别的情況下可能造成的讀的問題。
  • 事務的隔離級别有什麼呢?

1、Serializable (串行化):最嚴格的級别,事務串行執行,資源消耗最大;

2、REPEATABLE READ(重複讀) :保證了一個事務不會修改已經由另一個事務讀取但未送出(復原)的資料。避免了“髒讀取”和“不可重複讀取”的情況,但不能避免“幻讀”,但是帶來了更多的性能損失。

3、READ COMMITTED (讀送出):大多數主流資料庫的預設事務等級,保證了一個事務不會讀到另一個并行事務已修改但未送出的資料,避免了“髒讀取”,但不能避免“幻讀”和“不可重複讀取”。該級别适用于大多數系統。

4、Read Uncommitted(讀未送出) :事務中的修改,即使沒有送出,其他事務也可以看得到,會導緻“髒讀”、“幻讀”和“不可重複讀取”。

  • mysql預設的事務隔離級别是:可重複讀
mybatisplus @tablefield注解_基于mybatis plus講解一些知識點
  • 什麼是并發寫操作造成的丢失更新?

例如事務A和事務B同時修改小明的工資,他們兩個讀取到的小明工資都是2000然後事務A更改了小明工資為3000後送出,這時候事務B更改了小明的工資為5000,送出之後事務A對工資的更新就丢失了。結果為5000

悲觀鎖和樂觀鎖就是為了解決并發操作造成的丢失更新問題:

悲觀鎖: 顧名思義,就是悲觀的認為并發問題一定會出現,是以在對一個資料進行操作的時候一定會加鎖,因為他認為不加鎖一定會産并發問題。 樂觀鎖: 顧名思義,就是樂觀的認為讀取資料的時候不會有其他事務進行修改,是以不會加鎖,但是在更新時會判斷其他線程在這之前有沒有對資料進行修改,一般會使用 版本号機制 CAS操作 實作。這裡mybatis plus使用的就是版本号機制實作的樂觀鎖。
  • 先介紹一下CAS操作:Compare and Swap,即比較再交換。
java中有一個并發包

java.util.concurrent.*

,這個包中的類就是使用CAS算法實作了樂觀鎖。之前java語言靠

synchronized

關鍵字保證同步,使用

synchronized

關鍵字是一種獨占鎖,屬于悲觀鎖。
  • CAS的功能是判斷記憶體中的某個位置的值是否為預期值,如果是則改變為新的值,這個過程是原子的。

CAS算法實作的一個前提是需要取出記憶體中某時刻的資料,并在目前時間進行比較并替換,那麼在這個時間差内,如果線程A從記憶體V處取出值為1,這時候另一個線程B也在記憶體V處取出值為1,并且線程B經過一些操作将值改成了2,然後線程B又經過操作将值改回1,這時候線程A操作CAS發現記憶體中的值仍為1,然後線程A操作成功改了V處的值為3。

盡管這個線程A的CAS操作執行成功,但是并不代表這個過程是沒有問題的。這就是著名的

ABA問題

  • 如何解決ABA問題?使用版本号!mybatis plus就是使用版本号來實作樂觀鎖的。
可以使用版本号來解決,例如:資料庫中添加一個版本号,取出記錄的時候,擷取目前的版本号,更新資料的時候,帶上這個版本号,即 update set version = newVersion where version = oldVersion,如果版本号不等于之前的版本号,說明已經被更改過了,即本次更新失敗。
  • 使用mybatis plus實作樂觀鎖
  • 表中添加字段,作為樂觀鎖的版本号
  • 對應實體類添加屬性version
  • 在實體類版本号屬性上添加@Version注解
  • 配置mybatis plus樂觀鎖的插件
  • (可選)可以利用之前講的自動填充在添加記錄的時候給Version一個預設值1
mybatisplus @tablefield注解_基于mybatis plus講解一些知識點
/**
 * 版本号屬性
 * 設定此字段在添加的時候自動設定version值為 1
 * 需要配置實作接口 MetaObjectHandler
 */
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;
@Configuration
@MapperScan(basePackages = {"com.yunqing.demomybatisplus.mapper"})
public class MybatisPlusConfig {

    //配置樂觀鎖插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 實作添加的時候自動填充的字段
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        //配置添加role記錄的時候自動填充版本号為1
        this.setFieldValByName("version", "1", metaObject);
    }
}
           
  • 測試使用樂觀鎖進行更新資料
/**
     * ActiveRecord修改
     * UPDATE t_role SET role_code=?, role_name=?, update_time=?, version=? WHERE id=? AND version=?
     *
     * UPDATE t_role SET create_time=? WHERE (id = ?)
     */
    @Test
    void update() {
        /**
         * 測試樂觀鎖更新Role,先查詢擷取版本号,在更新,之後再擷取版本号
         */
        Role role = new Role().selectById(2L);
        log.info("更新之前擷取版本号 = {}", role.getVersion());
        role.setRoleName("管理者1234");
        Assertions.assertTrue(role.updateById());
        Role role2 = new Role().selectById(2L);
        log.info("更新之後擷取版本号 = {}", role2.getVersion());
        /**
         * 非樂觀鎖更新,直接更新
         */
        Assertions.assertTrue(new Role().update(new UpdateWrapper<Role>().lambda()
                .set(Role::getCreateTime, LocalDateTime.now()).eq(Role::getId, 1)));
    }
           
  • 可以看到控制台如下輸出:
mybatisplus @tablefield注解_基于mybatis plus講解一些知識點
  • 适用場景:讀取頻繁使用樂觀鎖,寫入頻繁使用悲觀鎖