目录
一. 配置元素
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不能限定其传递的数据类型,业务性质不强,可读性差 -
使用注解传递多个参数
RoleMapperList<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传递多个参数
RoleMapperList<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中