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

目录

一. 配置元素

1. select元素

2. insert元素

3. sql元素

4. resultMap元素

5. resultMap元素中的级联

一、映射器的配置元素

元素名称 描述 备注
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元素----插入语句

元素 说明 备注
id SQL编号,用来标识这条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或false fasle
aggressiveLazyLoading 当启动时,对任意延迟属性的调用会时带有延迟加载属性的对象完整加载;反之,则每种属性按需加载 true或false 3.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中

发布了9 篇原创文章 · 获赞 0 · 访问量 88

猜你喜欢

转载自blog.csdn.net/whc__/article/details/105244976