Mybatis 結構與動態SQL

 1.Mybatis結構

      

  (1)加載配置:配置來源於兩個地方,一處是配置文件,一處是Java代碼的註解,將SQL的配置信息加載成爲一個
 個MappedStatement對象(包括了傳入參數映射配置、執行的SQL語句、結果映射配置),存儲在內存中。
 (2)SQL解析:當API接口層接收到調用請求時,會接收到傳入SQL的ID和傳入對象(可以是Map、JavaBean或者基本數據類型),Mybatis會根據SQL的ID找到對應的          MappedStatement,然後根據傳入參數對象對MappedStatement進行解析,解析後可以得到最終要執行的SQL語句和參數。
(3)SQL執行:將最終得到的SQL和參數拿到數據庫進行執行,得到操作數據庫的結果。
(4)結果映射:將操作數據庫的結果按照映射的配置進行轉換,可以轉換成HashMap、JavaBean或者基本數據類型,並將最終結果返回

2.Mybaits配置文件解析

configuration —— 根元素 
properties —— 定義配置屬性文件
settings —— 一些全局性的配置
typeAliases —— 爲一些類定義別名
typeHandlers —— 定義類型處理,也就是定義java類型與數據庫中的數據類型之間的轉換關係
objectFactory
plugins —— Mybatis的插件,插件可以修改Mybatis內部的運行規則
environments —— 配置Mybatis的環境 
environment 
transactionManager —— 事務管理器
dataSource —— 數據源

mappers —— 指定映射文件或映射類

   2.1 Mybatis主配置文件

      <?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"   
"http://mybatis.org/dtd/mybatis-3-config.dtd">  
  
<configuration>  
    <!-- 屬性配置 -->  
    <properties resource="jdbc.properties"/>  
      
    <!-- 設置緩存和延遲加載等等重要的運行時的行爲方式 -->  
    <settings>  
        <!-- 設置超時時間,它決定驅動等待一個數據庫響應的時間  -->  
        <setting name="defaultStatementTimeout" value="25000"/>  
    </settings>  
      
    <!-- 別名 -->  
    <typeAliases>  
        <typeAlias alias="User" type="com.mybatis.model.User"/>  
    </typeAliases>  
      
    <environments default="development">  
        <!-- environment 元素體中包含對事務管理和連接池的環境配置 -->  
        <environment id="development">  
            <transactionManager type="JDBC" />  
            <dataSource type="POOLED">  
                <property name="driver" value="${driver}" />  
                <property name="url" value="${url}" />  
                <property name="username" value="${username}" />  
                <property name="password" value="${password}" />  
            </dataSource>  
        </environment>  
    </environments>  
      
    <!-- ORM映射文件 -->  
    <mappers>  
        <mapper resource="com/mybatis/model/UserSqlMap.xml" />  
    </mappers>  
</configuration>   

jdbc.properties

driver=com.mysql.jdbc.Driver  
url=jdbc:mysql://localhost:3306/db
username=root  
password=123456

 UserSqlMap.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE mapper       
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"       
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">       
     
