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是更新整个表

原文地址:https://www.cnblogs.com/LQBlog/p/9277490.html

时间: 2024-11-07 22:44:26

mybatis源码阅读-高级用法(二)的相关文章

Mybatis源码阅读之--整体执行流程

Mybatis执行流程分析 Mybatis执行SQL语句可以使用两种方式: 使用SqlSession执行update/delete/insert/select操作 使用SqlSession获得对应的Mapper,然后调用mapper的相应方法执行语句 其中第二种方式获取Mapper的流程在前面已经解析过,请查看文章Mybatis源码阅读之--Mapper执行流程 其实这个方法最后的MapperMthod也是调用SqlSession的相应方法执行增删该的操作,这边文章主要介绍SqlSession执

【转】Java开源项目源码阅读方法及二次开发方法

一直以来,都想要阅读某些Java开源项目的源代码,甚至想要修改某些代码,实现对开源项目进行二次开发的目的.但总是不知从何入手,直接将开源项目的源代码导入Eclipse,总是会报很多错误,而无法编译.可以直接通过Eclipse打开开源项目的源代码,至少能够达到可视化源码阅读.源码导航的目的,还是能在一定程度上解决源码阅读不爽的问题,因为直接打开并没有改变源文件项目的目录结果,对于修改过后的代码,可以通过命令行找到源文件项目目录,并使用mvn或者ant对项目进行编译,再查看修改后的项目是否正确. 由

MyBatis源码阅读

编程式开发使用MyBatis 在研究MyBatis源码之前,先来看下单独使用MyBatis来查询数据库时是怎么做的: 1 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); 2 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 3 SqlSession s

【JDK1.8】JDK1.8集合源码阅读——TreeMap(二)

目录 一.前言二. TreeMap的结构三.Tree源码解析3.1 TreeMap的成员变量3.2 TreeMap的构造方法3.3 TreeMap的重要方法四.总结 一.前言 在前一篇博客中,我们对TreeMap的继承关系进行了分析,在这一篇里,我们将分析TreeMap的数据结构,深入理解它的排序能力是如何实现的.这一节要有一定的数据结构基础,在阅读下面的之前,推荐大家先看一下:<算法4>深入理解红黑树.(个人比较喜欢算法四这里介绍的红黑树实现:从2-3树到红黑树的过渡很清晰,虽然源码里的实现

mybatis源码阅读(二)

通过SqlSessionFactory 创建 SqlSession // 通过SqlSessionFactory 获取创建一个SqlsessionSqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), nu

Yii源码阅读笔记(二)

接下来阅读BaseYii.php vendor/yiisoft/yii2/Yii.php—— 1 namespace yii; 2 3 use yii\base\InvalidConfigException; 4 use yii\base\InvalidParamException; 5 use yii\base\UnknownClassException; 6 use yii\log\Logger; 7 use yii\di\Container; 第1行定义命名空间为yii: 第3到7行使用了

Yii源码阅读笔记(二十九)

动态模型DynamicModel类,用于实现模型内数据验证: namespace yii\base; use yii\validators\Validator; /** * DynamicModel is a model class primarily used to support ad hoc data validation. * DynamicModel是一种主要用于支持ad hoc数据验证模型类 * * The typical usage of DynamicModel is as fo

underscore源码阅读记录(二)

引自underscore.js context参数用法 _.each(list, iteratee, [context]); context为上下文,如果传递了context参数,则把iterator绑定到context对象上 如果要修改iterator的调用对象为context,即函数中this为context,就传递这个参数,否则context为undefined 下面两个示例,运行看看打印出来的this就明白了 var arr = [1, 2, 3]; console.log(this);

mybatis源码阅读-SqlSessionFactory(三)

我们的一个mybatis程序 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); //返回的DefaultSqlSessionFactory的实例 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder .build(ClassLoader.getSystemResourceAsStream("mybatis.x