先簡單說下Mybatis的動態sql,這不是今天的重點。web
MyBatis的動態SQL是基於OGNL表達式的,它能夠幫助咱們方便的在SQL語句中實現某些邏輯。
sql
例如,sql語句where條件中,須要一些安全判斷,例如按某一條件查詢時若是傳入的參數是空,此時查詢出的結果極可能是空的,也許咱們須要參數爲空時,是查出所有的信息
安全
MyBatis中用於實現動態SQL的元素主要有:mybatis
if架構
choose(when,otherwise)app
trimide
wherespa
set架構設計
foreach設計
示例mapper.xml:
<select id="findActiveBlogLike" parameterType="BLOG" resultType="BLOG"> SELECT * FROM BLOG WHERE <trim prefix="WHERE" prefixOverrides="AND |OR "> <choose> <when test="title != null"> AND title like #{title} </when> <when test="author != null and author.name != null"> AND title like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose> </trim> </select> <update id="updateAuthorIfNecessary" parameterType="Author"> update Author <trim prefix="where" prefixOverrides=","> <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email}</if> </set> where id=#{id} </trim> </update>
可是問題來了,若是咱們沒有實體怎麼辦?如上代碼,都是關聯實體Author,BLOG。
更進一步,若是咱們連字段和表名都是程序運行時產生的,那麼在Mybatis中,咱們的mapper.xml又該如何寫呢?
不要說這種需求不多,實際上不少靈活性和擴展性要求比較高的應用,物理模型不肯定,也便是你的表結構不肯定,甚至連表名字都不肯定,基本上sql中的每個字母都不是寫死的。
我如今就遇到了這種需求,簡單描述以下:
事先定義好了不少數據集的信息模型,針對這些信息模型生成物理模型。而咱們須要針對這些物理模型進行操做。而這些數據集一旦更新,信息模型以及物理模型都要變更,因此事先不可能徹底肯定物理表結構等等信息。此時應該怎麼在mybatis中進行處理呢?
翻閱mybatis文檔,在一個不起眼的地方發現update標籤有一個屬性statementType,根據jdbc的經驗,這應該是控制sql預編譯仍是非預編譯的,若是sql執行是預編譯的,那麼動態傳入字段名,表名之類的,顯然
是不行的,因此你必須改爲非預編譯的。
二者有什麼區別呢?若是是預編譯的,那麼系統在初始化時就會讀取這段sql代碼,將指定的實體類中的字段替換了相似#{}這樣的語句,就是造成了相似這樣的語句:
"select * from tableName where id=?" 這個時候你在系統運行時再想向這句sql中替換tableName或者id,結果可想而知。若是是非預編譯呢,結果恰好相反,他會在系統運行時纔會去生成這樣相似的語句。此時就能夠去替換這些動態的字段或者表名之類。這樣在結合以前所講的返回類型的設置,咱們的問題就解決了。
咱們能夠不用設定參數和返回類型的實體類,只須要造成一個動態的表名和字段名的列表類。就能夠動態對那些未知的物理模型進行操做.以下代碼可做參考:
<select id="queryMetaList" resultType="Map" statementType="STATEMENT"> select * from ${tableName} t where <foreach item="item" index="index" collection="field" open=" " separator="and" close=" "> <choose> <when test="item.fieldType == 'DATE' and item.dateQueryFlag == 0"> ${item.fieldCode} between to_date('${item.fieldValue}','yyyy-mm-dd hh24:mi:ss') </when> <when test="item.fieldType == 'DATE' and item.dateQueryFlag == 1"> to_date('${item.fieldValue}','yyyy-mm-dd hh24:mi:ss') </when> <when test="item.fieldItemCode != null and item.fieldItemCode != ''"> ${item.fieldCode} = '${item.fieldItemCode}' </when> <otherwise> ${item.fieldCode} = '${item.fieldValue}' </otherwise> </choose> </foreach> </select>
注:會有sql注入危險,代碼中要處理。
另外,注意返回值,在mybatis中,不管你指定仍是不指定返回類型,mybatis都會默認的先將查詢回的值放入一個hashMap中(若是返回的值不止一條就是一個包含hashMap的list)。這其中的區別在於,若是你指定了返回類型,mybatis將會根據返回類型的實體類來從hashMap中獲取值並set到這個實體類中。若是不指定就默認返回一個HashMap<String,Object>(List<HashMap<String,Object>>)。
咱們沒有實體,固然就不要指定返回值,默認接受處理List<HashMap<String,Object>>結構的返回值便可。
最後發點感慨:
親,不要再把實體寫死了,或者不要有實體。實體寫死,最直接的後果就是:Hibernate會把sql框死,Mybatis也會把sql框死,手寫jdbc放入實體,也同樣是死,有新模塊,你還得從web到dao寫一串,累不累,你還怎麼玩。
因此,作大型平臺級應用的架構設計,儘可能不要停留在一個表對應一個實體的階段,必須考慮應用的動態可擴展和靈活性。