###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