MyBatis学习记录03——Mapper(上)

###0.前言

《Java EE互联网轻量级框架整合开发——SSM框架(Spring MVC+Spring+MyBatis)和Redis实现》

Mapper的学习记录

Mapper(映射器)主要由接口文件和xml文件组成(xml文件可以用注解代替),用于配置各类SQL语句并将结果映射为指定的POJO对象。

Mapper的配置元素主要有:

  • select : 查询语句,返回指定结果集
  • insert : 插入语句,返回整数
  • update : 更新语句,返回整数
  • delete : 删除语句,返回整数
  • sql : sql语句片段,用于导入(select、insert、update、delete等语句)
  • resultMap : 结果集与转化对象间的参数配对

###1.0 Mapper的结构说明

//Role类
public class Role{
    private Long id;
    private String roleName;
    private String note;
    //...省略...
}

//RoleMapper.java
public interface RoleMapper {
    public Role getRole(Long id);
}

//RoleMapper.xml
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.chapter5.mapper.RoleMapper">
    //...
</mapper>

RoleMapper包含两部分,即RoleMapper.java和RoleMapper.xml,通过mapper的namespace指定关联。

  • Mapper.xml : 用于配置SQL语句、POJO类与结果集的映射关系以及缓存方式等
  • Mapper.java : 接口文件,描述方法所需参数和返回值类型,被用于SQLSession的调用

###2.0 select元素 ——查询语句

//Role类
public class Role{
    private Long id;
    private String roleName;
    private String note;
    //...省略...
}

//RoleMapper.java
public interface RoleMapper {

    public Role getRole(Long id);
}

//RoleMapper.xml
<select id="getRole" parameterType="long" resultType="com.ssm.chapter5.pojo.Role">
    select id,role_name as roleName, note from t_role where id = #{id}
</select>

上述语句的含义: 一条id(唯一标识)为getRole的select语句,其中有一个long类型的参数id,其数据库查询后返回的结果集为Role类型。

select语句的参数有:

  • id : 唯一标识,与Mapper.java的接口文件的方法名一致(eg:xml中的id为getRole的语句对应接口文件中的getRole(long id)
  • parameterType : 传入到SQL中参数的类型,可以是全限定名,也可以是别名。(JavaBean、Map等)
  • resultType : 结果集类型,可以是全限定名,也可以是别名。(JavaBean、Map、int等)
  • resultMap : 结果集类型,通过引用已经定义的ResultMap节点的映射关系。(不与resultType同时使用)
  • flushCache : 清空之前的本地缓存和二级缓存(true/false),默认值:false
  • useCache : 启动二级缓存,缓存查询结果 (true/false),默认值:true
  • #{param} : 被传递入SQL的参数,参数的名称一般在Mapper.java接口文件中定义。即:
    • getRole(long id)//直接采用id作为传入参数的名词
    • getRole(@Param(“id”)long roleId),通过注解将roleId重命名为id,传入SQL。此方法可省略xml中的parameterType属性

多参数的传入的方式:

  • 普通参数传入 : 少于等于5个参数建议使用,可读性高。
    • eg: List<Role> findRolesByParameter(@Param("roleName")String rolename,@Param("note")String note);
  • Map传入 : 不推荐,不能限定数据类型且可读性差。
    • eg:List<Role> findRolesByMap(Map<String,Object> parameterMap);
  • JavaBean传入 : 当参数多于5个时候建议使用,通过javaBean限定,可以多处复用
    • eg:List<Role> findRolesByBean(@Param("params") RoleParams roleParams);注:xml通过params.roleName引用
  • 多JavaBean传入:单参数涉及多个JavaBean的时,采用多个javaBean封装参数
    • eg:List<Role> findRolesByBean(@Param("params") RoleParams roleParams,@Param("page") PageParams pageParams);

需要注意的:

  • resultType指定的POJO的属性名与select语句的列名要一一对应,不对应的可以通过SQL列名指定别名修改,例如role_name as roleName
  • resultType可以指定为maplist,只是可读性会下降
  • 通过配置mapUnderscoreToCamelCase:true可以把自动映射规则变为A_COLMN(下划线) <-> aColmn(驼峰),缺点是降低了灵活性。
  • resultType与resultMap不能同时使用。

###2.1 insert元素 ——插入语句

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

上述例子主要作用:插入一条role记录到数据库中,并将Statement对象getGeneratedKeys方法返回的主键传递给role对象id属性

insert的元素:

  • id : 唯一标识,供调用使用。命名空间+id+databaseId必须唯一,否则异常
  • parameterType : 传入参数类型
  • useGeneratedKeys:是否启用JDBC的getGeneratedKeys方法,取出数据库内部生成的主键(默认值:false)
  • keyProperty:将getGeneratedKeys回传的主键存放到POJO对象指定的属性中。
    • 主键为联合主键时,可以通过”,”隔开

自定义主键:

<insert id="insertRole2" parameterType="role">
    <selectKey keyProperty="id" resultType="long" order="BEFORE"> <!--自定义主键-->
        select
        if (max(id) = null, 1, max(id) + 3) from t_role
    </selectKey>
    insert into t_role2(id, role_name, note) values(#{id}, #{roleName},#{note}) <!--使用自定义主键-->
</insert>

上述例子主要作用:从role表查出已存在的最大id,并用最大id+3作为新插入记录的id主键。如果数据库没有记录,则设置新插入记录的id为1。

selectKey的元素:

  • keyProperty : 对应POJO的主键属性,eg:Role类中的id属性
    • 主键为联合主键时,可以通过”,”隔开
  • resultType : 结果集类型
  • order:执行顺序
    • BEFORE : sql语句执行前,预先自定义主键可用
    • AFTER : SQL语句执行后,执行完插入语句后获取数据库主键,插入完触发其他事件等。

###2.2 sql元素

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.chapter5.mapper.RoleMapper">
    <sql id="roleCols"> <!--sql元素定义SQL语句片段-->
        id,role_name,note
    </sql>    

<select id="getRoleUseResultMap" parameterType="long" resultMap="roleMap"> 
    select <include refid="roleCols"/> from t_role where id = #{id} <!--使用sql片段-->
</select> 
    //...
</mapper>

上述例子主要作用:预先定义sql语句片段,例如此例子的列名,通过include导入到select语句中。最大的好处是复用 。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.chapter5.mapper.RoleMapper">
    <sql id="roleCols"> <!--sql元素定义带有变量的SQL语句片段-->
        ${alias}.id,${alias}.role_name,${alias}.note
    </sql>    

<select id="getRoleUseResultMap" parameterType="long" resultMap="roleMap"> 
    select 
        <include refid="roleCols">  <!--使用sql片段-->
            <property name="alias" value="r">     <!--配置sql片段的参数: alias = r -->
        </include>    
     from t_role r where id = #{id} <!--使用sql片段-->
</select> 
    //...
</mapper>

上述例子主要作用:预先定义带参数的sql语句片段,通过include导入到select语句中,并对参数进行赋值。多表查询可以使用。

###2.3 参数

  1. 控制精度

    • 参数传入可以通过numericScale控制数值进度,eg:{width,javaType=double,jdbcType=NUMERIC,numericScale=2}
  2. #$

    • #{}:预编译处理,处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值.以有效的防止SQL注入,提高系统安全性。
    • ${}:字符串替换,处理${}时,就是把${}替换成变量的值.

使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入。在使用参数化查询的情况下,数据库系统不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行。因为对于参数化查询来说,查询SQL语句的格式是已经规定好了的,需要查的数据也设置好了,缺的只是具体的那几个数据而已。所以用户能提供的只是数据,而且只能按需提供,无法更进一步做出影响数据库的其他举动来。

###2.4 resultMap 结果集映射

<!--resultMap结构-->
<resultMap>
    <constructor>          <!--构造器:配置可能不存在无参构造器的POJO类-->
        <idArg />
        <arg />
    </constructor>

    <id />                <!--主键:联合主键下可以存在多个<id />节点-->
    <result />            <!--属性-->
    <association />        <!--一对一关系-->
    <collection />        <!--一对多关系-->
    <discriminator>        <!--鉴别器-->
        <case />
    </discriminator>
</resultMap>

不存在无参构造器的POJO类,可以使用resultMap的构造器配置

<resultMap type="role">
    <constructor>          
        <idArg column="id" javaType="int"/>
        <arg column="role_name" javaType="string"/>
    </constructor>
    //...
</resultMap>

上述配置对应着Role的构造器:public Role(int id,String roleName){...};

常用的映射配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.chapter5.mapper.RoleMapper">
    <resultMap id="roleMap" type="role"> <!--resultMap定义-->
        <id property="id" column="id" />  <!--resultMap子元素-->
        <result property="roleName" column="role_name" />
        <result property="note" column="note" />
    </resultMap>

<select id="getRoleUseResultMap" parameterType="long" resultMap="roleMap"> <!--此处通过id引用-->
    select id, role_name, note from t_role where id = #{id}
</select>
    //...
</mapper>

如上述例子,resultMap的元素:

  • resultMap中的id属性 : 唯一标识,用于下面select语句中resultMap的赋值
  • resultMap中的type属性: 指定resultMap映射的POJO类,可以是全限定名或者别名
  • resultMap的子元素:
    • id : resultMap的主键,含有property和column属性,property对应POJO的属性名,column对应SQL列名
    • result:resultMap的属性,property对应POJO的属性名,column对应SQL列名

###3.存储过程
存储过程是数据库预先编译好的一段程序片段,存放在数据库内存中。它有3中类型的参数:

  • 输入参数(IN):外界传入存储过程的参数
  • 输出参数(OUT):存储过程返回给程序的结果参数
  • 输入输出参数(INOUT):一开始作为参数传入存储过程,而存储过程修改后将其返回的参数(例如:库存的修改)

delimiter $$ //将语句的结束符号从分号;临时改为两个$$
CREATE PROCEDURE count_role(
IN p_role_name VARCHAR,
OUT count_total INTEGER
)
BEGIN
select count(*) into count_total from t_role where role_name like concat(‘%’, p_role_name, ‘%’);
END$$

delimiter;//将语句的结束符号恢复为分号

  1. 存储过程

存储过程对应的Mode:

  • IN:输入参数 eg:#{id,mode=IN}
  • OUT:输出参数
  • INOUT:输入输出参数

    <select id="findRole" parameterType="com.ssm.chapter5.param.PdFindRoleParams"
        statementType="CALLABLE">  <!--statementType指定为调用存储过程-->
        {call find_role(
        #{roleName, mode=IN, jdbcType=VARCHAR},
        #{countTotal, mode=OUT,jdbcType=INTEGER}
        )}
    </select>
    

调用存储过程需要修改statementType="CALLABLE",说明它是存储过程,参数的传入需要指定参数的modejdbcType

###4. 总结
Mapper的配置元素主要由3部分组成:

  • SQL语句:select、insert、update、delete和sql片段
  • 结果集映射配置:resultMap配置数据结果集与POJO类的映射关系
  • 缓存配置(后面单独记录):<cache />配置二级缓存

END

–Nowy

–2018.12.05

分享到