深刻學習Mybatis框架(二)- 進階

1.動態SQL

1.1 什麼是動態SQL?

  動態SQL就是經過傳入的參數不同,能夠組成不一樣結構的SQL語句。 這種能夠根據參數的條件而改變SQL結構的SQL語句,咱們稱爲動態SQL語句。使用動態SQL能夠提升代碼重用性。java

1.2 XML方式的實現

  1.2.1 須要使用到的標籤

<if> 用於判斷,相似java的if(){}
<foreach>通常用戶批量處理的SQL語句,相似java的foreach循環,
<trim> :切割標籤,主要用於切割關鍵字的頭和尾的字符.新版的Mybatis使用的概率不多.
<set>:使用 set標籤就是SQL語言的set關鍵字,能夠在update 的時候set 關鍵字後面的,逗號能夠自動忽略
<where>:使用where標籤做爲SQL語言的where關鍵字,好處若是where後面的條件都不成立,忽略where關鍵字.
<choose> <when> <otherwise> : java的swithc case
<sql> 用於聲明公有的SQL語句塊.,在操做標籤中使用<include>調用 [通常不建議用]mysql

不建議的緣由,會致使代碼難以維護。git

  1.2.2 使用示例

  條件查詢:(where)github

public List<User> selectByCondition(User user);
<!-- 條件查詢 -->
    <select id="selectByCondition" parameterType="com.gjs.pojo.User" resultType="com.gjs.pojo.User">
        select * from user
        <where>
            <!-- if標籤:條件判斷標籤 -->
            <if test="name!=null">
                name = #{name}
            <!--或者使用模糊查詢: name like concat('%',#{name},'%') -->
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
        </where>
</select>

 

  修改:(set)sql

public int updateByNotNull(User user);
<update id="updateByNotNull" parameterType="com.gjs.pojo.User">
        update user 
        <!-- set -->
        <set>
            <if test="name != null">name=#{name},</if>
            <if test="password != null">password=#{password},</if>
            <if test="age != null">age=#{age}</if>
        </set>
         where id=#{id}
</update>

 

  根據條件統計個數(trim )數據庫

public Long selectTotalByCondition(User user);
    <!-- 動態SQL語句trim標籤 
            perfix : 動態sql語句的前綴 (WHERE,SET)
            prefixOverrides : 自動截取掉或者替換條(WHERE 多餘後面 關鍵字 :AND-OR)
        -->
    <select id="selectTotalByCondition" parameterType="com.gjs.pojo.User" resultType="long" >
        select count(*) from user
        <trim prefix="WHERE" prefixOverrides="AND|OR">
            <if test="name!=null">
                name like concat('%',#{name},'%')
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
        </trim>
    </select>
 <!-- set操做: <trim prefix="SET" suffixOverrides=","> -->

 

  批量刪除:(foreach)apache

public int deleteByIds(@Param("ids")List<Integer> ids);
    <delete id="deleteByIds" parameterType="Integer">
        delete from user where id in
        <!-- 動態sql語句 foreach 循環標籤 
          <foreach collection="" open="" close="" item="" separator=""></foreach>
              collection : 要循環集合或者數組 
              open :開始位置符號 前小括號 (
              close : 開始位置符號 後小括號 )
              item : 每次循環的數據
              separator : 分隔符 逗號 ,
          -->
          <foreach collection="ids" open="(" close=")" item="id" separator=",">
              #{id}
          </foreach>
    </delete>

<sql>的使用:數組

     <sql id="condition_sql">
          <where>
              <if test="name !=null">
                  <!-- name like '%${name}%' -->
                  name like concat('%',#{name},'%')
              </if>
            <if test="age !=null">
                and age = #{age}
            </if>          
          </where>
    </sql>
 
     <select id="selectByCondition" parameterType="com.gjs.pojo.User" resultType="com.gjs.pojo.User">
          select * from user
          <!-- 引入sql片斷.  refid :被引入sql片斷的id -->
          <include refid="condition_sql_by_trim"/
      </select>

 

1.3 註解方式實現

動態sql除了支持xml方式之外,仍是支持使用純註解的方式
主要一下四個註解+對應動態sql語句的類文件緩存

1.@SelectProvider 動態查詢SQL語句對應註解
2.@InsertProvider 動態插入SQL語句對應註解
3.@UpdateProvider 動態修改SQL語句對應註解
4.@DeleteProvider 動態刪除SQL語句對應註解mybatis

  

示例:
Usermapper

package com.gjs.mapper;

import java.util.List;

import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;

import com.gjs.pojo.User;
import com.gjs.pojo.UserProvider;

public interface UserMapper {

    
    /*
     * 條件查詢
     * type : 編寫動態sql語句的類對應的字節碼
     * method : 編寫動態sql語句類對應的方法名稱
     * 此方法返回的是一個String字符串,字符串就是用於註解方法查詢的sql語句
     */
    @SelectProvider(type= UserProvider.class,method="selectByCondition")
    public List<User> selectByCondition(User user);
    
    //根據條件統計總數
    @SelectProvider(type= UserProvider.class,method="selectTotalByCondition")
    public Long selectTotalByCondition(User user);
    
     //修改
    @UpdateProvider(type= UserProvider.class,method="updateByNotNull")
    public int updateByNotNull(User user);
    
    //批量刪除
    @DeleteProvider(type=UserProvider.class,method="deleteByIds")
    public int deleteByIds(@Param("ids")List<Integer> ids);
}

  UserProvider
  構建方法參數規則:
    1.非數組、集合的參數,調用方法是什麼,構建SQL語句的方法就是什麼
    2.是數組、集合的參數,構建的方法須要包一層Map。如:調用方法爲:String[] ids ,構建方法格式爲Map<String,String[]> ids

package com.gjs.pojo;

import java.util.List;

import org.apache.ibatis.annotations.Param;

public class UserProvider {
    
    public String selectByCondition(User user) {
        StringBuilder sb = new StringBuilder();
        sb.append("select * from user where 1=1 ");
        if(user.getName()!=null) {
            //因爲最後字符串仍是返回給Mybatis執行的全部這裏須要使用OGNL表達式來獲取對象屬性的值
            sb.append("and name like concat('%',#{name},'%') ");
        }
        if(user.getAge()!=null) {
            sb.append("and age = #{age}");
        }
        return sb.toString();
    }
    
    public String selectTotalByCondition(User user) {
        StringBuilder sb = new StringBuilder();
        sb.append("select count(1) from user where 1=1 ");
        if(user.getName()!=null) {
            sb.append("and name like concat('%',#{name},'%') ");
        }
        if(user.getAge()!=null) {
            sb.append("and age = #{age}");
        }
        return sb.toString();
    }
    
    public String updateByNotNull(User user) {
        StringBuilder sb = new StringBuilder();
        sb.append("update user set ");
        if(user.getName()!=null) {
            sb.append("name = #{name},");
        }if(user.getPassword()!=null) {
            sb.append("password = #{password},");
        }if(user.getAge()!=null) {
            sb.append("age = #{age},");
        }
        sb.deleteCharAt(sb.length()-1);//刪除末尾多餘的逗號","
        sb.append("where id = #{id}");
        return sb.toString();
    }
    
    public String deleteByIds(@Param("ids")List<Integer> ids) {
        StringBuilder sb = new StringBuilder();
        sb.append("delete from user where id in(");
        for (int i = 0; i < ids.size(); i++) {
            sb.append("#{ids["+i+"]},");
        }
        sb.deleteCharAt(sb.length()-1);//刪除末尾多餘的逗號","
        sb.append(")");
        return sb.toString();
    }
}

2.緩存

  在Mybatis裏面,所謂的緩存就是將已經查詢過的記錄放在內存的緩衝區或文件上,這樣若是再次查詢,能夠經過配置的策略,命中已經查詢過的記錄.從而提升查詢的效率。
Mybatis的緩存分爲一級緩存和二級緩存

  2.1 一級緩存

   所謂的一級緩存就是會話(SqlSesion對象)級別的緩存,就是同一個會話,若是已經查詢過的數據會保存一份在內存中,若是會話沒有關閉,再次調用一樣的方法查詢,不會再查詢數據庫,而是直接從緩存中取出以前查詢的數據。一級緩存默認是打開的,並且是關閉不了的。
  如下幾種狀況一級緩存會被清空:
    1.關閉會話.close()
    2.進行了操做(增刪改),提交了commit();
    3.手工清除緩存clearCache()

  2.2 二級緩存

二級緩存是 SqlSessionFactory級別,在整個應用都有效,能夠在多個會話有效
MyBatis自己並無實現二級緩存,二級緩存須要第三方緩存提供商的支持
Ehcache:
下載地址:https://github.com/mybatis/ehcache-cache/releases
學習地址:http://www.mybatis.org/ehcache-cache/

 

3.MyBatis的對象關係映射

  在實際開發中,一個業務可能涉及到多個數據表的查詢,那麼多表查詢就涉及鏈接查詢(等值鏈接), 等值鏈接 表與表之間有一個外鍵關鍵。
  可是程序中最終獲取的表封裝的對象, 對象與對象之間是沒有外鍵關係的,對象和對象之間只有依賴關係

  對象之間關係主要是四種(什麼關係應該看從哪一個對象的角度)

一對一 關係
  一我的對應身份證號
一對多 關係
  一個部門對應多個員工
多對一 關係
  多個員工對應一個部門
多對多 關係
  多個學生對應多個老師,多個學生對應多個課程

  MyBatis框架支持多表查詢封裝對象之間關係:
    <collection>標籤: 一對多查詢
    <association>標籤:多對一和一對一查詢
  注:<collection>和<association>爲<resultMap>的子標籤

 

3.1 多對一查詢(<association>聯合查詢標籤)(N+1)

  例:以員工爲中心來查詢關聯部門(多對一關係,多個員工對應一個部門)

 

 數據庫表

員工表
CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `dept_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
部門表
CREATE TABLE `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

部門類

package com.gjs.pojo;

public class Department {
    private Integer id;
    private String name;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Department(Integer id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public Department() {
        super();
    }
    @Override
    public String toString() {
        return "Department [id=" + id + ", name=" + name + "]";
    }
    
}

員工類(Employee)

package com.gjs.pojo;

public class Employee {
    private Integer id;
    private String name;
    
    //以員工爲中心來關聯部門,多對一關係,多個員工對應一個部門 : many2one
    private Department dept;
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Department getDept() {
        return dept;
    }
    public void setDept(Department dept) {
        this.dept = dept;
    }
    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", dept=" + dept + "]";
    }
}

接口

package com.gjs.mapper;

import com.gjs.pojo.Employee;

public interface Many2OneMapper {
    /**
     * 根據與員工的編碼查詢出員工對應的全部信息(包含部門)
     * @param id
     * @return
     */
    public Employee selectByEmpId(Integer id);
}

 接口映射配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 配置映射
namespace : 命名空間(通俗說法: 給當前映射文件的惟一標識:起一個惟一的名字)    
 -->
<mapper namespace="com.gjs.mapper.Many2OneMapper">
    
    <!-- 因爲查詢出來的結果於com.gjs.pojo.Employee中的屬性不一致(Department dept),因此不能自動映射 -->
    <select id="selectByEmpId" resultMap="emp_map" parameterType="int">
        select * from employee where id = #{id}
    </select>
    
    <resultMap type="com.gjs.pojo.Employee" id="emp_map">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!-- 
            問題:private Department dept; 對象如何映射?
            解決方案: 使用聯合查詢標籤
            <association property="" column="" select=""></association>
            property : 須要映射額屬性 dept
            column : 已知的部門外檢列 dept_id
            select : 調用查詢經過部門id查詢出對應部門對象的功能的id
                  值規則 : 映射文件的命名空間 + 點兒(.) + 功能id
                  若是是在同一個命名空間,能夠省略命名空間和點
         -->
        <association property="dept" column="dept_id" select="selectByDeptId"/>
    </resultMap>
    
    <select id="selectByDeptId" parameterType="int" resultType="com.gjs.pojo.Department">
        select * from department where id = #{id}
    </select>
</mapper>

 

3.2 多對一查詢(<collection>集合映射標籤)(N+1)

例:以部門爲中心查詢部門的全部信息(包括員工),一個部門對應多個員工
部門類

package com.gjs.pojo;

import java.util.List;

public class Department {
    private Integer id;
    private String name;
    //以部門爲中心查詢部門的全部信息(包含員工)
    //一個部門有多個員工 一對多關係:one2many
    // 部門的元員工使用 list集合包裝
    private List<Employee> emps;
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<Employee> getEmps() {
        return emps;
    }
    public void setEmps(List<Employee> emps) {
        this.emps = emps;
    }
    public Department(Integer id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public Department() {
        super();
    }
    @Override
    public String toString() {
        return "Department [id=" + id + ", name=" + name + ", emps=" + emps + "]";
    }
    
}

員工類

package com.gjs.pojo;

public class Employee {
    private Integer id;
    private String name;
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + "]";
    }
    
    
}

接口

package com.gjs.mapper;

import com.gjs.pojo.Department;

public interface One2ManyMapper {
    /**
     * 根據與部門的編碼查詢出部門對應的全部信息(包含全部員工)
     * @param id
     * @return
     */
    public Department selectByDeptId(Integer id);
}

接口對應的映射配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 配置映射
namespace : 命名空間(通俗說法: 給當前映射文件的惟一標識:起一個惟一的名字)    
 -->
<mapper namespace="com.gjs.mapper.One2ManyMapper">
    
    <select id="selectByDeptId" parameterType="int" resultMap="dept_map">
        select * from department where id = #{id}
    </select>
    
    <resultMap type="com.gjs.pojo.Department" id="dept_map">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!-- 
        問題 :List<Employee> emps; 集合如何映射?
        解決方案: 使用 <collection>集合映射
        <collection property="" column="" select=""/>
            property :須要映射的屬性  emps 對應的list集合
            column : 部門自己的主鍵 id
            select : 關聯查詢的功能id
            規則 : 命名空間+點+功能id。 若是同一個命名空間下面直接 功能id便可
         -->
        <collection property="emps" column="id" select="selectEmpsByDeptId"/>
    </resultMap>
    
    <select id="selectEmpsByDeptId" resultType="com.gjs.pojo.Employee" parameterType="int">
        select * from employee where id = #{dept_id}
    </select>
</mapper>

3.3 等值鏈接方式查詢

  以上都是用N+1的方式。MyBatis的對象關係映射還有一種等值鏈接方式。
  以一對多爲例:


pojo類和接口皆與3.2的相同

映射配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 配置映射
namespace : 命名空間(通俗說法: 給當前映射文件的惟一標識:起一個惟一的名字)    
 -->
<mapper namespace="com.gjs.mapper.One2ManyMapper">
    
    <select id="selectByDeptId" parameterType="int" resultMap="dept_map">
        select e.id e_id ,e.name e_name,d.id d_id,d.name d_name
    from department d JOIN employee e ON d.id = e.dept_id WHERE d.id = #{id};
    </select>
    
    <resultMap type="com.gjs.pojo.Department" id="dept_map">
        <id property="id" column="d_id"/>
        <result property="name" column="d_name"/>
        <!--
             <collection property="emps" ofType="">
                 在標籤內部屬性進行手動映射
             </collection>
             property : 要映射的屬性
             ofType: 要映射集合泛型的類型 
             
         -->
        <collection property="emps" ofType="com.gjs.pojo.Employee">
            <id property="id" column="e_id"/>
            <result property="name" column="e_name"/>
        </collection>
    </resultMap>
</mapper>

4.MyBatis的逆向工程

  MyBatis的逆向工程能自動幫開發者生成數據庫表對應的 pojo實體文件,自動生成映射文件
  自定生成表的各類(CRUD)的sql語句, 可是隻能作單表操做,聯合查詢還得開發者本身編寫

  4.1逆向工程的插件安裝步驟

    使用逆向工程得先在Eclipse安裝逆向工程的插件

   

  

  

 

   

  判斷是否安裝成功

  

  4.2 逆向工程建立步驟

    4.2.1.新建一個項目,導入mybatis.jar包和數據庫驅動包

    4.2.2 建立生成配置文件

  generatorConfig.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
  <context id="context1">
   <!-- 註釋構建 -->
    <commentGenerator>
       <!-- 去掉全部的註釋 -->
        <property name="suppressAllComments" value="true"/>
        <property name="suppressDate" value="true"/>
    </commentGenerator>
    
    <!-- 數據庫四要素 -->
    <jdbcConnection connectionURL="jdbc:mysql://localhost:3306/mybatis" 
    driverClass="com.mysql.jdbc.Driver" 
    userId="root" 
    password="1234"/>
    <!-- 實體類 : pojo
        targetPackage : 實體類生成後存放的包
        targetProject : 存放的目錄通常都放在 src下面
      -->
    <javaModelGenerator targetPackage="com.gjs.pojo" targetProject="mybatis-generator/src" />
    <!-- 映射文件 -->
    <sqlMapGenerator targetPackage="com.gjs.mapper" targetProject="mybatis-generator/src" />
    <!-- 操做接口 
        type 生成映射的形式
            ANNOTATEDMAPPER : 純註解的,沒有xml映射
            XMLMAPPER : 生成的有xml映射文件
    -->
    <javaClientGenerator  targetPackage="com.gjs.mapper" targetProject="mybatis-generator/src" type="XMLMAPPER" />
    
    <!-- 要生成對應表的配置
        tableName : 數據庫表名
        //若是下面所有是true,mybatis直接可使用純面向對象開發
        enableCountByExample : 是否生成查詢總數的 Example 
        enableDeleteByExample : 是否生成刪除的 Example 
        enableSelectByExample : 是否生成查詢集合的 Example 
        enableUpdateByExample : 是否生成修改的 Example 
     -->
    <table  tableName="user"  enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>
    <table  tableName="employee" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>
    <table  tableName="department" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>
  </context>
</generatorConfiguration>

主要是數據庫四要素、實體類、映射文件、操做接口的配置視狀況進行修改

    4.2.3 開始逆向工程

  選中generatorConfig.xml文件右擊運行

  

  4.3.逆向功能的缺點

  逆向功能不能逆向多表操做,只能逆向單表操做,多表之間有外鍵對應java關聯關係沒辦法映射,須要開發者手動編寫對應代碼。

相關文章
相關標籤/搜索