day3 - mybatis基本查询映射配置&更新映射和缓存机制&查询结果的封装和高级映射&动态SQL

主要内容
1. 应用<select>元素实现数据查询
2. <insert><update><delete>元素更新数据
3. 应用<sql>元素实现重用,应用缓存设置
4. 查询返回结果的封装处理
5. 动态SQL应用

1. 应用<select>元素实现数据查询

select元素用来从数据库中查询数据结果,一个最简单的select实现方式如下所示:


1.1 使用select元素实现数据查询

1.1 <select>元素属性说明

select元素属性规范查询中的动作。诸如,如何传递查询参数,映射返回结果,是否使用缓存,查询超时限定等特性。

<select>常用的属性

1.2 select常用的属性

1.2 使用parameterType设置参数

  • parameterType是int, resultType是某个类的例子
    <select id="findStuByAge" resultType="stu" parameterType="int">
        <![CDATA[select * from student where age > #{age}]]>
    </select>

代码实现

    public List<Student> findStuByAge(int age){
        return SqlSessionFactoryManager.getSqlSession().selectList("students.findStuByAge",age);
    }
  • parameterType是string, resultType是map的例子
    <select id="findStuByEmail" resultType="map" parameterType="string">
        <![CDATA[select * from student where email = #{email}]]>
    </select>

代码实现

    public Map<String, Object> findStuByEmail(){
        String email = "123@163.com";
        return SqlSessionFactoryManager.getSqlSession().selectOne("students.findStuByEmail",email);
    }
  • parameterType是map, resultType是某个类的例子
    <select id="findStuByArgs" resultType="stu" parameterType="map">
        <![CDATA[select * from student where email <> #{email} and age > #{age}
        and birth > #{birth}
        ]]>
    </select>

代码实现

    public List<Student> findStuByArgs(){
        String email = "123@163.com";
        int age = 18;
        String birth = "1900-01-01";
        Map<String, Object> map = new HashMap<>();
        map.put("email",email);
        map.put("age",age);
        map.put("birth",birth);
        return SqlSessionFactoryManager.getSqlSession()
                .selectList("students.findStuByArgs",map);
    }

2<insert><update><delete>元素更新数据

2.1 <insert><update>通用属性

除了具有<select>等元素的通用属性外,<insert>和<update>还具有以下相同常用属性:


2.1 insert&update通用属性

keyProperty和keyColumn相互对应。

2.2 使用<insert>元素映射insert语句

保存一个department表的信息到数据库中
mapper.xml中的内容:

    <insert id="saveDepartment" parameterType="map" keyColumn="ID"
            keyProperty="id" statementType="PREPARED">
        <![CDATA[insert into department(id, name, code, newdate, descs)
            values(#{id},#{name},#{code},#{newdate},#{descs})]]>
    </insert>

代码实现:

    public int saveDepartment(Map<String, Object> map){
        System.out.println("执行保存");
        //获取sqlSession实例
        SqlSession sqlSession = SqlSessionFactoryManager.getSqlSession();
        int res = sqlSession.insert("deps.saveDepartment",map);
        sqlSession.commit();
        sqlSession.close();
        return res;
    }

2.3 使用<update>元素映射update语句

根据指定id更新数据库的表
mapper.xml中的内容:

    <update id="updateDepartmentById" parameterType="map" flushCache="true" statementType="PREPARED">
        <![CDATA[
            update department
            set descs = #{descs}, name = #{name}
            where id = #{id}]]>
    </update>

代码实现:

    /**
     * 实现按照部门id更新部门信息
     * @param map 部门map
     * @return
     */
    public int updateDepartmentById(Map<String, Object> map){
        System.out.println("执行修改");
        //获取sqlSession实例
        SqlSession sqlSession = SqlSessionFactoryManager.getSqlSession();
        int res = sqlSession.update("deps.updateDepartmentById",map);
        sqlSession.commit();
        sqlSession.close();
        return res;
    }

2.4 使用<delete>元素映射delete语句

根据给定的id删除department的内容
mapper.xml中的内容:

    <delete id="removeDepById" parameterType="string" flushCache="true" statementType="PREPARED">

        <![CDATA[
            delete from department
            where id = #{id}]]>
    </delete>

代码实现:

    /**
     * 按照指定的部门id删除此部门
     * @param depid 部门id
     * @return int
     */
    public int removeDepById(String depid){
        System.out.println("执行删除");
        //获取sqlSession实例
        SqlSession sqlSession = SqlSessionFactoryManager.getSqlSession();
        int res = sqlSession.delete("deps.removeDepById", depid);
        sqlSession.commit();
        sqlSession.close();
        return res;
    }

3.应用<sql>元素实现重用,应用缓存设置

3.1 <sql>元素实现重用

//定义一个重复使用的sql语句块

    <!--可重用的sql块,可以被当前映射中任何select, update及insert delete应用-->
    <sql id="DEPCommons">
        id, name, code, newdate, descs </sql>

//其他语句中调用

    <!--使用include引用sql元素不能使用常量语义符号包围, 否则无法进行解析-->
    <select id="queryDepList" resultType="dep" fetchSize="3">
        select <include refid = "DEPCommons"/> from department limit 0,3;
        <!--<![CDATA[select * from department limit 0,3;]]>-->
    </select>

3.2 应用缓存设置

数据缓存可以增加数据的访问操作效率,myBatis提供了设置sql的缓存策略和在sql映射中引用缓存策略。
myBatis使用cache元素在sql映射中设置缓存策略及使用ref-cache引用缓存策略。

3.2.1 myBatis缓存机制
3.1 myBatis缓存机制

myBatis一级缓存
一级缓存中每个SqlSession独享缓存数据

导致一级缓存查询失败的因素

3.2 myBatis一级缓存查询失败的因素

myBatis二级缓存
二级缓存中每个mapper中SqlSession会共享缓存数据

设置sql缓存

3.3.1 设置sql缓存

mybatis提供的几种缓存策略类型:

3.3.1 mybatis提供的几种缓存策略类型

应用缓存

cache-ref元素实现从一个mapper中引用当前的mapper中的缓存策略并应用。


3.3.2 应用缓存

导致二级缓存查询失败的原因

3.3.3 导致二级缓存查询失败的原因

二级缓存的缺陷

3.4 二级缓存的缺陷.png

缓存策略

使用第三方专业缓存工具代替myBatis二级缓存[Ehcache, Redis]

4. 查询返回结果的封装处理

myBatis通常在映射<select>元素执行sql使用returnType设置返回结果类型,有时需要对查询返回结果进行特殊处理;<select>元素提供了returnMap属性为查询返回结果进行处理的更灵活方式。
returnMap属性是对sql映射中某个returnMap元素的引用,而returnMap元素的定义决定了如何处理查询返回结果。

4.1 resultMap基本映射应用

Q: 数据库表student列名称和Student.Java类型属性名称不匹配,导致无法直接使用returnType指定为Student Java类型
S: 使用resultMap元素自定义返回结果的处理并映射记录行为Student Java类型,使用resultMap属性设置引用resultMap元素。
E:

    <!--使用列标签与实体bean属性保持一致达到自动映射的目的-->
    <select id="queryProductList" resultType="pro" useCache="true" statementType="PREPARED">
        <![CDATA[
        select id proId,code proCode,name proName,price proPrice,quality proQuantity,descs proDescs from product;
        ]]>
    </select>

    <resultMap id="ProMapping" type="pro" autoMapping="false">
        <id column="ID" property="proId" javaType="string" jdbcType="VARCHAR"/>
        <result column="CODE" property="proCode" javaType="string" jdbcType="VARCHAR"/>
        <result column="NAME" property="proName" javaType="string" jdbcType="VARCHAR"/>
        <result column="PRICE" property="proPrice" javaType="double" jdbcType="DOUBLE"/>
        <result column="QUALITY" property="proQuantity" javaType="integer" jdbcType="INTEGER"/>
        <result column="DESCS" property="proDescs" javaType="string" jdbcType="VARCHAR"/>
    </resultMap>

resultMap和resultType封装过程中只会有一个存在

4.2 resultMap元素说明

4.2.1 resultMap属性
4.2.1 resultMap元素属性
4.2.2 resultMap子元素
4.2.2 resultMap元素子元素
  • 高级复杂结果关联映射(一对多):collection的使用

使用resultMap元素映射复杂的一对多关联

    <select id="structTreeMap" resultMap="dep_emp_sal_map" statementType="PREPARED">
        <![CDATA[
        select d.id did, d.name dname, e.id eid, e.name ename,
               s.id sid, s.should, s.actual, s.mapYearMon
        from department d inner join emp e on d.id = e.DEPID
                          inner join salary s on e.id = s.empId;
        ]]>
    </select>

    <!--使用resultMap元素映射复杂的一对多关联-->
    <resultMap id="dep_emp_sal_map" type="dep" autoMapping="false">
        <id column="did" property="id" javaType="string" jdbcType="VARCHAR"/>
        <result column="dname" property="name" javaType="string" jdbcType="VARCHAR"/>
        <collection property="employeeList" autoMapping="false" ofType="emp">
            <id column="eid" property="id" javaType="string" jdbcType="VARCHAR"/>
            <result column="ename" property="name" javaType="string" jdbcType="VARCHAR"/>
            <collection property="salaryList" ofType="sal" autoMapping="false">
                <id column="sid" property="id" javaType="string" jdbcType="VARCHAR"/>
                <result column="should" property="should" javaType="integer" jdbcType="INTEGER"/>
                <result column="actual" property="actual" javaType="integer" jdbcType="INTEGER"/>
                <result column="mapYearMon" property="mapYearMon" javaType="date" jdbcType="DATE"/>
            </collection>
        </collection>
    </resultMap>

使用resultMap实现关联映射,实体数据使用map进行封装,避免定义更多的VO类

    <!--将一对多关联数据实体映射为map-->
    <select id="structTreeMap2" resultMap="depAndempAndsalAndmap" statementType="PREPARED">
        <![CDATA[
        select d.id did, d.name dname, e.id eid, e.name ename,
               s.id sid, s.should, s.actual, s.mapYearMon
        from department d inner join emp e on d.id = e.DEPID
                          inner join salary s on e.id = s.empId;
        ]]>
    </select>

    <!--使用resultMap元素映射复杂的一对多关联,实体数据使用Map进行封装,减少使用实体类-->
    <resultMap id="depAndempAndsalAndmap" type="treeMap" autoMapping="false">
        <id column="did" property="id" javaType="string" jdbcType="VARCHAR"/>
        <result column="dname" property="name" javaType="string" jdbcType="VARCHAR"/>
        <collection property="employeeList" autoMapping="false" ofType="treeMap">
            <id column="eid" property="id" javaType="string" jdbcType="VARCHAR"/>
            <result column="ename" property="name" javaType="string" jdbcType="VARCHAR"/>
            <collection property="salaryList" ofType="treeMap" autoMapping="false">
                <id column="sid" property="id" javaType="string" jdbcType="VARCHAR"/>
                <result column="should" property="should" javaType="integer" jdbcType="INTEGER"/>
                <result column="actual" property="actual" javaType="integer" jdbcType="INTEGER"/>
                <result column="mapYearMon" property="mapYearMon" javaType="date" jdbcType="DATE"/>
            </collection>
        </collection>
    </resultMap>

提示: id元素是嵌套映射中非常重要的角色,通常必须要映射它,如果不使用id作为标识结果,性能会有很大损耗。

  • 高级复杂结果关联映射(一对一): myBatis提供resultMap元素的association子元素实现一对一关联映射
  1. 使用association元素的select属性指向一条sql查询语句描述如何封装结果
  2. 使用association元素的resultMap属性指向association元素描述如何封装结果(效率高)

使用association元素的select属性指向一条sql查询语句描述如何封装结果

    <!-- 查询员工 -->
    <select id="findEmpAndDoc" resultMap="findEmpMap" statementType="PREPARED">
        select  * from emp 
    </select>

    <resultMap id="findEmpMap" type="emp" autoMapping="false">
        <id column="id" javaType="string" jdbcType="VARCHAR" property="id"/>
        <result column="name" javaType="string" jdbcType="VARCHAR" property="name"/>
        <!--column指定应该如何进行映射-->
        <association property="doc" autoMapping="false" column="id" javaType="doc"
                     select="findDocMap"/>
    </resultMap>

    <!--查询档案-->
    <select id="findDocMap" resultType="doc" parameterType="string" statementType="PREPARED">
        select  * from document where empid = #{empid};
    </select>

使用association元素的resultMap属性指向association元素描述如何封装结果(效率高, 仅仅去数据库之中查询一次)

    <!-- 查询员工对象和对应的档案对象集合 -->
    <select id="findEmployeeAndDocument" resultMap="findEmpMap2" statementType="PREPARED">
        select e.id eid,e.name, d.id did, d.number from emp e inner join document d on
            e.id = d.empid;
    </select>

    <!--映射员工查询返回结果-->
    <resultMap id="findEmpMap2" type="emp" autoMapping="false">
        <id column="eid" javaType="string" jdbcType="VARCHAR" property="id"/>
        <result column="name" javaType="string" jdbcType="VARCHAR" property="name"/>
        <association property="doc" javaType="doc" autoMapping="false"
                     resultMap="docMap" column="empid"/>
    </resultMap>

    <!--映射用户档案查询返回结果-->
    <resultMap id="docMap" type="doc" autoMapping="false">
        <id column="did" javaType="string" jdbcType="VARCHAR" property="id"/>
        <result column="number" javaType="string" jdbcType="VARCHAR" property="number"/>
    </resultMap>

5. 动态SQL应用

5.1 动态SQL应用

5.1 if

if元素是构成动态SQL语句非常重要的和使用比较频繁的元素,它表示执行一个逻辑判断,如果为true则执行if元素之内的内容,if元素有唯一的一个test属性用来接收一个布尔表达式,if经常用来组织where语句的连接生成

    <!--动态sql if的使用-->
    <select id="queryEmpListByGender" parameterType="int" resultType="emp" statementType="PREPARED">
        select id,name,birth,phone from emp
        <if test="'gender' != null">
            where gender = #{gender}
        </if>
    </select>

    <!--动态sql if的使用2-->
    <select id="queryEmpListByMap" parameterType="map" resultType="emp" statementType="PREPARED">
        select id,name,birth,phone from emp
        <if test="gender != null">
            where gender = #{gender}
        </if>
        <if test="name != null">
            and name like concat(#{name},"%")
            /*and name like '${name}%'*/
        </if>

    </select>

5.2 choose, when, otherwise

choose when otherwise类似于Java中的switch分支语句

    <!--choose when otherwise 动态sql-->
    <select id="queryEmpListByMap2" parameterType="map" resultType="emp" statementType="PREPARED">
        select id,name,birth,phone from emp where 1 = 1
        <choose>
            <when test="gender != null">and gender = #{gender}</when>
            <when test="name != null">and name not like '${name}%'</when>
            <when test="start != null and ends != null">
                and birth between #{start} and #{ends}
            </when>
            <otherwise>
                <if test="id != null">
                    and id = #{id}
                </if>
            </otherwise>
        </choose>
    </select>

5.3 trim, where, set

5.3.1 where

where元素用来代替用户手写的where sql关键字,它基本可以解决没有给定条件时而出现错误sql语法
where元素能够知道如果它包含的标签中有返回值的话,它就插入一个where, 如果标签返回的内容以AND或OR开头的,则它会剔除掉

示例:

    <!--动态sql 使用where元素代替手写where 避免手写where出错-->
    <select id="queryEmpListByMap3" parameterType="map" resultType="emp" statementType="PREPARED">
        select id,name,birth,phone from emp
        <where>
            <choose>
                <when test="gender != null">and gender = #{gender}</when>
                <when test="name != null">and name not like '${name}%'</when>
                <when test="start != null and ends != null">
                    and birth between #{start} and #{ends}
                </when>
                <otherwise>
                    <if test="id != null">
                        and id = #{id}
                    </if>
                </otherwise>
            </choose>
        </where>
    </select>
5.3.2 trim

trim元素可以用来代替where set等元素设置自定义动态sql语句,它有三个属性:
prefix: 指定前缀
suffix: 指定后缀
prefixOverrides: 设置分隔符(空格也属于分隔符之列)

示例:

    <!--动态sql trim 元素替代 where-->
    <select id="queryEmpListByMap4" parameterType="map" resultType="emp" statementType="PREPARED">
        select id,name,birth,phone from emp
        <trim prefix="where" prefixOverrides="and|or">
            <if test="gender != null">gender = #{gender}</if>
            <if test="name != null">and name like concat(#{name},'%')</if>
        </trim>
    </select>
5.3.3 set

set元素用来实现update语句的更新,它取代set关键字设置更新列信息。

示例:

    <!--使用set元素代替手写set更新sql语句关键字-->
    <update id="modifyEmployee" parameterType="emp" statementType="PREPARED">
        update emp
        <set>
            <if test="name != null">name = #{name},</if>
            <if test="gender != null">gender = #{gender},</if>
            <if test="address != null">address = #{address}</if>
        </set>
        where id = #{id}
    </update>

5.4 foreach

foreach是一个迭代功能的动态sql元素,通常在条件包含in或not in时使用其填充条件。
MyBatis会自动将有序集合及数组包装成一个Map处理,并使用它的名称作为key。List实例将使用"list"作为键,数组实例以"array"作为键。

示例:

    <!--使用foreach迭代参数集合-->
    <select id="queryEmpListForAddress" resultType="emp" statementType="PREPARED">
        select * from emp where address in
            <foreach collection="list" item="ele" index="index" separator=","
            open="(" close=")">
                #{ele}
            </foreach>
    </select>

    <!--使用foreach迭代参数集合(多参数)-->
    <select id="queryEmpListForAddress2" resultType="emp" parameterType="map" statementType="PREPARED">
        select * from emp where
        birth between #{startTime} and #{endTime} and address in 
        <foreach collection="addList" index="index" item="ele" separator=","
                 open="(" close=")">
            #{ele}
        </foreach>
    </select>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351