MyBatis框架之SQL映射和动态SQL

使用MyBatis实现条件查询1.SQL映射文件:MyBatis真正的强大之处就在于SQL映射语句,MyBatis专注于SQL,对于开发人员来说也是极大限度的进行SQL调优,以保证性能。下面是SQL映射文件的几个顶级元素的配置:  1.mapper:映射文件的根节点,只有一个属性namespace(命名空间),作用如下:    (1)用于区分不同的mapper,全局唯一。
    (2)绑定DAO接口,即面向接口编程,当绑定一个接口,就不用写此接口的实现类,会通过接口的完全限定名找到对应的mapper配置来执行SQL语句,所以,namespace的命名必须要写接口的完全限定名。
  2.cache:配置给定命名空间的缓存。  3.cache-ref:从其他命名空间引用缓存配置。  4.resultMap:用来描述数据库结果集和对象的对应关系。
  5.sql:可以重用的SQL块,也可以被其他语句引用。

  6.insert:映射插入语句。

  7.update:更新映射语句。

  8.delete:删除映射语句。

  9.select:映射查询语句。
2.使用select完成单条件查询
  <select id = "GetUserByName" resultType = "User" parameterType = "string">

    select * from user where name like concat('%',#,'%') //按照姓名模糊查询。

  </select>

这是一个id为GetUserByName的映射语句,参数类型为string,返回类型为User。

#{参数名}:告诉MyBatis生成的PreparedStatement参数,相对于JDBC,改参数被标识为‘?’。

  id:命名空间的唯一标识符,可以被用来引用这条语句。

  parameterType:表示查询语句传入参数的类型和完全限定名或别名。支持基础数据类型和复杂数据类型。上述实例中传入的参数是一个别名,代表String。

            别名与Java类型映射

别名      映射的类型    别名    映射的类型    
string  StringdoubleDouble
byteBytefloatFloat
long LongbooleanBoolean
shortShortdateDate
intIntegermapMap
integerIntegerhashmapHashMap
arraylistArrayListlistList

  resultType:查询语句返回结果类型的完全限定名或别名。别名使用方式和parameterType是一样的。
2.使用select完成多条件查询
  使用复杂数据类型,把条件参数封装为对象、Map进行入参。

  不管什么类型的参数,或者多少个参数,都可以封装为一个Map进行入参,通过Map的key进行获取传入的值。

     <select id="GetUserByNameAndRoleId" resultType="User"
         parameterType="map">
         select * from smbms_user where userName like
         CONCAT('%',#{userName},'%')
         and userRole = #{userRole}
     </select>

3.使用resultMap完成查询结果的展现
resultMap:做自定义映射,实体类属性名和数据库列名不一致的情况下,并且可以指定要显示的列,使用灵活 应用广泛。

   <resultMap type="User" id="userMap">
        <id column="id" property="id"/>
        <result column="userName" property="Name"/> //字段名和属性名不一致的情况下。
   </resultMap>

属性和子节点:

   id:唯一标识,此id值用于select元素resultMap属性的引用。

   type:标识该resultMap的映射结果类型。

   result子节点:标识一些简单属性,其中column属性代表数据库的字段名,property代表查询出来的字段名映射到pojo类的某个属性。   

   id子节点:与result子节点作用一样,一般表示数据库的主键列。


resultType和resultMap的对比如下 :
1.resultType:直接表示返回类型, 包括基本数据类型和复杂数据类型。

2.resultMap:外部resultMap定义的引用,通过对应的外部resultMap的id,表示结果映射到哪个resultMap上,一般用于字段名和属性名不一致的情况,或者需要做复杂的联合查询以便自由控制映射结果。

3.两者的关联

当进行查询时,查询出来的每个字段都会放在一个Map里,当查询元素返回属性是resultType的时候,会将键值对取出赋所指定的属性。其实MyBatis的每个查询映射的返回类型都是resultMap,只是当我们使用resultType的时候,会自动把对应的值赋给所指定的对象属性,当使用resultMap时候,因为map不是很好的表示领域,我们就进一步的转化为对应的实体对象。resultMap主要作用于复杂的联合查询上。
注意:resultType和resultMap本质是一样的,都是Map数据结构,但是二者不能同时存在。
4.resultMap的自动映射级别:默认级别为PARTIAL,也可以在settings更改值。

   <settings>
         <!-- 设置resultMap的自动映射级别为NONE(禁止自动匹配) -->
         <setting name = "autoMappingBehavior" value = "NONE">
   </settings>

4.使用MyBatis实现增删改操作
  1.使用insert完成增加操作

<insert id="add" parameterType="User">
    INSERT INTO user(userName,userCode) VALUES(#{userName},#{userCode})
</insert>

  2.使用update完成修改操作

<update id="update" parameterType="User">
        UPDATE user SET userName= #{userName},userCode= #{userCode} WHERE id = #{id}
    </update>

  3.使用delete完成删除操作

<delete id="User" parameterType="integer">
        delete from user
        where id = #{id}
</delete>

  元素中的属性:

    id:与select元素id一样,命名空间的唯一标识符。

    parameterType:传入参数的类型的完全限定名或者别名。

对于增删改操作的注意事项:

(1)该类型的操作本身默认返回执行SQL影响的行数,所以DAO层的接口方法返回值一般为int类型,最好不要boolean类型。

(2)insert、update、delete元素中均没有resultType属性,只有查询操作需要对返回结果类型(resultType/resultMap)进行相应的指定。

5.使用@param注解实现多参数入参

   使用注解用来传入多个参数,相当于给该参数重命名,在SQL映射文件中需要使用#{注解名称}来传入参数。 

    public user GetUserByIdAndName(@Param("id") Integer id,@Param("Name") String Name);

当参数为基础类型时,无论是多参数还是单参数,都使用@param注解进行参数的传递为好!
使用resultMap实现高级结果映射1.association:
  映射到JavaBean的某个复杂的”数据类型”属性,仅处理一对一的关联关系。

<resultMap type="SMS_USER" id="userMap">
        <id column="id" property="id" />
        <result column="userName" property="userName" />
        <result column="phone" property="phone" />
        <association property="role" javaType="SMS_Role">
            <id column="id" property="id" />
            <result column="roleName" property="roleName" />
        </association>
    </resultMap>

  association的属性节点:

    property:映射数据库列的实体对象属性名。

    javaType:完整的java类名和限定名。propert所映射的属性的类型。

    子元素:id:一般为映射主键,可以提高性能。

        result:

          column:映射的数据库的字段名。

          property:映射的数据列对应的实体对象属性。
2.collection:

  映射到JavaBean的某个复杂的”数据类型”属性,这个属性是一个集合列表,处理一对多的关联关系。

<resultMap type="SMS_USER" id="userMapAddress">
        <id column="id" property="id"/>
        <result column="userName" property="userName"/>
        <collection property="lists" ofType="SMS_Address"><br>       <id column = "id" property = "id">
            <result column="addressDesc" property="addressDesc"/>
        </collection>
    </resultMap>

  ofType:完整的java类名和限定名。propert所映射的属性的类型。

  其余和association基本一致。
MyBatis缓存
1.一级缓存:基于PerpetualCache(MyBatis自带)的HashMap本地缓存。作用域在Session域内,当session flush或者close之后,该缓存被清空。

2.二级缓存:global Cache,超出session范围之外,可以被所有SqlSession共享。开启它只需在MyBatis的核心配置文件 settings 设置即可。

  补充:一级缓存缓存的是SQL语句,二级缓存缓存的是结果对象。

3.二级缓存配置:

  (1)开启二级缓存;

<settings>
        <setting name="cacheEnabled" value = "true"/>
<settings>

  (2)mapper文件中设置缓存,默认是没有开启缓存的。作用域是针对namespace而言的,只在namespace内的查询才能共享这个cache。

  (3)对个别查询进行缓存,单独设置cache:

<select id = "getUser" resultType = "User" useCache = "true">
    ...........
</select>

补充:对MyBatis的缓存了解就可以了,对结果集做缓存并不是MyBatis所擅长的,而且性能也不是很好,它专心做的应该是SQL映射。


动态SQL一、使用动态SQL完成多条件查询。
动态SQL基于OGNL的表达式,我们可以方便的在SQL语句中实现某些逻辑,用于实现动态SQL元素如下:

  if:利用if实现简单的条件选择。

  choose(when,otherwise):相当于java中的switch语句,通常与when和otherwise搭配。

  set:解决动态更新语句。

  trim:灵活的去除多余的关键字。

  foreach:迭代一个集合,通常用于in条件。
二、if+where实现多条件查询

<select id="ShowUser" parameterType="Map" resultType="SMS_USER">
        select * from smbms_user
            <where>
                <if test="userRole != null and userRole != 0">
                    userRole = #{userRole}
                </if>
                <if test="userName != null and userName != ''">
                    and userName like CONCAT('%',#{userName},'%')
                </if>
            </where>
    </select>

where元素可以智能的处理and 和 or 的多余问题, 不需担心多余关键字导致语法错误。

if元素的test用于判断表达式是否符合,符合则继续拼接SQL语句。
三、if+trim+foreach实现多条件查询

  <select id="listsByInAndGender" resultType="SMS_USER">
           select * from smbms_user
         <trim prefix="where" prefixOverrides="and | or">
             <if test="userIds!=null">
                 id in
                 <foreach collection="userIds" item="ids" open="(" close=")" separator=",">
                     #{ids}
                 </foreach>
             </if>
             <if test="gender != null and gender != 0">
                 and gender = #{gender}
             </if>
         </trim>
  </select>

(1)trim的属性:

  prefix:前缀: 作用是通过自动识别是否有返回值后,在trim包含的内容上加上前缀,如上述示例的where。

  suffix:后缀: 作用是在trim包含的内容上加上后缀。

  prefixOverrides: 对于trim包含内容的首部进行指定内容,(如上述示例的 and | or) 的忽略(去余);

  suffixOverrides: 对于trim包含内容的首位部进行指定内容的忽略。

(2)foreach的属性:

  item:表示集合中每一个元素进行迭代时的别名。

  index: 指定一个名称,表示在迭代的过程中,每次迭代到的位置。

  open:表示该语句以什么开始(既然是in条件语句,必然是 ' ( ' 开始)

  separator: 表示每次进行迭代的时候以什么符号作为分隔符(既然是in条件语句,必然是 ' , ' 分隔)

  close: 表示该语句以什么结束(既然是in条件语句,必然是 ' ) ' 结束)

  collection:最关键,并且最容易出错的属性。需注意,该属性必须指定,不同情况下,该属性值是不同的,主要有三种情况:

    若传入的参数是单参数且类型为一个List的时候,属性值为list;

    若传入的参数是单参数且类型为一个数组的时候,属性值为array;

    若传入的参数为多参数时,就需要封装为一个map集合进行处理。属性值为Map的key;

使用set更新操作,类似于上述示例使用方法:

     <update id="UpdateUser" parameterType="SMS_USER">
          update smbms_user
          <trim prefix="set" suffixOverrides="," suffix="where id = #{id}">
              <if test="userName != null and userName != ''">
                  userName = #{userName},
              </if>
              <if test="gender != null and gender != 0">
                  gender = #{gender},
              </if>
              <if test="address != null and address != ''">
                  address = #{address},
              </if>
          </trim>
     </update>

三、choose(when、otherwise)

     <select id="QueryAllByChoose" resultType="SMS_provider">
         select * from smbms_provider where 1 = 1
         <choose>
             <when test="proCode != null and proCode != ''">
                 and proCode like CONCAT('%',#{proCode},'%')
             </when>
             <when test="proName != null and proName != ''">
                 and proName like CONCAT('%',#{proName},'%')
             </when>
             <when test="proContact != null and proContact != ''">
                 and proContact like CONCAT('%',#{proContact},'%')
             </when>
             <otherwise>
                 and YEAR(creationDate) = 2016
             </otherwise>
         </choose>
     </select>

  when元素:当test属性中的条件满足时,就会输出when元素中的内容,并且当when中一旦有条件满足时,就会跳出choose,所以只有一个条件会被输出。

  otherwise元素:当when中的所有条件都不满足时,则会输出此元素的内容。
四、MyBatis实现分页功能
  1.使用聚合函数获得总记录数-

  2.实现分页通过limit(起始位置,页面显示量)
补充:起始位置的下标 = (当前页码 - 1 ) * 页面显示量

       <!-- 多条件筛选分页 -->
     <select id="UserPage" parameterType="map" resultType="SMS_USER">
         select * from smbms_user
         <trim prefix="where" prefixOverrides="and | or">
             <if test="userName != null and userName != ''">
                 userName like concat('%',#{userName},'%')
             </if>
             <if test="userRole != null and userRole != 0">
                 and userRole = #{userRole}
             </if>
         </trim>
         limit #{startRow},#{pageSize}
     </select>