MyBatis-plus字段類型處理器、自動填充和樂觀鎖詳解
MyBatis-plus拓展
邏輯刪除
邏輯刪除就是增加一個字段表示這個數(shù)據(jù)的狀態(tài),通過狀態(tài)來顯示數(shù)據(jù)或隱藏?cái)?shù)據(jù),而不是真正的刪除。
MyBatis-plus使用@TableLogic注解來標(biāo)注邏輯刪除字段:
public class User extends Model<User> {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
// 配置當(dāng)前字段為邏輯刪除字段
// 默認(rèn)值是1,刪除狀態(tài)的值是0
@TableLogic(value = "1",delval = "0")
private Integer status;
}
此時如果調(diào)用Mapper的刪除方法,實(shí)際對應(yīng)的sql語句是更新操作。將邏輯刪除字段的值更新為0,而不是真正的刪除。
而且此時Mapper的查詢方法,不會查出這條數(shù)據(jù)。生成的sql語句會自動拼接where條件:status = 1
邏輯刪除也可以在配置文件中進(jìn)行全局配置:
mybatis-plus:
global-config:
banner: false
db-config:
id-type: assign_id
# 邏輯刪除字段為status
logic-delete-field: status
# 刪除狀態(tài)的值
logic-delete-value: 0
# 未刪除狀態(tài)的值
logic-not-delete-value: 1
使用全局配置后就不用使用@TableLogic注解了。
通用枚舉
假如要表示性別:只有男和女兩個值,我們就可以使用枚舉來描述。
數(shù)據(jù)庫表中使用gender (int 類型)表示性別,0表示女性,1表示男性。
使用@EnumValue來標(biāo)注將哪個變量的值插入到數(shù)據(jù)庫。
先創(chuàng)建枚舉類
public enum GenderEnum {
MAN(1,"男"),WOMAN(0,"女");
@EnumValue // 表示將這個變量的值插入到數(shù)據(jù)庫
private Integer gender;
private String genderName;
GenderEnum(Integer gender, String genderName) {
this.gender = gender;
this.genderName = genderName;
}
}給Pojo類添加枚舉屬性
public class User extends Model<User> {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
// 配置當(dāng)前字段為邏輯刪除字段
// 默認(rèn)值是1,刪除狀態(tài)的值是0
@TableLogic(value = "1",delval = "0")
private Integer status;
private GenderEnum gender;
}調(diào)用正常的插入方法即可實(shí)現(xiàn)。
字段類型處理器
某些場景下,實(shí)體類中使用map集合作為屬性接收前端傳來的數(shù)據(jù),但是把這些輸出存到數(shù)據(jù)庫時,使用json格式的字符串存儲。那怎么把map類型轉(zhuǎn)換成字符串類型呢?這里就需要使用字段類型處理器。
需要@TableName注解和@TableField注解配合使用。
實(shí)體類代碼如下:
// autoResultMap = true 表示查詢時,自動將contact字段json格式的字符串轉(zhuǎn)換為Map類型
@TableName(autoResultMap = true)
public class User extends Model<User> {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
// 配置當(dāng)前字段為邏輯刪除字段
// 默認(rèn)值是1,刪除狀態(tài)的值是0
@TableLogic(value = "1",delval = "0")
private Integer status;
private GenderEnum gender;
// 指定類型處理器,在新增或更新時,自動將Map類型轉(zhuǎn)換成json格式的字符串
// 這個處理器依賴于Fastjson,所以要在pom文件中引入Fastjson依賴
@TableField(typeHandler = FastjsonTypeHandler.class)
private Map<String, String> contact; // 聯(lián)系方式
}
自動填充
在實(shí)際應(yīng)用中,有一些屬性,其實(shí)不需要我們每次都手動填充,可以設(shè)置為自動填充,比如創(chuàng)建時間、更新時間等可以設(shè)置為自動填充。
注意時區(qū)的設(shè)置:
- mysql數(shù)據(jù)庫設(shè)置時區(qū):set global time_zone = '+8:00'
查看時區(qū)對不對?執(zhí)行select now() 看時間能不能對上。
- 項(xiàng)目中的時區(qū)設(shè)置:在數(shù)據(jù)庫連接Url中設(shè)置。
url: jdbc:mysql://localhost:3306/spring6?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&useUnicode=true
把serverTimezone=UTC改為serverTimezone=Asia/Shanghai。
- 使用@TableField注解設(shè)置填充時機(jī)
// autoResultMap = true 表示查詢時,自動將contact字段json格式的字符串轉(zhuǎn)換為Map類型
@TableName(autoResultMap = true)
public class User extends Model<User> {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
// 配置當(dāng)前字段為邏輯刪除字段
// 默認(rèn)值是1,刪除狀態(tài)的值是0
@TableLogic(value = "1",delval = "0")
private Integer status;
private GenderEnum gender;
// 指定類型處理器,在新增或更新時,自動將Map類型轉(zhuǎn)換成json格式的字符串
// 這個處理器依賴于Fastjson,所以要在pom文件中引入Fastjson依賴
@TableField(typeHandler = FastjsonTypeHandler.class)
private Map<String, String> contact; // 聯(lián)系方式
// 指定插入時填充
@TableField(fill = FieldFill.INSERT)
private Data creatTime;
// 指定插入或更新時填充
@TableField(fill = FieldFill.INSERT_UPDATE)
private Data modifyTime;
}
編寫自定義處理器設(shè)置填充策略
//自定義Handler,設(shè)置填充策略,這里需要實(shí)現(xiàn)MetaObjectHandler接口
@Component
public class MyMetaHandler implements MetaObjectHandler {
// 插入時的填充策略
@Override
public void insertFill(MetaObject metaObject) {
setFieldValByName("creatTime",new Date(),metaObject);
setFieldValByName("modifyTime",new Date(),metaObject);
}
// 更新時的填充策略
@Override
public void updateFill(MetaObject metaObject) {
setFieldValByName("modifyTime",new Date(),metaObject);
}
}此時在新增或更新時,無需手動設(shè)置創(chuàng)建時間更新時間,系統(tǒng)會自動填充時間到數(shù)據(jù)庫表中。
防止全表更新與刪除插件
配置攔截規(guī)則:插件默認(rèn)攔截沒有指定條件的 update 和 delete 語句。
當(dāng)觸發(fā)攔截時,會拋出MyBatisPlusException異常。
通過在MybatisPlusConfig 這個配置類中加入對應(yīng)的攔截器來阻止全表更新與刪除:
@Configuration
@MapperScan("com.ali.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 防止全表更新與刪除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多個插件, 切記分頁最后添加
// 如果有多數(shù)據(jù)源可以不配具體類型, 否則都建議配上具體的 DbType
return interceptor;
}
}
MyBatisX快速開發(fā)插件
MyBatisX是一款idea提供的插件,目的是為了簡化MyBatis和MyBatis-plus框架。
- MyBatisX先安裝插件:
- File -- > Settings -->Plugins -- >搜索 MyBatisX,然后安裝
- MyBatisX插件的快速定位:
- 這個插件把Mapper接口的方法和映射文件的sql進(jìn)行了一一對應(yīng)??梢詮腗apper接口方法快速跳到對應(yīng)的映射文件對應(yīng)的sql。

