Spring原始碼之JdbcTemplate中的坑
我們平常用JdbcTemplate最多的還是query()方法和queryForObject()方法。同樣,其中還有一個使用最多的是BeanPropertyRowMapper。
但是,在JdbcTemplate.queryForObject()中有一個很不起眼的坑,BeanPropertyRowMapper中也一樣。
坑一:BeanPropertyRowMapper的mapRow()
我們在使用BeanPropertyRowMapper時,是給query()方法傳遞一個BeanPropertyRowMapper物件,讓JdbcTemplate幫我們把查詢結果集ResultSet的每一行結果都使用BeanPropertyRowMapper.mapRow()方法,轉化成我們想要的Java類物件。從BeanPropertyRowMapper名稱上也能夠看出來,它是用來對映Java物件的屬性和MySQL表的欄位名稱的。但是,在對映的過程中,如果不注意Java物件的屬性名的規範,很可能就得不到我們想要的結果。
先來看一下,在建立BeanPropertyRowMapper物件時,會呼叫其中的initialize方法,我們看一下initialize方法的具體實現。
BeanPropertyRowMapper中的initialize方法
如圖,
首選,說一下mappedFields,mapperFields是一個HashMap,用來匹配Java物件的屬性和MySQL表的欄位名的。mapperFields中存放所有可能與MySQL表的欄位名對映上的那些Java屬性名字。
如紅色框框的第一行,在initialize方法中,BeanPropertyRowMapper會把傳入的泛型Java類的所有屬性名稱的全小寫形式放入mapperFields中,
第二行,把Java類的屬性名轉化成下劃線分割的形式,如myName會被轉化成my_name,這是因為,資料庫在設計欄位名稱的時候,一般都會使用下劃線分割形式,也就是my_name。
重點(敲黑板)
所以,如果在使用時,Java類名稱要想和資料庫欄位名稱匹配上,必須要把資料庫欄位名稱設計成以下兩種中的一種,
資料庫欄位名設計成全小寫的形式,如myname;資料庫欄位名設計成下劃線分割的形式,如my_name;
同時,Java屬性名稱應該儘量遵循Java編碼風格,使用camelCase風格,如myName。(欄位名不能帶數字 如:b2c 在mapperFields中會變成b_2_c )
坑二:JdbcTemplate.queryForObject()
在使用queryForObject()方法時,我們一般是希望返回一個物件,而不是像query()方法一樣返回一個列表。
但是,如果ResultSet結果集是空的話,queryForObject會丟擲異常;如果ResultSet結果集的大小大於1的話,queryForObject也會丟擲異常。
檢視一下queryForObject原始碼:
queryForObject
其實,queryForObject使用的是一個SingleColumnRowMapper物件,其中,第二個queryForObject方法會呼叫第一個queryForObject方法,坑就在DataAccessUtils.requiredSingleResult(results)裡面。
看一下DataAccessUtils.requiredSingleResult(results)原始碼:
requiredSingleResult
如圖所示,如果結果集為空,或者結果集的數量大於1,都會丟擲異常,導致queryForObject()無法返回期望結果。
在我們想查找出一個物件時,應該使用query()方法,如果返回結果不為空,那麼取其中的第一個結果就好了。一般不要使用queryForObject方法,queryForObject會要求符合條件的資料庫結果集有且僅有一條記錄,記錄不存在或者記錄數大於1都會丟擲異常。