Mybatis 處理列名、欄位名映射–AS用法&ResultMap
接上次的博文,看看第二種和第三種方案,因為第二種方案比較簡單,和第三種方案放在一起看了。
前言
考慮到在Select時使用AS和方案一其實沒什麼差別,在介紹ResultMap之前,順便帶過一下。
方案二-Select …. AS
當我們的資料庫列名和對象欄位之間不是駝峰式命名的關係,我們可以在Select時使用AS,使得列名和對象名匹配上。
映射文件中是本次會執行的sql,我們會查出id,city_id,city_name,city_en_name。 按照開啟的駝峰式命名開關,我們會對應到對象的id,cityId,cityName,cityEnName欄位。
select id,city_id,city_name
不過在這次,我們對PO做了小小的改動,把cityEnName改成了cityEnglishName。
由於找不到匹配的列,cityEnlishName肯定沒法被反射賦值,要為Null了。
CityPO
解決辦法: 在Select欄位的時候使用AS,下面是改動後的映射文件。
select id, city_id, city_name
改動後執行得到的結果如下。
那麼我們來看看它是如何生效的,主要的代碼在哪裡。在昨天我們第一個介紹的函數handleRowValues中傳入了參數rsw,它是對ResultSet的一個包裝,在這個包裝里,完成了具體使用哪個名字作為資料庫的列名。
final ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration); handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
在這個構造函數當中,我們會獲取資料庫的列名,AS為什麼可以生效,具體就在下面這段代碼。
在添加列名時,會從配置中獲取是否使用類標籤,isUseColumnLabel,默認為true。根據Javadoc,這個ColumnLabel就是AS後的那個名字,如果沒有AS的話,就是獲取的原生的欄位名。
the value returned from * getColumnLabel will be the same as the value returned by the * getColumnName method. * * @param column the first column is 1, the second is 2, ... * @return the suggested column title * @exception SQLException if a database access error occurs */ String getColumnLabel(int column) throws SQLException;
後面的過程就和昨天講的方案一一模一樣了,不再贅述。
方案三-ResultMap
resultMap 元素是 MyBatis 中最重要最強大的元素。它就是讓你遠離 90%的需要從結果 集中取出數據的 JDBC 代碼的那個東西, 而且在一些情形下允許你做一些 JDBC 不支持的事 情。 事實上, 編寫相似於對複雜語句聯合映射這些等同的代碼, 也許可以跨過上千行的代碼。 ResultMap 的設計就是簡單語句不需要明確的結果映射,而很多複雜語句確實需要描述它們 的關係。
ResultMap是Mybatis中可以完成複雜語句映射的東西,但在我們的日常開發中,我們往往是一個XML對應JavaBeans 或 POJOs(Plain Old Java Objects,普通 Java 對象),並沒有特別複雜的應用,下面也是基於日常的使用,看看簡單的ResultMap在源碼層面是如何展現的。
在resultMap的子元素result對應了result和對象欄位之間的映射,並通過id標示,你在Select語句中指定需要使用的resultMap即可。
源碼層面的話,依舊在DefaultResultSetHandler的handleResultSets中處理返回集合。
List resultMaps = mappedStatement.getResultMaps();
在這次的ResultMap中,相比之前方案,其屬性更加的豐富起來。將之前寫的Result的信息保存在了resultMappings,idResultMappings等中,以備後續使用。
後續的函數走向和方案一二一致,在創建自動映射的時候出現了不同。
private List createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
在這個函數中,會獲取沒有映射過的列名。
final List unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
之後會根據resultMap查看是否有未映射的欄位。
loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);List mappedColumnNames = new ArrayList(); List unmappedColumnNames = new ArrayList(); final String upperColumnPrefix = columnPrefix == null ,根據之前的圖,定義了ResultMap後,會記錄這些已經配置映射的欄位。 final Set mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix); for (String columnName : columnNames) { // 遍歷列名,如果在已映射的配置中,那麼就加入已經映射的列名數據, final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH); if (mappedColumns.contains(upperColumnName)) { mappedColumnNames.add(upperColumnName); } else { unmappedColumnNames.add(columnName); } } // 生成未映射和已映射的Map mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames); unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
如果有沒配置在ResultMap中,且Select出來的,那麼之後也會按照之前方案一那樣,繼續往下走,去對象中尋找映射關係。
由於沒有未映射的欄位,使用自動映射的結果是false。
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
之後繼續往下走,使用applyPropertyMappings來創建對象。使用了PropertyMapping。裡面包含了欄位名,列名,欄位的類型和對應的處理器。
遍歷整個Mappings。
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
函數里主要的就是獲取這個欄位對應的類型處理器,防止類型轉換失敗,這一部分下次會專門看一下。
final TypeHandler typeHandler = propertyMapping.getTypeHandler(); final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix); return typeHandler.getResult(rs, column);
TypeHandler就是一個介面,主要完成的工作就是從Result根據列名,獲取相應類型的值,為下一步反射賦值做準備。至於它是怎麼決定為什麼用這個類型的TypeHandler下次再看。最後就是給對應欄位賦值。
metaObject.setValue(property, value);
最後就完成了整個類的賦值。
總結
大致上,Mybatis完成映射主要是兩種方式。
1. 只根據列名,利用自動映射,根據反射類的信息,得到列名和欄位之間的關係,使用對應的TypeHandler,完成欄位的賦值。
2. 使用ResultMap預先定義好映射關係,也是最後根據TypeHandler和反射,完成欄位的賦值。
我個人感覺就簡單的用法來說,兩者都可以,在一次會話中,Configuration中的ResultMap關係建立好,在每一次查詢的時候就不用再去重新建立了,直接用就行。而自動映射的話,執行過一次後,也會在會話中建立自動映射的緩存。所以沒什麼差別。但如果複雜的映射的話,就非ResultMap莫屬啦。具體可以參考Mybatis文檔關於映射的章節,因為目前用不到比較複雜的映射, 不做深究了。
※如何做 Go 的性能優化?
※Xcode代碼全黑的另一種解決辦法
※微軟推出了一款 App 可以幫你讀出全世界
※PSAttack:一個包含所有的滲透測試用例的攻擊型Powershell腳本框架
※一個靈活的AssetBundle打包工具
TAG:推酷 |
※Hibernate 映射枚舉Enum 類型的屬性
※mybatis與spring整合:Dao層映射配置
※Occipital推出MR創作工具Bridge Engine,實現密實3D映射
※零示例學習中的映射域遷移 (projection domain shift) 問題
※kali Linux信息收集-網路映射工具Nmap
※將2D圖像人物實時映射3D模型,Facebook開源DensePose技術
※實現mybatis框架SQL映射文件SQL片段
※MyBatis框架之SQL映射和動態SQL
※Linux內存映射mmap原理分析
※Magic Leap系統為多人MR共享增加映射融合特性
※神作Revive再更新,Index手柄可良好映射Rift按鍵
※BigOne將支持EOS主網映射
※三星對Galaxy S8/S9的Bixby鍵開放重新映射功能
※ZB.com幫你安全完成EOS映射
※使用IDAPython自動映射二進位文件替換默認函數名
※Facebook最新開源,普通RGB相機即可實時映射3D模型
※ARKit 1.5 demo展示了iOS 11.3中的垂直平面映射升級
※深入淺出MyBatis:「映射器」全了解
※linux下簡單好用的工具rinetd,實現埠映射/轉發/重定向
※哪種特徵分析法適合你的任務?Ian Goodfellow提出顯著性映射的可用性測試