欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MybatisPlus?構(gòu)造器wrapper的使用與原理解析

 更新時(shí)間:2024年05月10日 11:48:54   作者:戰(zhàn)斧  
本次我們介紹了MybatisPlus?構(gòu)造器wrapper的使用方式及其易錯(cuò)點(diǎn),同時(shí)也針對(duì)其運(yùn)行的原理進(jìn)行了解釋,只有深刻理解了它的原理,我們才能更靈活的使用,并且更快的排查出問(wèn)題,感興趣的朋友跟隨小編一起看看吧

上一次我們給大家講解了何為MybatisPlus,我們使用它的目的以及它的主要特性。不過(guò),對(duì)于一線開(kāi)發(fā)而言,如何在代碼中使用上它的特性才是重中之重,本期我們就來(lái)好好講一下 MybatisPlus 中的條件構(gòu)造器

一、構(gòu)造器的分類

我們還是使用一張老圖來(lái)說(shuō)明

  • 構(gòu)造器都有一個(gè)核心父類= AbstractWrapper =,其他的構(gòu)造器都是它的子類,現(xiàn)在兩種分類方式,一種分類是用途;
  • 查詢更新 構(gòu)造器,另一種則是按使用方式,即一般lambda 構(gòu)造器。
  • 兩種分類交錯(cuò),最后我們就看到了四個(gè)構(gòu)造器實(shí)現(xiàn)類。

1. AbstractWrapper 的作用

作為所有條件構(gòu)造器的父類,AbstractWrapper 肩負(fù)著絕大部分的功能,來(lái)幫助我們實(shí)現(xiàn)各類復(fù)雜的SQL. 它實(shí)現(xiàn)了下面幾個(gè)接口:

Compare
-定義了一組方法用于比較操作,包括等于(eq)、不等于(ne)、大于(gt)

Nested
-定義了一組方法用于構(gòu)建嵌套條件,即在查詢條件中可以使用括號(hào)包裹的子條件

Join
-用于實(shí)現(xiàn)表的關(guān)聯(lián)查詢,通過(guò)指定關(guān)聯(lián)條件、連接方式,可以將多個(gè)表的數(shù)據(jù)進(jìn)行關(guān)聯(lián)查詢。

Func
-定義了一組方法用于構(gòu)建SQL函數(shù)表達(dá)式,包括COUNT、SUM、AVG、MAX、MIN等函數(shù)

2. 普通構(gòu)造器與lambda構(gòu)造器

我們首先看帶不帶lambda有什么區(qū)別,其實(shí)兩者幾乎一致,我們以查詢?yōu)槔?,也就是?duì)比一下LambdaQueryWrapperQueryWrapper ,不難發(fā)現(xiàn),兩者的差異重點(diǎn)是因?yàn)閮扇藢?shí)現(xiàn)Query接口的定義不一樣

普通的構(gòu)造器實(shí)現(xiàn)query,定義了只能使用String,而Lambda構(gòu)造器的入?yún)t是允許是一個(gè)function函數(shù)。這樣的區(qū)別,將導(dǎo)致兩種不同的寫(xiě)法,見(jiàn)如下:

// 普通構(gòu)造器
        QueryWrapper<CsdnUserInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("is_delete", 0);
        wrapper.orderByDesc("user_weight");
        return this.list(wrapper);
// lambda構(gòu)造器
        LambdaQueryWrapper<CsdnUserInfo> lambdaWrapper = new LambdaQueryWrapper<>();
        lambdaWrapper.eq(CsdnUserInfo::getIsDelete, 0);
        lambdaWrapper.orderByDesc(CsdnUserInfo::getUserWeight);
        return this.list(lambdaWrapper);

普通構(gòu)造器在使用時(shí),字段名只能直接寫(xiě)字符串,而lambda構(gòu)造器只能寫(xiě)方法。因?yàn)橹苯訉?xiě)字符串出錯(cuò)了不容易發(fā)現(xiàn),因此推薦大家還是盡量使用lambda構(gòu)造器,以方便在編碼階段就減少錯(cuò)漏可能性

3. query構(gòu)造器與update構(gòu)造器

首先兩者因?yàn)槎祭^承了 AbstractWrapper 的,所以大部分的SQL功能兩者是都具備的,比如SQL中where子句、join、排序那些東西。兩者最大的區(qū)別是它們分別實(shí)現(xiàn)了兩個(gè)不同的接口,一個(gè)繼承Query,一個(gè)繼承Update

