MyBatis(三)MyBatis-映射文件

一 介绍

映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义;

  • cache –命名空间的二级缓存配置
  • cache-ref – 其他命名空间缓存配置的引用。
  • resultMap – 自定义结果集映射
  • parameterMap – 已废弃!老式风格的参数映射
  • sql –抽取可重用语句块。
  • insert – 映射插入语句
  • update– 映射更新语句
  • delete– 映射删除语句
  • select – 映射查询语句

二 主键生成方式

  • 若数据库支持自动生成主键的字段(比如 MySQL和 SQL Server),则可以设置useGeneratedKeys=”true”,然后再把keyProperty 设置到目标属性上。
    <insert id="insertUser" parameterType="com.zyc.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into t_user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>

还可以
将insert插入的数据的主键返回到User对象中

        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            select last_insert_id()
        </selectKey>

mysql的uuid(),实现非自增主键的返回

           <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
               select uuid()
           </selectKey>
           insert into t_user (id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address})
  • 而对于不支持自增型主键的数据库(例如Oracle),则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会`被设置·,然后插入语句会被调用

    <insert id="addEmployee" databaseId="oracle">
        <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
            select EMPLOYEE_SEQ.nextval from dual
        </selectKey>
        <!-- 插入的主键是从序列中获取的 -->
            insert into employee(id,last_name,age,email)
                values(#{id},#{lastName},#{age},#{email})
     </insert>

三 参数传递

单个参数

mybatis不会做特殊处理,#{参数名/任意名}:取出参数值(测试得:真的任意名都可以)

多个参数

mybatis会做特殊处理,多个参数会被封装成 一个map
key:param1...paramN,或者arg0...argN-1

    <select id="findUserById" parameterType="int" resultType="com.zyc.entity.User">
        select username,DATE_FORMAT(birthday,'%Y-%m-%d')birthday,sex,address from t_user where id=#{id}
    </select>

命名参数

明确指定封装参数时map的key;@Param("id")

public User findUserByIdAndName(@Param("id")int id, @Param("name")String name);

映射文件

    <select id="findUserByIdAndName"  resultType="com.zyc.entity.User">
        select username,DATE_FORMAT(birthday,'%Y-%m-%d')birthday,sex,address from t_user where id=#{id} and username = #{name}
    </select>

POJO

如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
#{属性名}:取出传入的pojo的属性值

    <select id="findUserByUser" parameterType="com.zyc.entity.User" resultType="com.zyc.entity.User">
        select username,DATE_FORMAT(birthday,'%Y-%m-%d')birthday,sex,address from t_user where id=#{id}
    </select>

Map

这个是我们自己传得map,不是上面多参数是mybatis帮我们处理得。所以可以直接通过key来获取

    <select id="findUserByMap"  resultType="com.zyc.entity.User">
        select username,DATE_FORMAT(birthday,'%Y-%m-%d')birthday,sex,address from t_user where id=#{id}
    </select>

集合类型参数

#{list[0]},数组就是#{array[0]}

    <select id="findUserByList"  resultType="com.zyc.entity.User">
        select username,DATE_FORMAT(birthday,'%Y-%m-%d')birthday,sex,address from t_user where id=#{list[1]}
    </select>

四 #和$

#{key}:获取参数的值,预编译到SQL中(因为预编译,最好用在where里面,测试发现:在编译之后加了引号)。安全。
${key}:获取参数的值,拼接到SQL中。有SQL注入问题。

五 resultType

简单类型

<select id="getEmpNameById" resultType="string">

POJO

使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。(验证通过)

<select id="findUserByMap"  resultType="com.zyc.entity.User">

List类型

没什么区别
Mapper

public List<User> findUserByName(String username);

映射文件

    <select id="findUserByList"  resultType="com.zyc.entity.User">

Map类型

查询一条记录

将查询的数据以{表字段名, 对应的值}方式存入到Map中。

Mapper

Map<String, Object> getEmpAsMapById(Integer id);

映射文件

  <select id="getEmpAsMapById" resultType="map">
        select * from t_employee where id = #{id}
    </select>

查询多条记录

可以把查询的数据以{表中某一字段名, JavaBean}方式来封装成Map。
mapper

    // 查询所有员工的信息,把数据库中的 'id' 字段作为 key,对应的 value 封装成 Employee 对象
    // @MapKey 中的值表示用数据库中的哪个字段名作 key
    @MapKey("id")
    Map<Integer, Employee> getAllEmpsAsMap();

映射文件

    <!--
        注意 resultType 返回值类型,不再是 'map',而是 Map 的 value 对应的 JavaBean 类型
    -->
    <select id="getAllEmpsAsMap" resultType="employee">
        select * from t_employee
    </select>

自动映射

全局setting设置

  • autoMappingBehavior默认是PARTIAL,开启自动映射的功能。唯一的要求是列名和javaBean属性名一致
  • 如果autoMappingBehavior设置为null则会取消自动映射
  • 数据库字段命名规范,POJO属性符合驼峰命名法,如A_COLUMNaColumn,我们可以开启自动驼峰命名规则映射功能,mapUnderscoreToCamelCase=true

六 自定义resultMap

可以实现高级结果集映射

范例


    <!--自定义某个javaBean的封装规则
        type:自定义规则的Java类型
        id:唯一id方便引用
    -->
    <resultMap type="com.zyc.entity.User" id="rm1">
        <!--指定主键列的封装规则
        id定义主键会底层有优化;
        column:指定哪一列
        property:指定对应的javaBean属性
          -->
        <id column="id" property="id"/>
        <!-- 定义普通列封装规则 -->
        <result column="username" property="username"/>
        <!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上。 -->
    </resultMap>

    <!-- resultMap:自定义结果集映射规则;  -->
    <!-- public Employee getEmpById(Integer id); -->
    <select id="findUserByRm1"  resultMap="rm1">
         select id,username ,DATE_FORMAT(birthday,'%Y-%m-%d')birthday,sex ,address from t_user where id=#{id}
    </select>

"一对一"

查询Employee的同时查询员工对应的部门,就像hibernate中得一对一

sql语句

    <select id="getEmpAndDept" resultMap="MyDifEmp">
        SELECT
            e.id,
            e.lastname,
            e.gender,
            d.id did,
            d.dept_name
        FROM
            tbl_employee e,
            tbl_dept d
        WHERE
            e.d_id = d.id
        AND e.id = #{id}
    </select>

级联属性

    <!--
        联合查询:级联属性封装结果集
      -->
    <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="gender" property="gender"/>
        <result column="did" property="dept.id"/>
        <result column="dept_name" property="dept.departmentName"/>
    </resultMap>

使用association

    <!-- 
        使用association定义关联的单个对象的封装规则;
     -->
    <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp2">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="gender" property="gender"/>
        
        <!--  association可以指定联合的javaBean对象
        property="dept":指定哪个属性是联合的对象
        javaType:指定这个属性对象的类型[不能省略]
        -->
        <association property="dept" javaType="com.atguigu.mybatis.bean.Department">
            <id column="did" property="id"/>
            <result column="dept_name" property="departmentName"/>
        </association>
    </resultMap>

association分步查询

  1. 先按照员工id查询员工信息
  2. 根据查询员工信息中的d_id值去部门表查出部门信息
  3. 部门设置到员工中;
     <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpByStep">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
        <!-- association定义关联对象的封装规则
            select:表明当前属性是调用select指定的方法查出的结果
            column:指定将哪一列的值传给这个方法
            
            流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
         -->
        <association property="dept" 
            select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
            column="d_id">
        </association>
     </resultMap>

延迟加载(懒加载)

主配置文件

    <!-- 可以使用延迟加载(懒加载);(按需加载)
        Employee==>Dept:
            我们每次查询Employee对象的时候,都将一起查询出来。
            部门信息在我们使用的时候再去查询;
            分段查询的基础之上加上两个配置:
      -->


    <settings>
        <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
        <setting name="jdbcTypeForNull" value="NULL"/>
        
        <!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题  -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

"一对多"

例如:查询部门的时候将部门对应的所有员工信息也查询出来

sql语句

    <select id="getEmpsByDeptId" resultType="com.atguigu.mybatis.bean.Employee">
        select * from tbl_employee where d_id=#{deptId}
    </select>

一次查询

    <!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则  -->
    <resultMap type="com.atguigu.mybatis.bean.Department" id="MyDept">
        <id column="did" property="id"/>
        <result column="dept_name" property="departmentName"/>
        <!-- 
            collection定义关联集合类型的属性的封装规则 
            ofType:指定集合里面元素的类型
        -->
        <collection property="emps" ofType="com.atguigu.mybatis.bean.Employee">
            <!-- 定义这个集合中元素的封装规则 -->
            <id column="eid" property="id"/>
            <result column="last_name" property="lastName"/>
            <result column="email" property="email"/>
            <result column="gender" property="gender"/>
        </collection>
    </resultMap>
    <!-- public Department getDeptByIdPlus(Integer id); -->
    <select id="getDeptByIdPlus" resultMap="MyDept">
        SELECT d.id did,d.dept_name dept_name,
                e.id eid,e.last_name last_name,e.email email,e.gender gender
        FROM tbl_dept d
        LEFT JOIN tbl_employee e
        ON d.id=e.d_id
        WHERE d.id=#{id}
    </select>

分步查询

    <!-- collection:分段查询 -->
    <resultMap type="com.atguigu.mybatis.bean.Department" id="MyDeptStep">
        <id column="id" property="id"/>
        <id column="dept_name" property="departmentName"/>
        <collection property="emps" 
            select="com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
            column="{deptId=id}" fetchType="lazy"></collection>
    </resultMap>
    <!-- public Department getDeptByIdStep(Integer id); -->
    <select id="getDeptByIdStep" resultMap="MyDeptStep">
        select id,dept_name from tbl_dept where id=#{id}
    </select>

扩展:传递多列得值

  • 分步查询的时候通过column指定,将对应的列的数据传递过去,我们有时需要传递多列数据。使用{key1=column1,key2=column2…}的形式
  • association或者collection标签的fetchType=eager/lazy可以覆盖全局的延迟加载策略,指定立即加载(eager)或者延迟加载(lazy)

鉴别器

discriminator

<!-- <discriminator javaType=""></discriminator>
        鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
        封装Employee:
            如果查出的是女生:就把部门信息查询出来,否则不查询;
            如果是男生,把last_name这一列的值赋值给email;
     -->
     <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpDis">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
        <!--
            column:指定判定的列名
            javaType:列值对应的java类型  -->
        <discriminator javaType="string" column="gender">
            <!--女生  resultType:指定封装的结果类型;不能缺少。/resultMap-->
            <case value="0" resultType="com.atguigu.mybatis.bean.Employee">
                <association property="dept" 
                    select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
                    column="d_id">
                </association>
            </case>
            <!--男生 ;如果是男生,把last_name这一列的值赋值给email; -->
            <case value="1" resultType="com.atguigu.mybatis.bean.Employee">
                <id column="id" property="id"/>
                <result column="last_name" property="lastName"/>
                <result column="last_name" property="email"/>
                <result column="gender" property="gender"/>
            </case>
        </discriminator>
     </resultMap>

参考

  1. 尚硅谷mybatis教材
  2. MyBatis查询结果resultType返回值类型详细介绍
  3. https://www.cnblogs.com/xiaoxi/p/6378960.html
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容