深度分析MybatisPlus查詢(xún)結(jié)果映射失敗@TableField失效解決辦法
問(wèn)題場(chǎng)景
Springboot使用MybatisPlus框架,數(shù)據(jù)庫(kù)中SQL能查到值,但是代碼中查不到,出現(xiàn)All elements are null等問(wèn)題。以下MybatisPlus簡(jiǎn)稱(chēng)MP
表結(jié)構(gòu)
CREATE TABLE `camel` (
`pwd` varchar(255),
`age` int(11)
);
INSERT INTO `camel` VALUES ('100', 0);
INSERT INTO `camel` VALUES ('200', 1);
INSERT INTO `camel` VALUES ('300', 2);
INSERT INTO `camel` VALUES ('400', NULL);Java實(shí)體類(lèi)
@TableName(value = "camel")
@Data
public class Camel {
@TableField("pwd")
private String pass_word;
@TableField("age")
private Integer age;
}SpringBoot測(cè)試類(lèi)
@SpringBootTest
class DemoApplicationTests {
@Autowired
CamelMapper camelMapper;
@Test
void testTableField() {
// 構(gòu)造查詢(xún)條件 pwd > 100 的 LambdaQueryWrapper
LambdaQueryWrapper<Camel> lambdaQueryWrapper = Wrappers.lambdaQuery(Camel.class)
.gt(Camel::getPass_word, "100");
List<Camel> list = camelMapper.selectList(lambdaQueryWrapper);
list.forEach(System.out::println);
assert list.get(0).getPass_word()!=null;
}
}執(zhí)行結(jié)果


代碼執(zhí)行結(jié)果與預(yù)期不符,數(shù)據(jù)庫(kù)中能查詢(xún)到pwd,代碼中查到的pass_word為NULL
先說(shuō)原因
pass_word字段不滿(mǎn)足命名規(guī)范,導(dǎo)致MP框架無(wú)法通過(guò)反射將查詢(xún)字段賦值給實(shí)體類(lèi)屬性。
思考
數(shù)據(jù)從表中到實(shí)體類(lèi),可以理解為有兩個(gè)階段,首先是通過(guò)查詢(xún)sql得到字段值,然后ORM框架將查詢(xún)到的結(jié)果集通過(guò)setter的方式賦值給接收對(duì)象。
@TableField注解,用于使用MP動(dòng)態(tài)生成SQL或用resultType接受自定義SQL結(jié)果集時(shí),解決數(shù)據(jù)庫(kù)字段名與實(shí)體類(lèi)的屬性名不一致時(shí)的問(wèn)題,先前認(rèn)為使用該注解就能結(jié)果上述問(wèn)題。查看源碼中MP給出的注釋?zhuān)缦?/p>
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableField {
/**
* 數(shù)據(jù)庫(kù)字段值
* <p>
* 不需要配置該值的情況:
* <li> 當(dāng) {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 為 true 時(shí),
* (mp下默認(rèn)是true,mybatis默認(rèn)是false), 數(shù)據(jù)庫(kù)字段值.replace("_","").toUpperCase() == 實(shí)體屬性名.toUpperCase() </li>
* <li> 當(dāng) {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 為 false 時(shí),
* 數(shù)據(jù)庫(kù)字段值.toUpperCase() == 實(shí)體屬性名.toUpperCase() </li>
*/
String value() default "";當(dāng)使用@TableField注解,執(zhí)行sql如下:
select 注解值 as 實(shí)體類(lèi)屬性名 from table;
@TableField注解是通過(guò)別名實(shí)現(xiàn)的,其作用是當(dāng)resultType為實(shí)體類(lèi)時(shí),自動(dòng)為查詢(xún)結(jié)果設(shè)置別名,是發(fā)生在查詢(xún)階段,也因此將控制臺(tái)打印的sql拿到數(shù)據(jù)庫(kù)中能正常執(zhí)行,而本次問(wèn)題是發(fā)生在用實(shí)體類(lèi)接收查詢(xún)結(jié)果階段。
源碼分析
容器啟動(dòng)后,MP會(huì)緩存Mapper實(shí)體類(lèi)的setter與getter方法集合,這里使用了Lombok自動(dòng)生成setter與getter

開(kāi)始執(zhí)行查詢(xún)方法后,首先預(yù)編譯PrepareStatement

當(dāng)配置了Mybatis或MP的日志實(shí)現(xiàn),控制臺(tái)會(huì)打印預(yù)編譯sql與參數(shù),就是在這里實(shí)現(xiàn)的

預(yù)編譯參數(shù)賦值,這里就是查詢(xún)條件中的100

查詢(xún)SQL執(zhí)行完成,處理查詢(xún)結(jié)果的映射關(guān)系

根據(jù)是否打開(kāi)駝峰自動(dòng)轉(zhuǎn)換開(kāi)關(guān),處理屬性名,注意這個(gè)開(kāi)關(guān)在Mybatis中是默認(rèn)關(guān)閉,而在MP中是默認(rèn)打開(kāi)的!所以在這里的pass_word被替換成了password

之后從容器啟動(dòng)時(shí)MP緩存的實(shí)體類(lèi)的setter與getter方法集合中,查找實(shí)體類(lèi)屬性的setter方法,這里可以找到age,但是pass_word因被駝峰命名替換成password,而實(shí)際上實(shí)體類(lèi)中的屬性名是pass_word,導(dǎo)致無(wú)法找到password

用實(shí)體類(lèi)中映射成功的字段接收查詢(xún)結(jié)果,這里就只有age,沒(méi)有password了

通過(guò)反射執(zhí)行上面找到的屬性的setter方法

只有符合命名規(guī)范的字段才會(huì)被賦值成功

最終接收查詢(xún)結(jié)果的集合size = 3,但集合中只有兩個(gè)元素,是因?yàn)镾QL查出了3條記錄,其中pwd=400的記錄,因password字段賦值失敗為NULL,且age也為NULL,該對(duì)象的所有屬性都為NULL,所以集合中就存了一個(gè)NULL對(duì)象,如果集合中所有元素都是NULL,就會(huì)size != 0,且All elements are null

總結(jié)
MP通過(guò)反射的方式,使用屬性對(duì)應(yīng)setter方法為屬性賦值,將查詢(xún)結(jié)果映射到實(shí)體類(lèi),當(dāng)屬性命名不規(guī)范,且開(kāi)啟了駝峰命名開(kāi)關(guān),就會(huì)無(wú)法找到對(duì)應(yīng)setter方法,導(dǎo)致屬性無(wú)法賦值成功。
解決辦法
方法一:手動(dòng)添加setter方法
private void setPassword(String pass_word) {
this.pass_word = pass_word;
}方法二:若使用Lombok,規(guī)范屬性命名(可以保留命名不規(guī)范的屬性,避免影響項(xiàng)目原功能),本質(zhì)與方法一相同
private String password;
方法三:在配置文件中關(guān)閉駝峰自動(dòng)映射
mybatis-plus.configuration.map-underscore-to-camel-case=false
方法四:自定義Mapper方法,不使用實(shí)體類(lèi)作為resultType,在xml中使用自定義resultMap,指定查詢(xún)結(jié)果字段與實(shí)體類(lèi)屬性對(duì)應(yīng)關(guān)系(沒(méi)有使用resultType,@TableField注解不會(huì)生效)
<!--property="pass_word"是實(shí)體類(lèi)屬性名,最終執(zhí)行的setter方法是setPass_word-->
<!--column="pwd"是查詢(xún)結(jié)果字段名-->
<resultMap id="myResultMap" type="com.example.demo.generator.domain.Camel">
<result property="pass_word" column="pwd"/>
<result property="age" column="age"/>
</resultMap>
<select id="myList" resultMap="myResultMap">
select pwd, age from camel
</select> 參考
https://blog.csdn.net/HongYu012/article/details/123301153
到此這篇關(guān)于MybatisPlus查詢(xún)結(jié)果映射失敗@TableField失效解決辦法【源碼解析】的文章就介紹到這了,更多相關(guān)mybatisplus @tablefield失效內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis延遲加載、關(guān)聯(lián)查詢(xún)與結(jié)果映射的實(shí)現(xiàn)原理解析
- MyBatis 結(jié)果映射的兩種方式
- MyBatis結(jié)果映射(ResultMap)的使用
- MyBatis動(dòng)態(tài)SQL、模糊查詢(xún)與結(jié)果映射操作過(guò)程
- Java中MyBatis的結(jié)果映射詳解
- MyBatis中的SQL映射文件配置結(jié)果映射的操作指南
- 關(guān)于MyBatis結(jié)果映射的實(shí)例總結(jié)
- 基于mybatis查詢(xún)結(jié)果映射不到對(duì)象的處理
- MyBatis 結(jié)果映射的幾種實(shí)現(xiàn)方式
相關(guān)文章
SpringBoot實(shí)現(xiàn)HTTP調(diào)用的7 種方式
本文主要介紹了SpringBoot實(shí)現(xiàn)HTTP調(diào)用的7 種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04
Spring Cloud Feign實(shí)現(xiàn)動(dòng)態(tài)URL
本文主要介紹了Spring Cloud Feign實(shí)現(xiàn)動(dòng)態(tài)URL,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
ElasticSearch啟動(dòng)成功卻無(wú)法在瀏覽器訪(fǎng)問(wèn)問(wèn)題解決辦法
因工作的需要,要使用elasticsearch,安裝完了,啟動(dòng)也成功了之后發(fā)現(xiàn)了問(wèn)題,這篇文章主要給大家介紹了關(guān)于ElasticSearch啟動(dòng)成功卻無(wú)法在瀏覽器訪(fǎng)問(wèn)問(wèn)題的解決辦法,需要的朋友可以參考下2024-10-10
Springboot實(shí)現(xiàn)MQTT通信的示例代碼
本文主要介紹了Springboot實(shí)現(xiàn)MQTT通信的示例代碼,包含了MQTT協(xié)議的特點(diǎn)和工作原理等,具有一定的參考價(jià)值,感興趣的可以了解一下2025-01-01
Eclipse中如何引入JUnit進(jìn)行單元測(cè)試
這篇文章主要介紹了Eclipse中如何引入JUnit進(jìn)行單元測(cè)試問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
Java8中Stream?API的peek()方法詳解及需要注意的坑
這篇文章主要給大家介紹了關(guān)于Java8中Stream?API的peek()方法詳解及需要注意的坑,Java 中的 peek 方法是 Java 8 中的 Stream API 中的一個(gè)方法,它屬于中間操作,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06
SpringBoot集成Eclipse Mosquitto的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot集成Eclipse Mosquitto的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-08-08
Java設(shè)計(jì)模式之單例模式Singleton Pattern詳解
這篇文章主要介紹了Java設(shè)計(jì)模式之單例模式Singleton Pattern詳解,一些常用的工具類(lèi)、線(xiàn)程池、緩存,數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)連接池、賬戶(hù)登錄系統(tǒng)、配置文件等程序中可能只允許我們創(chuàng)建一個(gè)對(duì)象,這就需要單例模式,需要的朋友可以參考下2023-12-12