同樣,也可以在映射文件點(diǎn)擊小鳥快速跳轉(zhuǎn)到對應(yīng)的Mapper接口
- MyBatisX插件的逆向工程:
- 通過數(shù)據(jù)庫表,快速生成以下項(xiàng)目文件:
- 實(shí)體類
- Mapper接口
- Mapper映射文件
- Service接口
- Service實(shí)現(xiàn)類
- 首先使用idea連接數(shù)據(jù)庫:

2. 選擇對應(yīng)的數(shù)據(jù)庫表,然后右鍵選擇MyBatisX-generate

- 最后一步設(shè)置

這樣就生成了對應(yīng)的項(xiàng)目文件。注意這里的Mapper接口文件要手動加上@Mapper注解。
樂觀鎖
并發(fā)請求就是在同一時刻有多個請求去請求同一個服務(wù)器資源。如果是獲取信息,不會出現(xiàn)問題,但是如果做修改操作,就會出現(xiàn)并發(fā)問題。
比如:三個人去買同樣的商品,商品剩余一件。購買時一般先查詢庫存再購買后數(shù)量減一,并發(fā)請求就是同一時刻,三個人都查到了商品剩余1個,然后同時進(jìn)行購買。這樣只能一個人買到,另外兩人肯定買不到,此時就發(fā)生了超賣行為。這就是經(jīng)典的并發(fā)問題。
常見的數(shù)據(jù)庫鎖類型有兩種,悲觀鎖和樂觀鎖:
悲觀鎖:查詢時就鎖定數(shù)據(jù),在請求完成之前不會釋放鎖。完成后才釋放鎖。釋放鎖以后,其他請求才可以對數(shù)據(jù)進(jìn)行讀寫。
這樣雖解決了并發(fā)問題,但是效率較低。實(shí)際開發(fā)中很少使用悲觀鎖。
樂觀鎖:通過表字段完成設(shè)計(jì)。樂觀鎖的核心思想是:在讀取的時候不加鎖,其他請求仍可以讀取這個數(shù)據(jù),在修改的時候,判斷一個數(shù)據(jù)是否有被修改過,如果修改過,那本次請求的修改操作失效。
具體設(shè)計(jì)如下:
增加一個字段version。購買商品時先查詢,此時能獲取到version的值。
在執(zhí)行購買操作時,更新庫存數(shù)量前再查詢一次version的值,如果兩次的version值一樣,表示可以進(jìn)行更新庫存操作,更新時進(jìn)行 version = version +1,表示我執(zhí)行了一次。這樣就完成了樂觀鎖的操作。
如果兩次的version 值不一致,說明有人對庫存數(shù)據(jù)進(jìn)行了更新,此時不能直接進(jìn)行購買。需要重新進(jìn)行先查詢后購買的操作。
MyBatis-plus中樂觀鎖實(shí)現(xiàn)步驟如下:
在實(shí)體類中使用@Version注解指定版本字段
// autoResultMap = true 表示查詢時,自動將contact字段json格式的字符串轉(zhuǎn)換為Map類型
@TableName(autoResultMap = true)
public class User extends Model<User> {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
// 配置當(dāng)前字段為邏輯刪除字段
// 默認(rèn)值是1,刪除狀態(tài)的值是0
@TableLogic(value = "1",delval = "0")
private Integer status;
private GenderEnum gender;
// 指定類型處理器,在新增或更新時,自動將Map類型轉(zhuǎn)換成json格式的字符串
// 這個處理器依賴于Fastjson,所以要在pom文件中引入Fastjson依賴
@TableField(typeHandler = FastjsonTypeHandler.class)
private Map<String, String> contact; // 聯(lián)系方式
// 指定插入時填充
@TableField(fill = FieldFill.INSERT)
private Data creatTime;
// 指定插入或更新時填充
@TableField(fill = FieldFill.INSERT_UPDATE)
private Data modifyTime;
@Version // 表示將此字段作為版本信息
private Integer version;// 版本
}
在MybatisPlusConfig配置類中加入樂觀鎖攔截器
@Configuration
@MapperScan("com.ali.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 樂觀鎖攔截器
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防止全表更新與刪除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多個插件, 切記分頁最后添加
// 如果有多數(shù)據(jù)源可以不配具體類型, 否則都建議配上具體的 DbType
return interceptor;
}
}
測試代碼:
// 第一次查詢:執(zhí)行成功 version是1 User user1 = userService.getById(1L); // 第二次查詢:執(zhí)行成功 version是1 User user5 = userService.getById(1L); user1.setAge(12); // 第一次更新:執(zhí)行成功,因?yàn)樵俅潍@取到version還是1,和上次保持一致。 // 更新后 version +1 此時version是2 userService.updateById(user1); user5.setAge(22); // 第二次更新:執(zhí)行失敗,因?yàn)樵俅潍@取到version的值是2,和上次獲取的值不一樣,不執(zhí)行更新操作。 userService.updateById(user5);
代碼生成器
代碼生成器和逆向功能的區(qū)別在于,代碼生成器可以生成更多的結(jié)構(gòu),更多的內(nèi)容,允許配置更多的內(nèi)容。
具體步驟如下:
引入相關(guān)依賴
<!--代碼生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<!--freemarker 模板依賴-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>編寫生成類(可以在mybatis-plus官網(wǎng)直接copy):
public class CodeGeneratorTest {
public static void main(String[] args) {
// 使用 FastAutoGenerator 快速配置代碼生成器
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8", "root", "password")
.globalConfig(builder -> {
builder.author("Your Name") // 設(shè)置作者
.outputDir("src/main/java"); // 輸出目錄
})
.packageConfig(builder -> {
builder.parent("com.example") // 設(shè)置父包名
.entity("model") // 設(shè)置實(shí)體類包名
.mapper("dao") // 設(shè)置 Mapper 接口包名
.service("service") // 設(shè)置 Service 接口包名
.serviceImpl("service.impl") // 設(shè)置 Service 實(shí)現(xiàn)類包名
.xml("mappers"); // 設(shè)置 Mapper XML 文件包名
})
.strategyConfig(builder -> {
builder.addInclude("table1", "table2") // 設(shè)置需要生成的表名
.entityBuilder()
.enableLombok() // 啟用 Lombok
.enableTableFieldAnnotation() // 啟用字段注解
.controllerBuilder()
.enableRestStyle(); // 啟用 REST 風(fēng)格
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用 Freemarker 模板引擎
.execute(); // 執(zhí)行生成
}
}執(zhí)行sql打印分析
通過sql分析來獲取sql語句的執(zhí)行時間。
具體步驟如下:
由于該功能依賴于p6spy組件,p6spy是一個強(qiáng)大的工具,它為MyBatis-Plus用戶提供了便捷的SQL分析與打印功能。通過合理配置,你可以在開發(fā)和測試階段有效地監(jiān)控和優(yōu)化SQL語句。然而,由于性能損耗,建議在生產(chǎn)環(huán)境中謹(jǐn)慎使用。所以先引入依賴:
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>在application.yml中修改配置
spring:
datasource:
username: root
password: root
url: jdbc:p6spy:mysql://localhost:3306/spring6?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&useUnicode=true
driver-class-name: com.p6spy.engine.spy.P6SpyDriver在resource下,創(chuàng)建spy.properties配置文件
# 模塊列表,根據(jù)版本選擇合適的配置 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定義日志格式 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger # 日志輸出到控制臺 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 取消JDBC驅(qū)動注冊 deregisterdrivers=true # 使用前綴 useprefix=true # 排除的日志類別 excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 實(shí)際驅(qū)動列表 # driverlist=org.h2.Driver # 開啟慢SQL記錄 outagedetection=true # 慢SQL記錄標(biāo)準(zhǔn)(單位:秒) outagedetectioninterval=2 # 過濾 flw_ 開頭的表 SQL 打印 filter=true exclude=flw_*
多數(shù)據(jù)源
先引入依賴
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>dynamic-datasource 是一個開源的 Spring Boot 多數(shù)據(jù)源啟動器,提供了豐富的功能,包括數(shù)據(jù)源分組、敏感信息加密、獨(dú)立初始化表結(jié)構(gòu)等
配置數(shù)據(jù)源
spring:
datasource:
dynamic:
# 默認(rèn)數(shù)據(jù)源名為 master,可通過 spring.datasource.dynamic.primary 修改。
primary: master
strict: false
datasource:
master:
url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave_1:
url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave_2:
url: ENC(xxxxx)
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.jdbc.Driver使用 @DS 切換數(shù)據(jù)源:
@Service
@DS("master")
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
@DS("slave_1")
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}到此這篇關(guān)于MyBatis-plus字段類型處理器、自動填充和樂觀鎖詳解的文章就介紹到這了,更多相關(guān)MyBatis-plus字段類型處理器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
將Java(SpringBoot)項(xiàng)目打包為Docker鏡像的三種方法
這篇文章主要介紹了將Java(SpringBoot)項(xiàng)目打包為Docker鏡像的三種方法,分別是手動構(gòu)建、使用Dockerfile和使用SpringBootMaven插件,每種方法都有其特點(diǎn)和適用場景,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-03-03
Spring?Boot集成Redisson實(shí)現(xiàn)延遲隊(duì)列
本文詳細(xì)介紹了電商支付場景中利用Redisson的RDelayedQueue實(shí)現(xiàn)訂單的延遲關(guān)閉功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-08-08
Springboot mybatisplus如何解決分頁組件IPage失效問題
這篇文章主要介紹了Springboot mybatisplus如何解決分頁組件IPage失效問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
SpringBoot中處理的轉(zhuǎn)發(fā)與重定向方式
這篇文章主要介紹了SpringBoot中處理的轉(zhuǎn)發(fā)與重定向方式,分別就轉(zhuǎn)發(fā)和重定向做了概念解說,結(jié)合示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-11-11
Spring Task定時任務(wù)的完整使用教學(xué)
Spring Task是Spring框架提供的輕量級定時任務(wù)工具,它無需依賴額外的第三方庫,直接集成在Spring核心包中,在SpringBoot中,Spring Task提供了自動配置支持,只需少量注解即可快速實(shí)現(xiàn)定時任務(wù)功能,本文給大家介紹了Spring Task定時任務(wù)的完整使用教學(xué)2026-06-06

