MyBatis(六)------MyBatis映射器(select元素、insert元素、update元素、select元素、sql元素)

2021年9月30日 25点热度 0条评论 来源: 洒家肉山大魔王

目录

1、概述

2、select元素

 2.1 简单的select元素的应用

 2.2 自动映射和驼峰映射

 2.3 传递多个参数

2.3.1 使用map接口传递参数

2.3.2 使用注解传递参数

2.3.3 使用Java Bean传递参数

2.3.4 使用混合方式传递参数

 2.4 使用resultMap映射结果集

 2.5 分页参数RowBounds

3、insert元素

 3.1 简单的insert语句的应用

 3.2 主键回填

 3.3 自定义主键

4、update元素和delete元素

5、sql元素

 

映射器是MyBatis最为复杂且最重要的组件。它由一个接口和XML文件(或注解)组成。在映射器中可以配置参数、SQL语句、存储过程、缓存、级联等,并能够通过简易的映射规则映射到指定的POJO或其他对象上,映射器能够有效消除JDBC底层的代码。

整个MyBatis应用程序开发中,映射器的开发量占全部工作量的80%。MyBatis的映射器可以使用注解完成,但是应用范围并不广,主要原因:【1】面对复杂性,SQL会显得无力,尤其是面对较长的SQL;【2】注解的可读性较差;【3】在功能上,注解丢失了XML上下文相互引用的功能。基于实际情况,我们主要讨论XML的实现方式。

1、概述

映射器的配置元素:

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

2、select元素

select元素代表SQL的select语句,用于查询。看一下select元素的配置。

         元素                                               描述                            备注
id id和Mapper的命名空间组合起来是唯一的,供MyBatis使用 如果命名空间和id结合起来不唯一,MyBatis将抛出异常
parameterType 给出类的全命名,也可以给出别名,但是别名必须是MyBatis内部定义或者自定义的 可以选择Java Bean、Map等简单的参数类型传递给SQL
resultType

定义类的全路径,在允许自动匹配的情况下,结果集将通过Java Bean的规范映射;

或定义为int、double、float、map等参数

也可以使用别名,但是要符合别名规范,且不能和resultMap同时使用

常用的参数之一,比如统计总条数时可以把它的值设置为int
resultMap 它是映射集的引用,将执行强大的映射功能。resultType和resultMap不能同时使用 最复杂的元素,可以配置映射规则、级联、typeHandler等
flushCache 在调用SQL后,是否要求MyBatis情况之前查询本地缓存和二级缓存 取值为布尔类型,默认为false
useCache 启动二级缓存的开关,是够要求MyBatis将此次结果缓存 布尔类型,默认为true
timeout 设置超时参数,超时时将抛出异常,单位为秒 默认为数据库厂商提供的JDBC驱动所设置的秒数
fetchSize 获取记录的总条数设定 默认为数据库厂商提供的JDBC驱动所设置的条数
statementType 使用哪个JDBC的Statement工作,取值为STATEMENT、PREPARED、CALLABLE 默认为PREPARED
resultSetType —— 默认为数据库厂商提供的JDBC驱动所设置
databaseId 数据库厂商标识设置 多数据库支持
resultOrdered 用于嵌套结果select语句 布尔类型,默认为false
resultSets 用于多结果集的情况,将列出执行SQL后每个结果集的名称,每个名称之间用逗号分隔 较少使用

 2.1 简单的select元素的应用

一个栗子,统计用户表中同一个姓氏的数量:

