mybatis源碼閱讀-高級用法(二)
新建學生表和學生證表
--學生表 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源碼閱讀-高級用法(二)