
關注我,精彩文章第一時間推送給你
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(不含中劃線)
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預設的事務隔離級别是:可重複讀
- 什麼是并發寫操作造成的丢失更新?
例如事務A和事務B同時修改小明的工資,他們兩個讀取到的小明工資都是2000然後事務A更改了小明工資為3000後送出,這時候事務B更改了小明的工資為5000,送出之後事務A對工資的更新就丢失了。結果為5000
悲觀鎖和樂觀鎖就是為了解決并發操作造成的丢失更新問題:
悲觀鎖: 顧名思義,就是悲觀的認為并發問題一定會出現,是以在對一個資料進行操作的時候一定會加鎖,因為他認為不加鎖一定會産并發問題。 樂觀鎖: 顧名思義,就是樂觀的認為讀取資料的時候不會有其他事務進行修改,是以不會加鎖,但是在更新時會判斷其他線程在這之前有沒有對資料進行修改,一般會使用 版本号機制 或 CAS操作 實作。這裡mybatis plus使用的就是版本号機制實作的樂觀鎖。
- 先介紹一下CAS操作:Compare and Swap,即比較再交換。
java中有一個并發包,這個包中的類就是使用CAS算法實作了樂觀鎖。之前java語言靠
java.util.concurrent.*
關鍵字保證同步,使用
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
/**
* 版本号屬性
* 設定此字段在添加的時候自動設定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)));
}
- 可以看到控制台如下輸出:
- 适用場景:讀取頻繁使用樂觀鎖,寫入頻繁使用悲觀鎖