Mybatis動態構建Sql(無實體類)


原文:http://blog.csdn.net/shan9liang/article/details/40452375

 

先簡單說下Mybatis的動態sql,這不是今天的重點。


MyBatis的動態SQL是基於OGNL表達式的,它可以幫助我們方便的在SQL語句中實現某些邏輯。


例如,sql語句where條件中,需要一些安全判斷,例如按某一條件查詢時如果傳入的參數是空,此時查詢出的結果很可能是空的,也許我們需要參數為空時,是查出全部的信息

MyBatis中用於實現動態SQL的元素主要有:

 

  • if
  • choose(when,otherwise)
  • trim
  • where
  • 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寫一串,累不累,你還怎么玩。

所以,做大型平台級應用的架構設計,盡量不要停留在一個表對應一個實體的階段,必須考慮應用的動態可擴展和靈活性。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM