###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); 
 - eg: 
 - Map传入 : 不推荐,不能限定数据类型且可读性差。
- eg:
List<Role> findRolesByMap(Map<String,Object> parameterMap); 
 - eg:
 - JavaBean传入 : 当参数多于5个时候建议使用,通过javaBean限定,可以多处复用
- eg:
List<Role> findRolesByBean(@Param("params") RoleParams roleParams);注:xml通过params.roleName引用 
 - eg:
 - 多JavaBean传入:单参数涉及多个JavaBean的时,采用多个javaBean封装参数
- eg:
List<Role> findRolesByBean(@Param("params") RoleParams roleParams,@Param("page") PageParams pageParams); 
 - eg:
 
需要注意的:
- resultType指定的POJO的属性名与select语句的列名要一一对应,不对应的可以通过SQL列名指定别名修改,例如
role_name as roleName - resultType可以指定为map和list,只是可读性会下降
 - 通过配置
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 参数
控制精度
- 参数传入可以通过numericScale控制数值进度,eg:{width,javaType=double,jdbcType=NUMERIC,numericScale=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;//将语句的结束符号恢复为分号
- 存储过程
 
存储过程对应的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",说明它是存储过程,参数的传入需要指定参数的mode和jdbcType。
###4. 总结
Mapper的配置元素主要由3部分组成:
- SQL语句:select、insert、update、delete和sql片段
 - 结果集映射配置:resultMap配置数据结果集与POJO类的映射关系
 - 缓存配置(后面单独记录):
<cache />配置二级缓存 
END
–Nowy  
–2018.12.05