不難看出,兩者核心的不同就是語(yǔ)法的區(qū)別,查詢哪些字段或更新哪些字段

二、使用方式

關(guān)于構(gòu)造器的基本方法,內(nèi)容非常多,我們不再做搬運(yùn)工,大家可以直接去下面的官網(wǎng)看方法的文檔:條件構(gòu)造器文檔

1. 基礎(chǔ)使用

我們以舉例說(shuō)明,比方說(shuō)現(xiàn)在我們想要實(shí)現(xiàn)一個(gè)查詢功能,根據(jù)以下條件來(lái)獲取學(xué)生的信息:

性別為男性; 年齡在18到20歲之間; 成績(jī)大于80分; 班級(jí)為A班,然后按成績(jī)由高到底排

使用QueryWrapper來(lái)實(shí)現(xiàn)這個(gè)查詢的代碼如下

QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("gender", "男性")
        .between("age", 18, 20)
        .gt("score", 80)
        .eq("class", "A班")
        .orderByDesc("score");
List<Student> students = studentMapper.selectList(queryWrapper);

又比如說(shuō) 我們想將姓名為"張三"的學(xué)生年齡更新為20。則可以使用UpdateWrapper實(shí)現(xiàn)

String name = "張三";
Integer newAge = 20;
UpdateWrapper<Student> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", name)
             .set("age", newAge);
int result = studentMapper.update(null, updateWrapper);

2. 易錯(cuò)點(diǎn)-邏輯范圍

由于條件構(gòu)造器的語(yǔ)法非常符合自然語(yǔ)言,所以有的時(shí)候反而讓人疏忽,我們?nèi)耘e一個(gè)例子,比如我們想獲取這樣的用戶

用戶狀態(tài)為有效的,名字為 張三 或 李四 或 王五 的用戶

你可能想當(dāng)然寫(xiě)成如下的樣子

// 錯(cuò)誤寫(xiě)法
Set<String> set = new HashSet<>();
set.add("zhangsan");
set.add("lisi");
set.add("wangwu");
LambdaQueryWrapper<CsdnUserInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CsdnUserInfo::getIsDelete, 0);
for (String name : set) {
    wrapper.or(item -> item.eq(CsdnUserInfo::getUserName, name));
}
return this.list(wrapper);

但事實(shí)上,最終預(yù)編譯產(chǎn)生的SQL是這樣的:

SELECT id,user_name,nick_name,like_status,collect_status,comment_status,user_weight,user_home_url,curr_blog_url,article_type,create_time,update_time,is_delete FROM csdn_user_info WHERE (is_delete = ? OR (user_name = ?) OR (user_name = ?) OR (user_name = ?))

不難發(fā)現(xiàn)這樣我們用戶狀態(tài)篩選 和 用戶名篩選 用 or 關(guān)聯(lián)起來(lái)了,這樣其實(shí)就會(huì)查出所有有效的用戶,不符合我們的預(yù)期。產(chǎn)生這種問(wèn)題的原因,其實(shí)是少用了個(gè)括號(hào)。我們應(yīng)該把用戶名的 or 限制在一個(gè)括號(hào)內(nèi),不能讓它擴(kuò)散出去。

此時(shí)我們可以使用 nested 來(lái)修復(fù)這個(gè)問(wèn)題,把我們的for循環(huán)扔進(jìn) nested

Set<String> set = new HashSet<>();
set.add("zhangsan");
set.add("lisi");
set.add("wangwu");
wrapper.eq(CsdnUserInfo::getIsDelete, 0);
wrapper.nested(wp -> {
            for (String name : set) {
                wp.or(item -> item.eq(CsdnUserInfo::getUserName, name));
            }
});
return this.list(wrapper);

這樣最后的編譯的SQL是這樣的

SELECT id,user_name,nick_name,like_status,collect_status,comment_status,user_weight,user_home_url,curr_blog_url,article_type,create_time,update_time,is_delete FROM csdn_user_info WHERE (is_delete = ? AND ((user_name = ?) OR (user_name = ?) OR (user_name = ?)))

3. 易錯(cuò)點(diǎn)-null處理

使用 MybatisPlus 還有一個(gè)坑點(diǎn)容易被忽略,那就是 null 值的處理,比如我們?cè)诓迦牖蚋乱粭l數(shù)據(jù)時(shí),如果我們對(duì)象內(nèi)某一個(gè)字段為null,這個(gè)null值可能不會(huì)插入或更新進(jìn)表中。如果 null 在你的表中有業(yè)務(wù)意義,此刻就格外需要注意了。

比如我們想把張三的昵稱置為空,寫(xiě)了這么一段代碼

CsdnUserInfo user = new CsdnUserInfo();
user.setId(99999);
user.setUserName("張三");
user.setNickName(null);
csdnUserInfoService.updateById(user);

但我們的目的是不會(huì)生效的,因?yàn)榇藭r(shí) NickName 根本不會(huì)更新,它的SQL是這樣的

UPDATE csdn_user_info SET user_name=? WHERE id=?

此時(shí)我們可以在實(shí)體類上加一個(gè)注解 @TableField(updateStrategy= FieldStrategy.IGNORED) 這是因?yàn)閷?duì)字段的變更默認(rèn)會(huì)有空值校驗(yàn),只有顯示的指定為 忽略校驗(yàn) 才能把空值更新進(jìn)表中

經(jīng)過(guò)這樣的改動(dòng),我們?cè)賮?lái)看看編譯的SQL

UPDATE csdn_user_info SET user_name=?, nick_name=? WHERE id=?

nick_name 的 null 就可以成功更新進(jìn)表中了。

三、生效原理

如果你看過(guò)我們之前的《springboot基于Mybatis mysql實(shí)現(xiàn)讀寫(xiě)分離》,就不難理解,我們其實(shí)可以把整個(gè) MybatisPlus 分為項(xiàng)目啟動(dòng)階段做的準(zhǔn)備階段,和真正要執(zhí)行某段SQL的執(zhí)行階段。準(zhǔn)備階段就是把我們寫(xiě)在xml的SQL進(jìn)行解析,構(gòu)造出一個(gè)個(gè)映射聲明(Mappedstatement),里面包含了我們的SQL主體。執(zhí)行階段則是通過(guò)方法全稱找到自己的映射聲明(Mappedstatement),對(duì)其進(jìn)行拼接形成真正的可執(zhí)行SQL。

那在MybatisPlus下,我們明明沒(méi)寫(xiě)SQL,SQL又是從哪來(lái)的呢?

正如上面說(shuō)的,比如一個(gè)簡(jiǎn)單的插入,我們沒(méi)有寫(xiě)SQL,甚至連xml文件都沒(méi)有,而是直接使用的BaseMapper 中的 insert 方法。

所以在啟動(dòng)的時(shí)候,如下面的 service其實(shí)就在做準(zhǔn)備工作了,如果說(shuō)我們以前的xml文件寫(xiě)了現(xiàn)成的SQL語(yǔ)句,從而解析XML文件內(nèi)容,產(chǎn)生mappedStatement,
而像下面這種service其實(shí)也是這樣,不過(guò)他多了一步,它得先把service翻譯成xml格式,然后再來(lái)轉(zhuǎn)成mappedStatement。

@Service
public class AlgorithmicProblemServiceImpl extends ServiceImpl<AlgorithmicProblemMapper, AlgorithmicProblem> implements AlgorithmicProblemService {
}

就拿上面的 insert 舉例,MybatisPlus 中有一個(gè) insert 類,即 com.baomidou.mybatisplus.core.injector.methods.Insert 其中就有關(guān)鍵方法

1. 獲取語(yǔ)法模板SQL

所謂獲取模板SQL,其實(shí)就是在MybatisPlus中內(nèi)置了很多方法的基本語(yǔ)法,這些內(nèi)容只要在填上對(duì)應(yīng)的表名、字段名就能構(gòu)成一個(gè)基礎(chǔ) SQL 的輪廓了

還是以我們的插入語(yǔ)句為例,比如我們想插入一條數(shù)據(jù),它的基礎(chǔ)語(yǔ)法模板就是

"<script>\nINSERT INTO %s %s VALUES %s\n</script>"

其中三個(gè) ‘%s’ 就是待定的表名和字段名信息,將在后面代入表信息

2. 代入表及字段信息

我們上面看到了這樣一句話,它的作用就是根據(jù)語(yǔ)法模板,和表信息,構(gòu)造出一個(gè)基礎(chǔ)的SQL