<!-- namespace用於java代碼調用時識別指定xml的mapper文件 -->  
<mapper namespace="com.mybatis.model.User">  
    <!-- 配置ORM映射 -->  
    <resultMap type="User" id="user_orm">  
        <id property="id" column="id"/>  
        <result property="userName" column="userName"/>  
        <result property="password" column="password"/>  
    </resultMap>  
      
    <!-- 用來定義可重用的SQL代碼段 -->  
    <sql id="demo_sql">  
       userName,password  
    </sql>  
      
    <insert id="inser_user" parameterType="User">  
        <!-- include 引用可重用的SQL代碼段 -->  
        INSERT INTO USER(<include refid="demo_sql"/>) VALUES(#{userName},#{password})  
    </insert>  
      
    <update id="update_user" parameterType="User">  
        UPDATE USER SET userName=#{userName} ,password=#{password}  WHERE id=#{id}  
    </update>  
      
    <update id="delete_user" parameterType="int">  
        DELETE FROM USER WHERE id=#{id}  
    </update>  
    
    <select id="selectAll_user" useCache="false" flushCache="true" resultMap="user_orm">  
        SELECT * FROM USER  
    </select>  
      
    <!-- 使用map傳人多個參數 -->  
    <select id="selectList_user" useCache="false" flushCache="true" parameterType="map" resultMap="user_orm">  
        SELECT * FROM USER LIMIT #{pageNow},#{pageSize}   
    </select>  
      
    <select id="selectById_user" parameterType="int" resultType="User">  
        SELECT * FROM USER WHERE id= #{id}  
    </select>  
      
    <select id="selectCount_user"  resultType="int">  
        SELECT count(*) FROM USER   
    </select>  
      
    <select id="selectByName_user" parameterType="String" resultType="User">  
        SELECT * FROM USER WHERE userName= #{userName}  
    </select>  
  
    <!--模糊 MyIbatis 3.2.0-->  
    <select id="selectUserByName" parameterType="String" resultMap="user_orm">  
         SELECT * FROM USER WHERE userName like "%"#{userName}"%"  
    </select>  
</mapper>  

SqlSessionFactoryUtil.java  MyBatis工具類,用於創建SqlSessionFactory


import java.io.IOException;  
import java.io.Reader;  
  
import org.apache.ibatis.io.Resources;  
import org.apache.ibatis.session.SqlSession;  
import org.apache.ibatis.session.SqlSessionFactory;  
import org.apache.ibatis.session.SqlSessionFactoryBuilder;  
  
public class SqlSessionFactoryUtil{  
      
    private static final String RESOURCE = "Configuration.xml";  
    private static SqlSessionFactory sqlSessionFactory = null;  
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();  
  
    static {  
        Reader reader = null;  
        try {  
            reader = Resources.getResourceAsReader(RESOURCE);  
        } catch (IOException e) {  
            throw new RuntimeException("Get resource error:"+RESOURCE, e);  
        }  
  
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);  
    }  
      
    /** 
     *  獲得SqlSessionFactory 
     */  
    public static SqlSessionFactory getSqlSessionFactory(){     
        return sqlSessionFactory;     
    }  
      
    /** 
     * : 重新創建SqlSessionFactory 
     */  
    public static void rebuildSqlSessionFactory(){  
        Reader reader = null;  
        try {  
            reader = Resources.getResourceAsReader(RESOURCE);  
        } catch (IOException e) {  
            throw new RuntimeException("Get resource error:"+RESOURCE, e);  
        }  
  
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);  
    }  
      
    /** 
     * 獲取sqlSession 
     */  
    public static SqlSession getSession(){  
        SqlSession session = threadLocal.get();  
          
        if(session!=null){  
            if(sqlSessionFactory == null){  
                getSqlSessionFactory();  
            }  
            //如果sqlSessionFactory不爲空則獲取sqlSession,否則返回null  
            session = (sqlSessionFactory!=null) ? sqlSessionFactory.openSession(): null;  
        }  
          
        return session;  
    }  
      
    /** 
     *  關閉sqlSession 
     */  
    public static void closeSession(){  
        SqlSession session = threadLocal.get();  
        threadLocal.set(null);  
        if(session!=null){  
            session.close();  
        }  
    }  
}  

3.動態SQL配置

 MyBatis 的一個強大的特性之一通常是它的動態 SQL 能力。 如果你有使用 JDBC 或其他 相似框架的經驗,你就明白條件地串聯 SQL 字符串在一起是多麼的痛苦,確保不能忘了空 格或在列表的最後省略逗號。動態 SQL 可以徹底處理這種痛苦

通常使用動態 SQL 不可能是獨立的一部分,MyBatis 當然使用一種強大的動態 SQL 語 言來改進這種情形,這種語言可以被用在任意映射的 SQL 語句中。
動態 SQL 元素和使用 JSTL 或其他相似的基於 XML 的文本處理器相似。在 MyBatis 之 前的版本中,有很多的元素需要來了解。MyBatis 3 大大提升了它們,現在用不到原先一半 的元素就能工作了。MyBatis 採用功能強大的基於 OGNL 的表達式來消除其他元素。
if
choose (when, otherwise)
trim (where, set)
foreach
if
在動態 SQL 中所做的最通用的事情是包含部分 where 字句的條件。比如:

<select id="findActiveBlogWithTitleLike" 
     resultType="Blog">
  SELECT * FROM BLOG 
  WHERE state = ‘ACTIVE’ 
  <if test="title != null">
    AND title like #{title}
  </if>
</select>
這條語句會提供一個可選的文本查找功能。如果你沒有傳遞 title,那麼所有激活的博客 都會被返回。但是如果你傳遞了 title,那麼就會查找相近的 title(對於敏銳的檢索,這中情 況下你的參數值需要包含任意的遮掩或通配符)的博客。

假若我們想可選地搜索 title 和 author 呢?首先,要改變語句的名稱讓它有意義。然後 簡單加入另外的一個條件。

<select id="findActiveBlogLike" 
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’ 
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>
choose, when, otherwise
有時我們不想應用所有的條件, 相反我們想選擇很多情況下的一種。 Java 中的 switch 和 語句相似,MyBatis 提供 choose 元素。

我們使用上面的示例,但是現在我們來搜索當 title 提供時僅有 title 條件,當 author 提 供時僅有 author 條件。如果二者都沒提供,只返回 featured blogs(也許是由管理員策略地選 擇的結果列表,而不是返回大量沒有意義的隨機博客結果列表)。

<select id="findActiveBlogLike" 
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>
trim, where, set
前面的例子已經方便地處理了一個臭名昭著的動態 SQL 問題。要考慮我們回到“if”示 例後會發生什麼,但是這次我們將“ACTIVE = 1”也設置成動態的條件。

<select id="findActiveBlogLike" 
     resultType="Blog">
  SELECT * FROM BLOG 
  WHERE 
  <if test="state != null">
    state = #{state}
  </if> 
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>
如果這些條件都沒有匹配上將會發生什麼?這條 SQL 結束時就會成這樣:

SELECT * FROM BLOG 
WHERE
這會導致查詢失敗。如果僅僅第二個條件匹配是什麼樣的?這條 SQL 結束時就會是這 樣:

SELECT * FROM BLOG 
WHERE 
AND title like ‘someTitle’
這個查詢也會失敗。這個問題不能簡單的用條件來解決,如果你從來沒有這樣寫過,那 麼你以後也不會這樣來寫。

MyBatis 有一個簡單的處理,這在 90%的情況下都會有用。而在不能使用的地方,你可 以自定義處理方式。加上一個簡單的改變,所有事情都會順利進行:
<select id="findActiveBlogLike" 
     resultType="Blog">
  SELECT * FROM BLOG 
  <where> 
    <if test="state != null">
         state = #{state}
    </if> 
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>
where 元素知道如果由被包含的標記返回任意內容,就僅僅插入“WHERE” 。而且,如 果以“AND”或“OR”開頭的內容,那麼就會跳過 WHERE 不插入。


如果 where 元素沒有做出你想要的,你可以使用 trim 元素來自定義。比如,和 where 元素相等的 trim 元素是:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ... 
</trim>

和動態更新語句相似的解決方案是 set。set 元素可以被用於動態包含更新的列,而不包 含不需更新的。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>
這裏,set 元素會動態前置 SET 關鍵字,而且也會消除任意無關的逗號,那也許在應用 條件之後來跟蹤定義的值。
如果你對和這相等的 trim 元素好奇,它看起來就是這樣的:
<trim prefix="SET" suffixOverrides=",">
  ...
</trim>
注意這種情況下我們覆蓋一個後綴,而同時也附加前綴。
foreach
另外一個動態 SQL 通用的必要操作是迭代一個集合, 通常是構建在 IN 條件中的。 比如:
<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>
foreach 元素是非常強大的,它允許你指定一個集合,聲明集合項和索引變量,它們可 以用在元素體內。它也允許你指定開放和關閉的字符串,在迭代之間放置分隔符。這個元素 是很智能的,它不會偶然地附加多餘的分隔符。

注意 你可以傳遞一個 List 實例或者數組作爲參數對象傳給 MyBatis。當你這麼做的時 候,MyBatis 會自動將它包裝在一個 Map 中,用名稱在作爲鍵。List 實例將會以“list” 作爲鍵,而數組實例將會以“array”作爲鍵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章