深度分析MybatisPlus查詢結果映射失敗@TableField失效解決辦法
問題場景
Springboot使用MybatisPlus框架,數(shù)據(jù)庫中SQL能查到值,但是代碼中查不到,出現(xiàn)All elements are null等問題。以下MybatisPlus簡稱MP
表結構
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實體類
@TableName(value = "camel")
@Data
public class Camel {
@TableField("pwd")
private String pass_word;
@TableField("age")
private Integer age;
}SpringBoot測試類
@SpringBootTest
class DemoApplicationTests {
@Autowired
CamelMapper camelMapper;
@Test
void testTableField() {
// 構造查詢條件 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í)行結果


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

開始執(zhí)行查詢方法后,首先預編譯PrepareStatement

當配置了Mybatis或MP的日志實現(xiàn),控制臺會打印預編譯sql與參數(shù),就是在這里實現(xiàn)的

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

查詢SQL執(zhí)行完成,處理查詢結果的映射關系

根據(jù)是否打開駝峰自動轉換開關,處理屬性名,注意這個開關在Mybatis中是默認關閉,而在MP中是默認打開的!所以在這里的pass_word被替換成了password

之后從容器啟動時MP緩存的實體類的setter與getter方法集合中,查找實體類屬性的setter方法,這里可以找到age,但是pass_word因被駝峰命名替換成password,而實際上實體類中的屬性名是pass_word,導致無法找到password

用實體類中映射成功的字段接收查詢結果,這里就只有age,沒有password了

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

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

最終接收查詢結果的集合size = 3,但集合中只有兩個元素,是因為SQL查出了3條記錄,其中pwd=400的記錄,因password字段賦值失敗為NULL,且age也為NULL,該對象的所有屬性都為NULL,所以集合中就存了一個NULL對象,如果集合中所有元素都是NULL,就會size != 0,且All elements are null

總結
MP通過反射的方式,使用屬性對應setter方法為屬性賦值,將查詢結果映射到實體類,當屬性命名不規(guī)范,且開啟了駝峰命名開關,就會無法找到對應setter方法,導致屬性無法賦值成功。
解決辦法
方法一:手動添加setter方法
private void setPassword(String pass_word) {
this.pass_word = pass_word;
}方法二:若使用Lombok,規(guī)范屬性命名(可以保留命名不規(guī)范的屬性,避免影響項目原功能),本質與方法一相同
private String password;
方法三:在配置文件中關閉駝峰自動映射
mybatis-plus.configuration.map-underscore-to-camel-case=false
方法四:自定義Mapper方法,不使用實體類作為resultType,在xml中使用自定義resultMap,指定查詢結果字段與實體類屬性對應關系(沒有使用resultType,@TableField注解不會生效)
<!--property="pass_word"是實體類屬性名,最終執(zhí)行的setter方法是setPass_word-->
<!--column="pwd"是查詢結果字段名-->
<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
到此這篇關于MybatisPlus查詢結果映射失敗@TableField失效解決辦法【源碼解析】的文章就介紹到這了,更多相關mybatisplus @tablefield失效內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java關鍵字this(動力節(jié)點Java學院整理)
java中的this隨處可見,用法也多。通常情況下理解this關鍵字還是很容易的,但是在我初學的時候,有一個疑問卻一直不能很清晰的理解,現(xiàn)在慢慢的理解了,下面通過本文給大家記錄下,有需要的朋友參考下2017-03-03
親測SpringBoot參數(shù)傳遞及@RequestBody注解---踩過的坑及解決
這篇文章主要介紹了親測SpringBoot參數(shù)傳遞及@RequestBody注解---踩過的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
Java實現(xiàn)訂單未支付則自動取消的五種方案及對比分析
作為電商系統(tǒng)中的核心功能,"訂單超時未支付自動取消" 是一個典型的定時任務場景,這個看似簡單的需求背后,隱藏著高并發(fā)、數(shù)據(jù)一致性、性能損耗等多個技術痛點,本文將從業(yè)務場景出發(fā),分析該需求的難點,然后依次介紹五種 Java 技術實現(xiàn)方案,需要的朋友可以參考下2025-05-05