<select id="" parameterType="String" resultType="sysRole">
    select count(*) from sys_role
    where user_name like concat('%',#{firstName},'%')
<select/>
  •  id:它配合Mapper的全限定名,联合成一个唯一的标识,用于标识这条SQL
  • parameterType:表示这条SQL接受的参数类型【即传入的参数的类型】,可以是MyBatis系统定义或者自定义的别名;
  • resultType:表示这条SQL语句返回的结果集的类型,与parameterType一样,可以是系统或者自定义的别名,也可以是类的全限定名;
  • #{firstName}:如果传入的是pojo实体类型,占位符中的变量名称必须是pojo实体类中对应的属性.属性.属性...。起到占位作用,如果传入的是基本类型(string,long,double,int,boolean,float等),那么#{}中的变量名称可以随意写。  

仅仅只有这一条SQL语句,还不够,我们还需要一个接口程序才能运行起来:

public intserface SysRoleMapper{
    public Integer getRoleFirstName(String firstName);
}

这个栗子仅仅是让我们认识select元素的基础属性和用法,在实际的开发情况中,我们所遇到的问题比这条SQL更加复杂。 

 2.2 自动映射和驼峰映射

MyBatis提供了自动映射的功能,在默认情况下自动映射是开启的,使用它能够有效地减少大量的映射配置,进而减少体力劳动。

关于它们的配置主要在MyBatis的基础文件mybatisConfig.xml中进行配置。在<settings><settings/>元素标签内有两个可以配置的选项autoMappingBehavior和mapUnderscoreToCamelCase,它们是控制自动映射和驼峰映射的开关。

配置自动映射的autoMappingBehavior选项的取值范围是:

  • NONE,不进行自动映射。
  • PARTIAL,默认值,只对没有嵌套结果集进行自动映射。
  • FULL,对所有的结果集进行自动映射,包括嵌套结果集。

默认情况下,使用默认的PARTIAL级别就可以了。为了实现自动映射,首先给出一个POJO类:

public class SysRole{
    private int id;
    private String roleName;
    private String note;
    /* setter and getter */
}

 如上SysRole类,如果编写的SQL列名和属性名保持一致,那么它就会形成自动映射,比如通过角色编号获取角色信息:

<select id="getRole" parameterType="java.lang.Integer" resultType="com.learn.ssmr.chapter5.pojo.SysRole">
    select id, role_name as roleName ,note
    from sys_role
    where id = #{id}
<select/>

 原来的列名role_name被别名roleName代替了,这样就和POJO上的属性名称保持一致了。此时MyBatis就会将这个结果集映射到POJO的属性roleName上,自动完成映射,而无须再进行任何配置,明显减少了工作量。

如果系统按照驼峰命名法,那么只需在配置项把mapUnderscoreToCamelCase设置为true即可。这样上述SQL语句可以修改成:

<select id="getRole" parameterType="java.lang.Integer" resultType="com.learn.ssmr.chapter5.pojo.SysRole">
    select id, role_name, note
    from sys_role
    where id = #{id}
<select/>

 MyBatis会严格按照驼峰命名的方式做自动映射,只是这样会要求字段和POJO的属性名严格对应,降低了灵活性。

自动映射和驼峰映射都建立在SQL列名和POJO属性名的映射关系上,而现实开发情况更复杂,可能有些字段有主表和从表关联的级联,或者typeHandler的复杂转换规则,此时resultType元素是无法满足这些要求的。如果需要更强大的映射规则,可考虑使用resultMap。

 2.3 传递多个参数

自己写的demo中多是传递一个参数,在实际开发中我们可能会遇到传递多个参数的情况,如订单可以通过编号查询,也可以通过订单名称、日期或者价格等参数进行查询。假定场景:通过角色名role_name和备注note对角色进行模糊查询,这样就有两个参数了。

2.3.1 使用map接口传递参数

  • 在MyBatis中运行map接口通过键值对传递多个参数,接口方法定义如下:
public interface SysRoleMapper{
    public List<SysRole> getRoleByMap(Map<String, Object> paramterMap);
}
  • 此时,传递给映射器的是一个map对象,使用它在SQL中设置对应的参数,如下:
<select id="getRoleByMap" paramterType="map" resultType="sysRole">
    select id, role_name as roleName, note
    from sys_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
<select/>
  •  设置map参数:
SysRoleMapper roleMapper = sqlSession.getMapper(SysRoleMapper.class);
Map<String, Object> paramterMap = new HashMap<String, Object>();
paramterMap.put("roleName", "1");
paramterMap.put("note", "1");
List<SysRole> roles = roleMapper.getRoleMap(paramterMap);
  • 严格来说,map适用几乎所有场景,但是实际情况使用不多。原因:

【1】map是一个键值对应的集合,使用者需要通过阅读它的键,才能明白其作用;

【2】使用map不能限定其传递的数据类型,因此业务性不强,可读性差。 

2.3.2 使用注解传递参数

  • 使用map传递多个参数----可读性差。而MyBatis也提供了一个注解@Param,可以通过它去定义映射器的参数名称,使用它可以得到更好的可读性,接口定义方法如下:
public interface SysRoleMapper{
    public List<SysRole> getRoleByAnnotation(@Param("roleName") String roleName, 
        @Param("note") String note);
}
  • 可读性提高了,我们能够明确知道参数,一目了然。这个时候需要修改一下映射文件:
<select id="getRoleByAnnotation" resultType="sysRole">
    select id, role_name as roleName, note
    from sys_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
<select/>
  • 此时,不需要再给出传入的参数类型,即不用设置patamterType属性,让MyBatis自动搜索即可。通过注解配置提高了可读性,但是如果SQL语句很复杂,传递的参数很多的时候,那么接口方法要传递的参数也就很多了,使用起来很不方便。此时,我们还可以使用Java Bean的方式传递参数。

2.3.3 使用Java Bean传递参数

  • 定义一个POJO类---RoleParams:
public class RoleParams{
    private String roleName;
    private String note;
    /*setter and getter*/
}
  •  此时,接口方法定义如下:
public interface SysRoleMapper{
    public List<SysRole> getRoleByJavaBean(RoleParams roleParam);
}
  • 修改映射文件如下:
<select id="getRoleByJavaBean" paramterType="com.learn.ssm.chapter.param.Roleparams"
      resultType="sysRole">
    select id, role_name as roleName, note
    from sys_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
<select/>
  •  修改JavaBean定义的属性作为参数:
SysRoleMapper roleMapper = sqlSession.getMapper(SysRoleMapper.class);
RoleParams roleParams = new RoleParams();
roleParams.setRoleName("1");
roleParams.setNote("1"); 
List<SysRole> roles = roleMapper.getRoleByJavaBean(roleParams);

2.3.4 使用混合方式传递参数

  • 在某些情况下,可能还需要混合使用几种方法来传递参数,比如查询角色名和备注的同时,还需要支持分页,而分页的POJO实现如下:
public class PageParams{
    private int start;
    private int limit;
    /*setter and getter*/
}
  • 此时,接口设计如下:
public interface SysRoleMapper{
    public List<SysRole> getRoleByMix(@Param("params") RoleParams roleParams,
        @Param("page") PageParams pageParams);
}
  •  关于映射文件,则如下修改:
<select id="getRoleByMix" resultType="sysRole">
    select id, role_name as roleName, note
    from sys_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
    limit #{page.start},#{page.limit}
<select/>
  • 这样就能使用混合参数,MyBatis对params和page这类Java Bean参数提供EL(中间语言)支持。

小结:

  • 使用map传递参数导致了业务可读性的丢失,致使后期维护困难,应废弃这种方式;
  • 使用@Param注解传递多个参数,收到参数个数(n)的影响。当n<= 5时,最佳传递方式,比使用Java Bean更好;当n>= 5时,多个参数将给调用带来困难,不推荐;
  • 当参数多于5个时,建议使用JavaBean方式。

 2.4 使用resultMap映射结果集

在复杂的映射配置时,select元素提供了resultMap属性。先定义resultMap属性:

<mapper namespace="com.learn.ssm.chapter.param.Roleparams">
    <resultMap id="roleMap" type="sysRole">
        <id property="id" column="id"/>
        <result property="roleName" column="role_name"/>
        <result property="note" column="note"/>
    <resultMap/>

    <select id="getRoleMap" paramterType="long" resultMap="roleMap">
        select id, role_name, note 
        from sys_role
        where id=#{id}
    </select>
</mapper>

分析一下上边的SQL配置: 

  • resultMap:定义了一个roleMap,它的属性id代表它的标识,type代表使用哪个类作为其映射类,可以是别名或全限定类名
  • 子元素id:表示resultMap的主键,而result表示其属性,id和result元素的属性property表示POJO类的属性名,而column表示SQL的字段名。把POJO的属性和SQL的列名做对应,就建立起了映射关系
  • select元素中:resultMap指定了采用哪个resultMap作为其映射规则

 2.5 分页参数RowBounds

MyBatis支持分页,内置了一个专门处理分页的类——RowBounds。

RowBounds源码:

poublic class RowBounds{

    private static final int NO_ROW_OFFSET = 0;
    private static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
    private static final RowBounds DEFAULT = new RowBounds();
    
    private int offset;
    private int limit;
    
    public RowBounds(){
        this.offset = NO_ROW_OFFSET;
        this.limit = NO_ROW_LIMIT;
    }

    public RowBounds(int offset, int limit){
        this.offset = offset;
        this.limit = limit;
    }
    /*setter and getter*/
}
  • offset:偏移量,即从第几行开始读取记录
  • limit:限制条数,默认值0~2147483647

此时,我们需要给接口增加一个rowBounds参数

public interface SysRoleMapper{
    public List<SysRole> getRoleByRowBounds(@Param("roleName") String roleName,@Param("note") String note, RowBounds rowBounds);
}

配置一下映射文件:

<select id="getRoleByRowBounds" resultType="sysRole">
    select id, role_name as roleName, note
    from sys_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
<select/>

看一下配置文件,并没有关于RowBounds参数的任何信息,它是一个附加参数,MyBatis会自动识别,然后进行分页。测试一下接口:

SqlSession sqlsession = null;
try{
    sqlSession = SqlSessionFactoryUtils.openSqlSession();
    SysRoleMapper roleMapper = sqlSession.getMapper(SysRoleMapper.class);
    RowBounds rowBounds = new RowBounds(0, 20);
    List<SysRole> roleList = roleMapper.getRoleByRowBounds("roleName", "note", "rowBounds");
} catch(Exception e){
    e.printStackTrace();
} finally{
    if(sqlSession != null){
        sqlSession.close();
    }
}

运行分页就可以限定地查询返回至多20条记录的结果。而这个RowBounds分页只能应用于一些小数据量的查询,原理是执行SQL查询后,按照偏移量和限制条数返回查询结果,所以对于大量的数据查询,性能不佳,可通过分页插件进行处理。 

3、insert元素

执行select的基础就是先插入数据,而插入数据依赖于insert语句。相对于select而言,insert语句简单很多。insert语句可以配置以下属性:

            属性                                                               描述 备注
id SQL编号,标识这条SQL 命名空间+id+databaseId唯一,否则抛异常
parameterType 传入参数类型,同select元素 和select一样,可以是单个或多个参数
flushCache 是否刷新缓存,布尔类型;true时,插入数据会刷新一级和二级缓存;false时,不刷新缓存 默认true
statementType 使用哪个JDBC的Statement工作,取值为STATEMENT、PREPARED、CALLABLE 默认PREPARED
useGeneratedKeys 是否启用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键(如MySQ和SQLServer这样的数据库表的自增主键) 默认false
keyProperty (仅对insert和update有用)唯一标记一个属性,通过generatedKeys的返回值,或通过insert语句的selectKey子元素设置它的键值。如果是复合主键,要把每一个名称用逗号隔开 默认unset,不能和keyColumn一起使用
keyColumen (仅对insert和update有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库中是必须的,当主键列不是表中的第一列时需要设置。如果是复合主键,需要把每一个名称用逗号隔开 不能和keyProperty一起使用

MyBatis在执行一条insert语句后,会返回一个整数表示其影响记录数。 

 3.1 简单的insert语句的应用

插入一条角色记录:

<insert id="insertRole" paramterType="sysRole">
    insert into sys_role(roleName, note)
    values(#{roleName}, #{note})
<insert/>

分析:

  •  id:表示这条SQL,结合命名空间让MyBatis找到它
  • paramterType:表示传入的参数类型
  • 没有配置的属性将采用默认值,这样就完成一个角色记录的插入。

 3.2 主键回填

JDBC的statement对象在执行插入的SQL后,可以通过getGeneratedKeys方法获得数据库生成的主键,这样便能达到获取主键的功能。在insert语句中有个开关属性useGeneratedKeys,用来控制是否打开这个功能,默认值为false。当打开这个开关,还要配置其属性keyPropertykeyColumn,告诉系统把生成的主键放入哪个属性中,如果存在多个主键,就用逗号隔开。

返回主键:

<insert id="insertRole" paramterType="sysRole" useGeneratedKeys="true" keyProperty=id>
    insert into sys_role(roleName, note)
    values(#{roleName}, #{note})
<insert/>
  • useGeneratedKeys:表示采用JDBC的Statement对象的getGeneratedKeys方法返回主键
  • keyProperty:表示使用哪个POJO的属性去匹配这个主键,这里是id,说明它会用数据库生成主键去赋值给这个POJO。 

 3.3 自定义主键

有时候主键可能依赖于某些规则,比如取消角色表的id递增规则,而将其规则修改为:

  • 角色表记录为空时,id设置为1
  • 角色表记录不为空时,id设置为当前id加3

它依赖于selectKey元素进行支持,允许自定义键值得生成规则:

<insert id="insertRole" paramterType="sysRole">
    <selectKey keyProperty="id" resultType="java.lang.Integer" order="VEFORE">
        select if (max(id) = null, 1, max(id) + 3)
        from sys_role
    </selectKey>
        insert into sys_role(id,roleName,note)
        values(#{id},#{roleName},#{note})
</insert>
  • selectey元素的keyProperty指定了采用哪个属性作为POJO的主键。
  • resultType:指定返回结果集类型
  • order = “BEFORE”:说明它相当于当前定义的SQL前执行【在执行插入之前执行生成主键的SQL,然后插入数据。】。 

4、update元素和delete元素

updatedelete的属性差不多,执行完会返回一个整数。所以表示该SQL语句影响了数据库的记录行数

<update id="updateRole" parameterType="sysRole">
    update sys_role set roleName = #{roleName},note = #{note}
    where id = #{id}
</update>
<delete id="deleteRole" parameterType="java.lang.Integer">
    delete from sys_role where id = #{id}
</delete>

5、sql元素

sql元素的作用:定义一条SQL的一部分,便于后面SQL引用。

<mapper namespace="com.learn.ssm.chapter.param.Roleparams">
    <resultMap id="roleMap" type="sysRole">
        <id property="id" column="id"/>
        <result property="roleName" column="role_name"/>
        <result property="note" column="note"/>
    <resultMap/>
    <!-- sql元素 -->
    <sql id="roleCols">
        id, roleName, note
    </sql>
    <select id="getRoleMap" paramterType="long" resultMap="roleMap">
        select <include refid="roleCols"/> 
        from sys_role
        where id=#{id}
    </select>

    <insert id="insertRole" paramterType="sysRole">
    <selectKey keyProperty="id" resultType="java.lang.Integer" order="VEFORE">
        select if (max(id) = null, 1, max(id) + 3)
        from sys_role
    </selectKey>
        insert into sys_role(<include refid="roleCols"/>)
        values(#{id},#{roleName},#{note})
</insert>
</mapper>

通过sql元素进行定义,就可以通过include元素引入到各条SQL中,减少对其列名的重复编写。

sql元素还支持变量递增:

<sql id="roleCols">
    ${alias}.id,${alias}.roleName,${alias}.note,
</sql>
<select id="getRole" parameterType="java.lang.Integer" resultMap="sysRole">
<include refid="roleCols">
    <property name="alias" value="r">
</include>
    from sys_role where id = #{id}
</select>

 在include元素中定义了一个命名为alias的变量,其值是SQL中表sys_role的别名,然后sql元素就能够使用这个变量名了。

 

    原文作者:洒家肉山大魔王
    原文地址: https://blog.csdn.net/qq_27706119/article/details/89889530
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。