【MyBatis】 ---- 映射器(接口+XML文件)

目录

一. 配置元素

1. select元素
2. insert元素
3. sql元素
4. resultMap元素
5. resultMap元素中的级联

(代码github)

一、映射器的配置元素

元素名称描述备注
select查询语句可以自定义参数,返回结果集
insert插入语句执行后返回一个整数,代表插入的条数
update更新语句执行后返回一个整数,代表更新的条数
delete删除语句执行后返回一个整数,代表删除的条数
sql允许定义一部分SQL,然后在各个地方引用它比如,一张表列名,一次定义,可以在多个SQL语句中使用
resultMap用来描述从数据库结果集中来加载对象它提供映射规则
cache给定命名空间的缓存配置
cache-ref其它命名空间缓存配置的引用

1. select元素----查询语句

元素说明备注
id与Mapper的命名空间组合起来是唯一的,供MyBatis调用如果命名空间和id结合起来不唯一,将抛出异常
parameterType可以给出类的全类名,也可以给出别名,别名必须是MyBatis内部定义或者自定义可以选择Java Bean、Map等简单的参数类型传递给SQL
resultType定义类的全路径,在允许自动匹配的情况下,结果集将通过Java Bean的规范映射,不能和resultMap同时使用常用的参数之一,比如统计总条数时可以把它的值设置为int
resultMap它是映射集的引用,能提供自定义映射规则的机会可以配置映射规则、级联、typeHandler等
flushCache在调用SQL后,是否要求MyBatis清空之前查询本地缓存和二级缓存取值为布尔值,true/false,默认值为false
useCache启动二级缓存的开关,是否要求MyBatis将此次结果缓存取值为布尔值,默认为true

1.1 简单的select元素应用

RoleMapper接口

Integer countUserByFirstName(String firstName);

RoleMapper.xml