String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);

上面的第一個(gè)入?yún)?sqlMethod.getSql() 就是我們上面說(shuō)的語(yǔ)法模板,第二個(gè)入?yún)⑹潜砻退淖侄蝿t是根據(jù)字段名拼出來(lái)的SQL,表名的話,我們?cè)趯?shí)體類里已經(jīng)寫(xiě)了,即 @TableName 注解后面的內(nèi)容

@Data
@ApiModel("算法題實(shí)體類")
@TableName("algorithmic_problem")
public class AlgorithmicProblem extends Model<AlgorithmicProblem> {
    @ApiModelProperty("主鍵id")
    private Integer id;
    @ApiModelProperty("問(wèn)題名稱")
    private String questionName;
    @ApiModelProperty("問(wèn)題類型")
    private String questionType;
    @ApiModelProperty("1~10的分值")
    private Integer degreeOfImportance;
    @ApiModelProperty("1:簡(jiǎn)單;2:中等;3:困難")
    private Integer degreeOfDifficulty;
    @ApiModelProperty("困難指數(shù)")
    private Integer difficultyOfScore;
    @ApiModelProperty("力扣的問(wèn)題號(hào)")
    private Integer leetcodeNumber;
    @ApiModelProperty("力扣的問(wèn)題鏈接")
    private String leetcodeLink;
    @ApiModelProperty("標(biāo)簽")
    private String tag;
    @ApiModelProperty("創(chuàng)建時(shí)間")
    private Date createTime;
    @ApiModelProperty("邏輯刪除,0未刪除,1已刪除")
    private Integer isDelete;
}

而后面的 columnScript, valuesScript 則是根據(jù)我們的字段值,生成的一些腳本,其中包含了我們以前在mybatis 的 xml 文件中會(huì)寫(xiě)的動(dòng)態(tài)標(biāo)簽,這些標(biāo)簽的作用可以參考此片文章《數(shù)據(jù)庫(kù)操作不再困難,MyBatis動(dòng)態(tài)Sql標(biāo)簽解析

通過(guò)上面的操作,不難發(fā)現(xiàn),我們以前在XML文件里需要寫(xiě)的 SQL 在此刻就被拼接出來(lái)了,它們形式是一樣的。需要注意的是,這里的字段全都是非空才會(huì)插入的。

<script>
INSERT INTO algorithmic_problem <trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if><if test="questionName != null">question_name,</if>
<if test="questionType != null">question_type,</if>
<if test="degreeOfImportance != null">degree_of_importance,</if>
<if test="degreeOfDifficulty != null">degree_of_difficulty,</if>
<if test="difficultyOfScore != null">difficulty_of_score,</if>
<if test="leetcodeNumber != null">leetcode_number,</if>
<if test="leetcodeLink != null">leetcode_link,</if>
<if test="tag != null">tag,</if>
<if test="createTime != null">create_time,</if>
<if test="isDelete != null">is_delete,</if>
</trim> VALUES <trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id},
</if><if test="questionName != null">#{questionName},</if>
<if test="questionType != null">#{questionType},</if>
<if test="degreeOfImportance != null">#{degreeOfImportance},</if>
<if test="degreeOfDifficulty != null">#{degreeOfDifficulty},</if>
<if test="difficultyOfScore != null">#{difficultyOfScore},</if>
<if test="leetcodeNumber != null">#{leetcodeNumber},</if>
<if test="leetcodeLink != null">#{leetcodeLink},</if>
<if test="tag != null">#{tag},</if>
<if test="createTime != null">#{createTime},</if>
<if test="isDelete != null">#{isDelete},</if>
</trim>
</script>

3. 代入條件構(gòu)造器邏輯

上面的部分對(duì)于簡(jiǎn)單的 insert來(lái)說(shuō),其實(shí)已經(jīng)夠用了,但是對(duì)于一些用戶的查詢或修改邏輯,比如我們?cè)?servece 中寫(xiě)的那些篩選條件,排序等,它們又是如何起作用的呢?這里我們要分兩個(gè)階段來(lái)說(shuō):啟動(dòng)階段、執(zhí)行階段

1. 啟動(dòng)階段的 “ew” 參數(shù)

