MyBatis 學習記錄7 一個Bug引發的思考
主題
這次學習MyBatis的主題我想記錄一個使用起來可能會遇到,但是沒有經驗的話很不好解決的BUG,在特定情況下很容易發生.
異常
java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.xxx.package.ClassA.fun1
這個錯誤非常非常常見,百度一下大部分問題都能解決.這個問題90%出現的原因是一個Mapper檔案中有2個節點的id相同就會出現. 只要仔細檢查下id就OK了.
可能很多問題Mybatis都會報這個錯,id重複是其中之一,也是最常見的,這點上來說MyBatis的錯誤提示不夠好.
現在我來分享一種情況也會出現這個錯誤,但是不是id重複的情況.
1.用MyBatis Generator自動生成Mapper和XML,並且
<javaClientGenerator targetPackage="" targetProject="" type="MIXEDMAPPER"/>
type="MIXEDMAPPER" 也就是自定生成的檔案是混用註解和XML的時候有一定概率產生.
這種生成下Mapper中的select語句有註解@ResultMap, 同時resultMap是定義在XML中的.
2.因為一般專案自動生成的XML和自己手寫的會分開,所以會有多個XML
3.一點點運氣,這個和XML載入順序有關係,在idea沒有打成jar包,也就是檔案是按檔名排序的時候不會有概率問題(開發debug載入順序是確定的),但是mapper.XML被打成jar在jar裡的時候載入順序不是按照字母順序,(釋出打成jar釋出到生產上的時候看起來是亂序)的時候就有一定概率發生(不是必現錯誤,需要看當時XML的載入順序)
原理
正如之前文章分享的那樣,MyBatis在啟動的時候會讀取Mapper XML去解析生產MapperedStatement.
假設我有1個Mapper A, 對應XML1和XML2. 2個XML檔案.
A.fun1方法上有@ResultMap註解,在XML2中定義對應的resultMap.
啟動的時候如果先讀取到了XML1這個XML. 這個時候會呼叫XMLMapperBuilder.parse
public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }