1. 程式人生 > >mybatis源碼閱讀-高級用法(二)

mybatis源碼閱讀-高級用法(二)

The when xenu writer eterm 讀鎖 繼承 concat xen

新建學生表和學生證表

--學生表
CREATE TABLE student(
 id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT ‘id‘,
 `name` VARCHAR(20) NOT NULL  COMMENT ‘姓名‘,
 `age` INT NOT NULL COMMENT ‘年齡‘,
  sex INT NOT NULL COMMENT ‘性別 1 男 0 女‘,
  cid INT NOT NULL COMMENT ‘班級id‘,
  cardId INT COMMENT ‘學生證id‘
 )
 --學生證表
 CREATE TABLE Card
 (
  id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 
‘id‘, number INT NOT NULL COMMENT ‘學生證id‘, studentId INT NOT NULL COMMENT ‘學生id‘ )

動態標簽

include&sql

實現sql復用

<sql id="columns">
        id,name,age
    </sql>
    <!--useCache 表示使用緩存 -->
    <select id="selectOne" parameterType="string"  flushCache="false"
useCache="true" resultMap="studentAndClassMap"> select <include refid="columns"/> from student <!--模擬if標簽 --> <if test="id !=null and id!=‘‘"> where id=#{id} </if> </select>

where&if

<select id
="selectOne" parameterType="string" flushCache="false" useCache="true" resultMap="studentAndClassMap"> select <include refid="columns"/> from student <where> <if test="id !=null and id!=‘‘"> and id=#{id} </if>
<if test="name !=null and name!=‘‘">
and name=#{name}
</if> </where> </select>

where標簽裏面有if標簽成立 則自動在sql後面拼接where 同時去除多余的and

choose, when, otherwise

<select id="find" parameterType="map" resultType="student">

        select * from student  
    <!--     if else if  else -->
    <where>
        <choose>
            <when test="sex != null">
              and sex=#{sex}
           </when>
           <when test="name != null and name != ‘‘">
               and name like concat(‘%‘,#{name},‘%‘)
           </when>
        <!-- <otherwise>
           ...
        </otherwise> -->
        </choose>
    </where>
    </select>

trim, set

select * from user 

  <trim prefix="WHERE" prefixoverride="AND |OR">

    <if test="name != null and name.length()>0"> AND name=#{name}</if>

    <if test="gender != null and gender.length()>0"> AND gender=#{gender}</if>

  </trim>

滿足條件拼接 where 同時去掉前面多余的and或者or

 update user

  <trim prefix="set" suffixoverride="," suffix=" where id = #{id} ">

    <if test="name != null and name.length()>0"> name=#{name} , </if>

    <if test="gender != null and gender.length()>0"> gender=#{gender} ,  </if>

  </trim>

  滿足條件拼接set 同時去掉後面多余的, 再拼接 suffix

 update user

  <set>

    <if test="name != null and name.length()>0"> name=#{name} , </if>

    <if test="gender != null and gender.length()>0"> gender=#{gender} ,  </if>

  </set>

滿足條件拼接set 同時去掉多余的,

一對多級聯

1.學生mapper增加一個根據班級獲取學生

<select id="findByClassesId" parameterType="int" resultType="student">
        select * from student where cid=#{id}
    </select>
 public List<Student> findByStudent(Student params);

2.在classes類增加一個學生集合

public class Classes implements Serializable{
....
private List<Student> students;
}

2.classesMapper增加自定義resultMap

<resultMap id="classVoMap" type="com.liqiang.vo.ClassesVo">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <collection property="students" column="id"
            select="com.liqiang.mapper.StudentMapper.findByClassesId"></collection>
    </resultMap>
select為調用指定mapperStatement(可以理解是指定key的標簽namespace+id)
column 為將哪個列作為參數傳遞
students 返回結果賦值的屬性
可以設置 fetchType="eager(默認值 不按層級加載)|lazy" 就是有有很多級的時候 是否一下把所有級加載出來

3.在需要使用resultMap的地方指定resultMap 如根據id獲得class

<select id="findById" parameterType="Integer" resultMap="classVoMap">
   select *
   from classes where id=#{id}
</select>

多對一, 一對一級聯

1.學生表增加classes

public class Student implements Serializable{
.... private Classes classes;
}

2.學生表增加自定義resultMap

    <resultMap id="studentAndClassMap" type="student" >
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="age" column="age" />
        <result property="cid" column="cid" />
        <association property="classes" column="cid"
                     select="com.liqiang.mapper.ClassesMapper.findById"></association>
    </resultMap>

3.在需要級聯的地方resultMap改為這個

    <!--useCache 表示使用緩存 -->
    <select id="selectOne" parameterType="string"  flushCache="false"
        useCache="true" resultMap="studentAndClassMap">
        select * from student
        <!--模擬if標簽 -->
        <if test="id !=null and id!=‘‘">
            where id=#{id}
        </if>
    </select>

跟多對一一樣 只是標簽改為association

技術分享圖片

可以通過Sesstio全局設置級聯是否延遲加載

<settings>
    <setting name="lazyLoadingEnabled" value="true"></setting><!--開啟級聯延遲加載-->
      <!--不按層級延遲加載  根據get 來加載   可以在collection 或者association 加上屬性覆蓋全局  fetchType="eager(默認值 不按層級)|lazy"-->
    <setting name="aggressiveLazyLoading" value="false"></setting>

  </settings>

層級加載的意思 就是 比如學生級聯班級,班級又有學校級聯 當延遲加載情況getClass時是否也級聯加載學校,或者非延遲加載 所有的都加載出來

resultMap或者ParameterMap繼承

<resultMap id="studentMap" type="studentVo">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="age" column="age" />
        <result property="cid" column="cid" />
        
        <association property="card" column="id"
            select="com.liqiang.mapper.CardMapper.findByStudentId"></association>
    </resultMap>
    <resultMap id="studentAndClassMap" type="student" >
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="age" column="age" />
        <result property="cid" column="cid" />
        <association property="classes" column="cid"
                     select="com.liqiang.mapper.ClassesMapper.findById"></association>
    </resultMap>

如我定義了2個Resultmap 一個需要class級聯 一個只需要學生證級聯,是否發現前面幾個屬性幾乎是copy的

我們可以這樣

<resultMap id="simpleType" type="student">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="age" column="age" />
        <result property="cid" column="cid" />

    </resultMap>
    <resultMap id="studentMap" type="student" extends="simpleType">

        <association property="card" column="id"
            select="com.liqiang.mapper.CardMapper.findByStudentId"></association>
    </resultMap>
    <resultMap id="studentAndClassMap" type="student"  extends="simpleType" >

        <association property="classes" column="cid"
                     select="com.liqiang.mapper.ClassesMapper.findById"></association>
    </resultMap>

使用extends="simpleType" 繼承

Mybatis對枚舉支持

1.添加一個性別枚舉

public enum SexEnum {
    MALE(1, "男"), FEMAL(0, "女");
    private int id;
    private String name;

    private SexEnum(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

2.將學生性別改為枚舉類型

public class Student implements Serializable{
....
private SexEnum sex; }

3.resultMap的TypeHandle處理器改為mybatis提供的枚舉處理器

<resultMap id="studentMap" type="studentVo">
                ....
        <result property="sex" column="sex"
            typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler" />

自定義TypeHandle

實現TypeHandle接口就好了。然後像上面一樣typeHandle改為自己指定的,因為mybatis默認提供個TypeHandle基本夠用了。找不到例子 測試

自定義緩存

分布式 或者集群我們緩存都是存在redis或者其他nosql上面,redis緩存默認是存在當前服務器內存,

這種時候我們需要自定義緩存

public class MyCache implements org.apache.ibatis.cache.Cache{
    private String id;
    /**
     *讀的時候其他線程可以讀
     *讀的時候其他線程不能寫
     *寫的時候其他線程不能寫
     *寫的時候不能讀
     * getData(){
     *     try{
     *         readWriteLock.readLock(); 讀鎖
     *     }catch(execption e){
     *   
     *     }finally{
     *    readWriteLock.readLock().unlock();
     *     }
     * }
     * 
     * setData(){
     *  *     try{
     *         readWriteLock.writerLock(); 寫鎖
     *     }catch(execption e){
     *   
     *     }finally{
     *    readWriteLock.writerLock().unlock();
     *     }
     * }
     * 外部代理類會在get 和put 方法 加上讀寫鎖
     */
    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();//讀寫鎖 
    
    public MyCache(String id){
        System.out.println(id);//對應的mapper全名稱標示
        this.id=id;
    }
    private static Map<Object, Object> cacheManager=new HashMap<Object, Object>();//模擬redis
    //獲取緩存編號
    public String getId() {
        // TODO Auto-generated method stub
         
        return id;
    }
    //緩存數據
    public void putObject(Object key, Object value) {
        
        System.out.println("緩存對象 key為:"+key.toString());
        // TODO Auto-generated method stub
        cacheManager.put(key, value);
        
    }
   //獲取緩存
    public Object getObject(Object key) {
        // TODO Auto-generated method stub
        System.out.println("獲取對象 key為:"+key.toString());
        return cacheManager.get(key);
    }
   //刪除緩存
    public Object removeObject(Object key) {
        return cacheManager.remove(key);
    }
    //清空緩存
    public void clear() {
        cacheManager.clear();
        
    }
    //獲取緩存對象大小
    public int getSize() {
        // TODO Auto-generated method stub
        return cacheManager.size();
    }
    //獲取緩存讀寫鎖
    public ReadWriteLock getReadWriteLock() {
        // TODO Auto-generated method stub
        return readWriteLock;
    }

}

實現Cache 接口

在需要Mapper緩存的Mapper.xml配置

<!-- eviction 回收策略 LRU:最近最少使用 FIFO:先進先出 SOFT 軟引用 WEAK 移除最長時間不使用的對象 flushInterVal:刷新時間間隔 
        毫秒。不配置 執行inser update delete 才會刷新 type :自定義緩存需要實現 org.apache.ibatis.cache.Cache -->
    <cache eviction="LRU" flushInterval="100000" size="1024"
        readOnly="true" type="com.liqiang.tool.MyCache"></cache>

在需要更新緩存的地方

    <!--flushCache 是否刷新緩存 -->
    <update id="update" parameterType="student" flushCache="true">
        update student set name=#{name},sex=#{sex} where id=#{id}
    </update>

打上flushCache="true"

mybatis緩存的粒度不是很細。一刷新緩存整個表就刷新了。比如id為1的數據修改 你想只更新這個緩存,mybatis是更新整個表

mybatis源碼閱讀-高級用法(二)