首先,我們?cè)诖a中寫(xiě)的所有wrapper(條件構(gòu)造器),這些wrapper包含了篩選條件,排序規(guī)則之類的,其實(shí)最終都被視為一個(gè)參數(shù)“ew”(Entity Wrapper)放入BaseMapper中進(jìn)行操作

// service 層
 @Override
public List<CsdnUserInfo> allUser() {
     QueryWrapper<CsdnUserInfo> wrapper = new QueryWrapper<>();
     wrapper.eq("is_delete", 0);
     wrapper.orderByDesc("user_weight");
     return this.list(wrapper);
}
// IService 類
default List<T> list(Wrapper<T> queryWrapper) {
     return this.getBaseMapper().selectList(queryWrapper);
}
// BaseMapper 類
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

這個(gè)ew參數(shù)在下面就是核心,我們上面提到了,在應(yīng)用啟動(dòng)階段,經(jīng)過(guò)基本語(yǔ)法和表的代入后,可以形成一些基礎(chǔ)SQL腳本。而對(duì)于像selectList 這種,會(huì)帶有ew參數(shù)的,他們的基礎(chǔ)SQL就會(huì)比較復(fù)雜了,它們會(huì)在SQL中大量預(yù)留ew參數(shù)相關(guān)的內(nèi)容。比如selectList 最終就會(huì)生成如下的一大串腳本:

<script>
<if test="ew != null and ew.sqlFirst != null">
    ${ew.sqlFirst}
</if> 
SELECT 
<choose>
	<when test="ew != null and ew.sqlSelect != null">
      ${ew.sqlSelect}
    </when>
<otherwise>id,user_name,nick_name,like_status,collect_status,comment_status,user_weight,user_home_url,curr_blog_url,article_type,create_time,update_time,is_delete</otherwise>
</choose> FROM csdn_user_info 
	<if test="ew != null">
		<where>
			<if test="ew.entity != null">
				<if test="ew.entity.id != null">id=#{ew.entity.id}</if>
				<if test="ew.entity['userName'] != null"> AND user_name=#{ew.entity.userName}</if>
				<if test="ew.entity['nickName'] != null"> AND nick_name=#{ew.entity.nickName}</if>
				<if test="ew.entity['likeStatus'] != null"> AND like_status=#{ew.entity.likeStatus}</if>
				<if test="ew.entity['collectStatus'] != null"> AND collect_status=#{ew.entity.collectStatus}</if>
				<if test="ew.entity['commentStatus'] != null"> AND comment_status=#{ew.entity.commentStatus}</if>
				<if test="ew.entity['userWeight'] != null"> AND user_weight=#{ew.entity.userWeight}</if>
				<if test="ew.entity['userHomeUrl'] != null"> AND user_home_url=#{ew.entity.userHomeUrl}</if>
				<if test="ew.entity['currBlogUrl'] != null"> AND curr_blog_url=#{ew.entity.currBlogUrl}</if>
				<if test="ew.entity['articleType'] != null"> AND article_type=#{ew.entity.articleType}</if>
				<if test="ew.entity['createTime'] != null"> AND create_time=#{ew.entity.createTime}</if>
				<if test="ew.entity['updateTime'] != null"> AND update_time=#{ew.entity.updateTime}</if>
				<if test="ew.entity['isDelete'] != null"> AND is_delete=#{ew.entity.isDelete}</if>
			</if>
			<if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.nonEmptyOfWhere">
				<if test="ew.nonEmptyOfEntity and ew.nonEmptyOfNormal"> AND </if>
				 ${ew.sqlSegment}
			</if>
		</where>
		<if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.emptyOfWhere">
          ${ew.sqlSegment}
        </if>
	</if>
<if test="ew != null and ew.sqlComment != null">
   ${ew.sqlComment}
</if>
</script>

這里面,我們看到這段SQL中,涉及ew參數(shù)的有這么幾個(gè)非常重要的元素:

  • ew.sqlFirst :表示SQL片段,可以自定義SQL片段放在SQL的最開(kāi)始位置
  • ew.sqlSelect:表示要查詢的字段,默認(rèn)為"*",即查詢所有字段。可以通過(guò)該屬性指定要查詢的字段,多個(gè)字段之間用逗號(hào)分隔
  • ew.entity:表示要查詢的實(shí)體對(duì)象??梢酝ㄟ^(guò)該屬性指定要查詢的實(shí)體對(duì)象,可以根據(jù)實(shí)體對(duì)象的屬性進(jìn)行條件封裝
  • ew.sqlSegment:表示SQL片段,可以自定義SQL片段,用于在動(dòng)態(tài)SQL中添加自定義的SQL語(yǔ)句
  • ew.sqlComment:表示SQL注釋,可以為SQL語(yǔ)句添加注釋??梢酝ㄟ^(guò)該屬性指定要為SQL語(yǔ)句添加的注釋內(nèi)容。