<select id="countUserByFirstName" parameterType="string" resultType="int">
        select count(*) total from t_user
        where user_name like concat(#{firstName}, '%')
</select>

1.2 传递多个参数

  • 使用map接口传递参数(不推荐)
    首先,map是一个键值对应的集合,使用者要通过阅读它的键,才能明了其作用
    其次,使用map不能限定其传递的数据类型,业务性质不强,可读性差

  • 使用注解传递多个参数
    RoleMapper

    List<Role> findRoleByAnnotation(@Param("roleName") String rolename, @Param("note") String note);
    

    RoleMapper.xml

    <select id="findRoleByAnnotation" resultType="role">
            select id,role_name as roleNam, note from t_role
            where role_name like concat('%', #{roleName}, '%')
            and note like concat('%', #{note}, '%')
    </select>
    
  • 通过Java Bean传递多个参数
    RoleMapper

    List<Role> findRolesByBean(RoleParams roleParams);
    

    RoleMapper.xml

    <select id="findRolesByBean" parameterType="cn.whc.March_30.entity.RoleParams" resultType="role">
    	        select id,role_name as roleNam, note from t_role
    	        where role_name like concat('%', #{roleName}, '%')
    	        and note like concat('%', #{note}, '%')
    	 </select>
    
  • 混合使用
    例子,查询一个角色,通过角色名称和备注进行查询,同此还支持分页

     public class PageParams {
        private int start;
        private int limit;
    
        public int getStart() {
            return start;
        }
    
        public void setStart(int start) {
            this.start = start;
        }
    
        public int getLimit() {
            return limit;
        }
    
        public void setLimit(int limit) {
            this.limit = limit;
        }
    }
    
    List<Role> findByMix(@Param("params") RoleParams roleParams, @Param("page") PageParams pageParam);
    
    <select id="findByMix" resultType="role">
            select id,role_name as roleName, note from t_role
            where role_name like
            concat ('%', #{params.roleName}, '%')
            and note like concat('%', #{params.note}, '%')
            limit #{page.start}, #{page.limit}
     </select>
    

1.3 使用resultMap映射结果集

<resultMap id="roleMap" type="cn.whc.March_30.entity.Role">
        <!--id代表主键, property代表POJO的属性名称, column代表SQL的列名-->
        <id property="id" column="id"/>
        <result property="roleName" column="role_name"/>
        <result property="note" column="note"/>
    </resultMap>

2.insert元素----插入语句

元素说明备注
idSQL编号,用来标识这条SQL命名空间+id+databaseId唯一,否则将抛出异常
parameterType可以给出类的全类名,也可以给出别名,别名必须是MyBatis内部定义或者自定义可以选择Java Bean、Map等简单的参数类型传递给SQL
flushCache是否刷新缓存,可以配置true/false,为true时,插入时会刷新一级和二级缓存,否则不刷新取值为布尔值,true/false,默认值为true
useGeneratedKeys是否启动JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键。默认值为false
keyColumn通过生成的键值设置表中的列名,这个设置仅在某些数据库中是必须的,当主键列不是表中的第一列时需要设置.如果是复合主键,需要把每一个名称用逗号隔开不能和keyProperty连用
keyProperty唯一标记一个属性,MyBatis会通过getGeneratedKeys的返回值,或者通过insert语句的selectKey子元素设置它的键值。如果是复合主键,要把每一个名称用逗号隔开默认值为unset。不能和keyColumn连用

主键回填例子

<insert id="insertRole" parameterType="role" useGeneratedKeys="true" keyProperty="id">
        insert into t_role(role_name,note) values (#{roleName}, #{note})
</insert>

keyProperty代表将用哪个POJO的属性去匹配这个主键,这里是id,会用数据库生成的主键去赋值给这个POJO

3.sql元素

<sql id="roleCols">
        id,role_name,note
</sql>

    <select id="getRole" parameterType="long" resultMap="roleMap">
        select <include refid="roleCols"/> from t_role where id = #{id}
    </select>

4.resultMap元素

作用: 定义映射规则、级联的更新、定制类型转换器等。resultMap定义的主要是一个结果集的映射关系,也就是SQL到Java Bean的映射关系定义,它也支持级联等特性。不支持更新或者保存

使用POJO存储结果集

<resultMap id="roleMap" type="cn.whc.March_30.entity.Role">
        <!--id代表主键, property代表POJO的属性名称, column代表SQL的列名-->
        <id property="id" column="id"/>
        <result property="roleName" column="role_name"/>
        <result property="note" column="note"/>
</resultMap>

5.resultMap元素中的级联

级联是resultMap中的配置,分为三种
  • 鉴别器(discriminator):它是一个根据某些条件决定采用具体实现类级联的方案,比如体检表要根据性别去区分
  • 一对一(association):比如学生证和学生就是一种一对一的级联,雇员和工牌表也是一种一对一的级联
  • 一对多(collection):比如班主任和学生就是一种一对多的级联

例子
在这里插入图片描述

  • 以雇员表为中心
  • 雇员表和工牌表是一对一的级联关系
  • 雇员表和员工任务表是一对多的级联关系
  • 员工任务表和任务表是一对一的级联关系
  • 每个雇员都会有一个体检表,随着雇员表字段性别取值的不同,会有不同的关联表

一对一级联:雇员表通过id与工牌表(emp_id)关联
在这里插入图片描述

<!--工卡表信息-->
        <association property="workCard" column="id" select="cn.whc.March_30.mapper.WorkCardMapper.getWorkCardByEmpId"/>

解析:雇员表通过编号(id)和工牌表(emp_id)关联,使用association元素,然后将结果返回给雇员POJO的属性workCard

一对一级联:雇员任务表通过任务编号(task_id)和任务表示关联

雇员任务表POJO
在这里插入图片描述

EmployeeTaskMapper.xml(雇员任务表的映射文件)

<mapper namespace="cn.whc.March_30.mapper.EmployeeTaskMapper">
    <resultMap id="EmployeeTaskMap" type="cn.whc.March_30.pojo.EmployeeTask">
        <id column="id" property="id"/>
        <result column="emp_id" property="empId"/>
        <result column="task_name" property="taskName"/>
        <result column="note" property="note"/>
        <!--雇员任务表和任务编号(task_id)和任务表示一对一级联关联-->
        <!--property属性代表映射到POJO属性上-->
        <!--分步查询:select配置是命名空间+SQL id的形式,可以指向对应Mapper的SQL,MyBatis就会通过对应的SQL将数据查询回来
        column代表雇员任务表SQL的列,用作参数传递给select属性指定的SQL,如果是多个参数,则需要用逗号隔开-->
        <association property="task" column="task_id" select="cn.whc.March_30.mapper.TaskMapper.getTask"/>
    </resultMap>

    <select id="getEmployeeTaskByEmpId" resultMap="EmployeeTaskMap">
        select id,emp_id,task_name,task_id,note from t_employee_task where emp_id = #{empId}
    </select>
</mapper>

解析:雇员任务表通过任务编号(task_id)和任务表(id)表示关联,使用association元素

一对多级联:雇员通过雇员编号(id)和雇员任务表关联
在这里插入图片描述

<!--雇员任务表信息-->
        <!--一对多级联,其select元素指向SQL,将通过雇员表中column指定的SQL字段作为参数进行传递
            然后将结果返回给雇员POJO的属性employeeTaskList
        -->
        <collection property="employeeTaskList" column="id" select="cn.whc.March_30.mapper.EmployeeTaskMapper.getEmployeeTaskByEmpId" />

解析:collection元素:一对多级联,其select指向sql,通过column指定的SQL字段作为参数进行传递,然后将结果返回给雇员POJO的属性employeeTaskList

鉴别器:雇员表和体检表

SexEnum枚举类以及自定义SexTypeHandler(以后再补充,后续放在github)

 <resultMap id="employee" type="cn.whc.March_30.pojo.Employee">
        <id column="id" property="id"/>
        <result column="real_name" property="realName"/>
        <result column="sex" property="sex" typeHandler="cn.whc.March_30.typeHandler.SexTypeHandler"/>
        <result column="birthday" property="birthday"/>
        <result column="mobile" property="mobile"/>
        <result column="email" property="email"/>
        <result column="position" property="position"/>
        <result column="note" property="note"/>
        <!--工卡表信息-->
        <association property="workCard" column="id" select="cn.whc.March_30.mapper.WorkCardMapper.getWorkCardByEmpId"/>

        <!--雇员任务表信息-->
        <!--一对多级联,其select元素指向SQL,将通过column指定的SQL字段作为参数进行传递
            然后将结果返回给雇员POJO的属性employeeTaskList
        -->
        <collection property="employeeTaskList" column="id" select="cn.whc.March_30.mapper.EmployeeTaskMapper.getEmployeeTaskByEmpId" />

        <!--鉴别器,它的属性column代表使用哪个字段进行鉴别,这里是sex,子元素case用于区分,1则对应男,2则对应女-->
        <discriminator javaType="long" column="sex">
            <case value="1" resultMap="maleHealthFormMapper"/>
            <case value="0" resultMap="femaleHealthFormMapper"/>
        </discriminator>
</resultMap>

    <!--id为employee的resultMap 被femaleHealthFormMapper通过extends元素继承-->
    <resultMap id="femaleHealthFormMapper" type="cn.whc.March_30.pojo.FemaleEmployee" extends="employee">
        <association property="femaleHealthForm" column="id" select="cn.whc.March_30.mapper.FemaleHealthFormMapper.getFemaleHealthForm"/>
    </resultMap>

    <resultMap id="maleHealthFormMapper" type="cn.whc.March_30.pojo.MaleEmployee" extends="employee">
        <association property="maleHealthForm" column="id" select="cn.whc.March_30.mapper.MaleHealthFormMapper.getMaleHealthForm"/>
    </resultMap>

解析:discriminator元素:鉴别器,它的属性column代表使用哪个字段进行鉴别,这里的是sex,而它的子元素case,用于区分,类似于java的switch…case…语句。而resultMap属性表示采用哪个ResultMap去映射,比如sex=0,则使用femaleHealthFormMapper进行映射.
对于雇员体检表而言,id为employee的resultMap,被maleHealthFormMapper和femaleHealthFormMapper通过extends元素继承。

测试
在这里插入图片描述
N+1问题
上面的级联日志可以看出所有级联都加载出来了。但是会引发性能问题,比如作为一个雇员的管理者,只想要员工信息和员工任务信息,对于体检表和工牌的信息就是多余的。所以执行了几条毫无用处的SQL,导致数据库资源的损耗和系统性能的下降。
解决方法: Mybatis提供了延迟加载功能,一次性把常用的级联数据通过SQL直接查询出来,对于不常用的级联数据则等待要用时才取出

延迟加载
settings配置项

配置项作用配置选项说明默认值
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中,可通过设置fetchType属性来覆盖该项的开关状态true或falsefasle
aggressiveLazyLoading当启动时,对任意延迟属性的调用会时带有延迟加载属性的对象完整加载;反之,则每种属性按需加载true或false3.4.1版本后默认false

lazyLoadingEnabled是一个开关,决定开不开启延迟加载,默认值为false,则不开启延迟加载。正如上面例子,当获取员工信息时,所有信息都被加载进来。

测试1:lazyLoadingEnabled和aggressiveLazyLoading都为true

<settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="true"/>
</settings>
@Test
    public void test() {
        try {

            Logger logger = Logger.getLogger(AppTest.class);
            sqlSession = SqlSessionFactoryUtils.openSqlSession();
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            // 获取编号为1的雇员信息,但设置aggressiveLazyLoading为true后,由于任务表只与雇员任务表关联,没有被加载
            Employee employee = employeeMapper.getEmployee(1L);
            logger.info(employee.getBirthday());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

在这里插入图片描述
在这里插入图片描述

测试2:lazyLoadingEnabled为true;aggressiveLazyLoading为false
开启全局延迟,层级加载失效

<settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
</settings>

在这里插入图片描述
测试3:fetchType属性
上面的两种测试都是全局性配置,并不能解决我们的需求,我们希望加载雇员信息时,只加载雇员任务信息,因为层级加载会把工牌信息也加载进来。在MyBatis中使用fetchType属性,可以处理全局定义无法处理的问题

 <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
<collection property="employeeTaskList" column="id" select="cn.whc.March_30.mapper.EmployeeTaskMapper.getEmployeeTaskByEmpId" fetchType="eager" />

在这里插入图片描述
fetchType出现在级联元素(association、collection,注意:discriminator没有这个属性可配置)
属性值

  • eager:获得当前POJO后立即加载对应的数据
  • lazy:获得当前POJO后延迟加载对应的数据

另一种级联:采用连缀
SQL比较复杂;所需要的配置比之前复杂得多;一次性将所有的数据取出会造成内存的浪费。适用于比较简单且关联关系不多的场景下。

==============================================================================

TODO

这一节的内容还是挺多的,还没写完,后面的放在另一篇文章,这两天上课+晚睡觉,补午觉都到下午3 4点了,另外我的csdn网页版有时候就打不开(估计dns域名出问题了,坑了n次)
后面打算写一下idea命令行如何将项目推送到github中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值