2. 執(zhí)行階段的 “ew” 參數(shù)

以我們上面提到過(guò)的代碼為例

public List<CsdnUserInfo> allUser() {
     QueryWrapper<CsdnUserInfo> wrapper = new QueryWrapper<>();
     wrapper.eq("is_delete", 0);
     wrapper.orderByDesc("user_weight");
     return this.list(wrapper);
}

這樣的 wrapper 最終會(huì)形成這樣一個(gè)對(duì)象,我們?yōu)樗砑恿藘蓚€(gè)屬性,即

有些眼尖的同學(xué)發(fā)現(xiàn)了,xml中用的是 ew.sqlSegment, 而我們這里的條件全部都進(jìn)了 ew.expression 中,這兩也沒(méi)對(duì)上啊。其實(shí)在條件構(gòu)造器中 ew.sqlSegment = expression.getSqlSegment + lastSql

// AbstractWrapper
public String getSqlSegment() {
     return this.expression.getSqlSegment() + this.lastSql.getStringValue();
}

而 expression.getSqlSegment() 又是其下的各個(gè)小 Segment 拼出來(lái)的

this.sqlSegment = this.normal.getSqlSegment() + this.groupBy.getSqlSegment() + this.having.getSqlSegment() + this.orderBy.getSqlSegment();

如此一來(lái),利用ognl,篩選條件也被我們拼湊出來(lái)了,最后拼接出 ew.sqlSegment 實(shí)際解析成為了一個(gè)字符串

(is_delete = #{ew.paramNameValuePairs.MPGENVAL1}) ORDER BY user_weight DESC

這個(gè)時(shí)候,又有眼尖的同學(xué)發(fā)現(xiàn)了,我們?cè)诖a中明明已經(jīng)寫(xiě)死了 is_delete = 0,為什么解析完卻成了 is_delete = #{ew.paramNameValuePairs.MPGENVAL1}

實(shí)際上這里有一個(gè)兼容的作用,我們可以在業(yè)務(wù)代碼里寫(xiě) wrapper.eq(“is_delete”, 0),也可以填上一個(gè)未知的變量 wrapper.eq(“is_delete”, var1),我們都知道為了防止SQL注入,實(shí)際上對(duì)于入?yún)⑽覀兌际窃谧詈蟛盘峤贿M(jìn)SQL中的,不可能因?yàn)槟闶菍?xiě)死的0,就提前把0填寫(xiě)在這里。所以,此處會(huì)先把你填的東西存放在 paramNameValuePairs 參數(shù)鍵值對(duì)中,再后面提交SQL的時(shí)候才取出來(lái)。其源碼如下:

// AbstractWrapper
    // 把一個(gè)條件翻譯sqlSegment,以上述wrapper.eq("is_delete", 0)為例,此處column 為字符串“is_delete” ;sqlKeyword 為EQ的枚舉值,會(huì)被翻譯成字符串,最后的參數(shù)值則會(huì)被翻譯成成如 {ew.paramNameValuePairs.MPGENVAL}=
    protected Children addCondition(boolean condition, R column, SqlKeyword sqlKeyword, Object val) {
        return this.maybeDo(condition, () -> {
            this.appendSqlSegments(this.columnToSqlSegment(column), sqlKeyword, () -> {
                return this.formatParam((String)null, val);
            });
        });
    }
    protected final String formatParam(String mapping, Object param) {
        String genParamName = "MPGENVAL" + this.paramNameSeq.incrementAndGet();
        String paramStr = this.getParamAlias() + ".paramNameValuePairs." + genParamName;
        this.paramNameValuePairs.put(genParamName, param);
        return SqlScriptUtils.safeParam(paramStr, mapping);
    }

那么現(xiàn)在我們已經(jīng)擁有了兩件東西:1.一個(gè)大而全的基礎(chǔ)SQL語(yǔ)句(里面依賴了大量的ew內(nèi)容),2.一個(gè)完整的“ew”對(duì)象。同時(shí)擁有這兩者后,就能夠利用 Mybatis 的動(dòng)態(tài)解析能力去構(gòu)建出一個(gè)真正的可執(zhí)行的SQL了

總結(jié)

本次我們介紹了MybatisPlus 構(gòu)造器wrapper的使用方式及其易錯(cuò)點(diǎn),同時(shí)也針對(duì)其運(yùn)行的原理進(jìn)行了解釋,只有深刻理解了它的原理,我們才能更靈活的使用,并且更快的排查出問(wèn)題。所以也希望大家能結(jié)合源碼再思考一下,以便更好地掌握這部分內(nèi)容。

到此這篇關(guān)于MybatisPlus 構(gòu)造器wrapper的使用與原理的文章就介紹到這了,更多相關(guān)MybatisPlus 構(gòu)造器wrapper內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java異常架構(gòu)Exception(異常)詳解

    Java異常架構(gòu)Exception(異常)詳解

    這篇文章主要介紹了Java異常架構(gòu)Exception(異常),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Mybatis引入與使用的圖文步驟

    Mybatis引入與使用的圖文步驟

    本文主要介紹了Mybatis引入與使用的圖文步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09
  • Springboot利用Aop捕捉注解實(shí)現(xiàn)業(yè)務(wù)異步執(zhí)行

    Springboot利用Aop捕捉注解實(shí)現(xiàn)業(yè)務(wù)異步執(zhí)行

    在開(kāi)發(fā)過(guò)程中,盡量會(huì)將比較耗時(shí)且并不會(huì)影響請(qǐng)求的響應(yīng)結(jié)果的業(yè)務(wù)放在異步線程池中進(jìn)行處理,那么到時(shí)什么任務(wù)在執(zhí)行的時(shí)候會(huì)創(chuàng)建單獨(dú)的線程進(jìn)行處理呢?這篇文章主要介紹了Springboot利用Aop捕捉注解實(shí)現(xiàn)業(yè)務(wù)異步執(zhí)行
    2023-04-04
  • SpringBoot下的值注入(推薦)

    SpringBoot下的值注入(推薦)

    這篇文章主要介紹了SpringBoot下的值注入(推薦)的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Java生產(chǎn)者消費(fèi)者的三種實(shí)現(xiàn)方式

    Java生產(chǎn)者消費(fèi)者的三種實(shí)現(xiàn)方式

    這篇文章主要介紹了Java生產(chǎn)者消費(fèi)者的三種實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • 詳談Java中Object類中的方法以及finalize函數(shù)作用

    詳談Java中Object類中的方法以及finalize函數(shù)作用

    下面小編就為大家?guī)?lái)一篇詳談Java中Object類中的方法以及finalize函數(shù)作用。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-04-04
  • Java實(shí)現(xiàn)在線語(yǔ)音識(shí)別

    Java實(shí)現(xiàn)在線語(yǔ)音識(shí)別

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)在線語(yǔ)音識(shí)別功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • Springboot詳細(xì)講解RocketMQ實(shí)現(xiàn)順序消息的發(fā)送與消費(fèi)流程

    Springboot詳細(xì)講解RocketMQ實(shí)現(xiàn)順序消息的發(fā)送與消費(fèi)流程

    RocketMQ作為一款純java、分布式、隊(duì)列模型的開(kāi)源消息中間件,支持事務(wù)消息、順序消息、批量消息、定時(shí)消息、消息回溯等,本篇我們了解如何實(shí)現(xiàn)順序消息的發(fā)送與消費(fèi)
    2022-06-06
  • Zookeeper和Eureka哪個(gè)更好?

    Zookeeper和Eureka哪個(gè)更好?

    今天小編就為大家分享一篇關(guān)于Zookeeper和Eureka哪個(gè)更好?,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02
  • Java基礎(chǔ):流Stream詳解

    Java基礎(chǔ):流Stream詳解

    Stream流是數(shù)據(jù)渠道,用于操作數(shù)據(jù)源(集合、數(shù)組等)所生成的元素序列。這篇文章主要介紹了Java8新特性Stream流的相關(guān)資料,需要的朋友參考下吧
    2021-09-09

最新評(píng)論