7、resultMap配置
7.1、元素组成
<resultMap>
<constructor>
<idArg/>
<arg/>
</constructor>
<id/> //**
<result/> //**
<association/>
<collection/>
<discriminator>
<case/>
</discriminator>
</resultMap>
7.1.1、constructor元素用于配置构造方法。POJO类可能不存在没有参数的构造方法
假设构造方法为:
public Role(int id,String roleName);
constructor元素配置为:
<constructor >
<idArg column="id" javaType="int"/>
<arg column="r_name" javaType="string"/>
</constructor>
这样Mybatis就会使用对应构造方法来构造
7.1.2、id元素表示那个列是主键,允许多个主键(联合主键)
7.1.3、result配置POJO到SQL的映射关系,result和idArg元素属性如下
property 对应POJO的字段
column 对应SQL的列
javaType 类全名或别名
jdbcType
typeHandler 类型转换器
7.1.4、其他的在7.3节详述
7.2、存储结果集
7.2.1、map存储,resultType="map"
可读性下降,需要进一步了解map键值的构成和数据类型
7.2.2、POJO存储
之前也示例过,就不赘述了
7.3、级联(示例是书上的,不想搬代码了..把重点描述一下.....)
7.3.1、association元素,一对一级联,property属性代表映射到POJO属性上,select配置为命名空间+SQL id ,指向对应Mapper的SQL,这样就会将对应数据查询回来,column代表SQL的列,用作参数传递给select属性指定的SQL,多个参数需要用逗号隔开。
7.3.2、collection元素,一对多级联,属性基本同上
7.3.3、discriminator元素,鉴别器,属性column代表使用哪个字段进行鉴别,子元素case,用于区分,类似switch..case语句,resultMap属性表示采用哪个resultMap去映射。
示例:雇员employee表中一对一关联工牌workcard表,工牌表中存有外键emp_id对应雇员编号
又 一对多关联雇员任务employeeTask表,雇员任务表存有外键emp_id对应雇员编号,一个雇员有很多任务(雇员任务表中也有一对一关联了任务Task表)
又 体检表分男女(前列腺|子宫。。。等区别),又分男雇员和女雇员,提取相同点为Employee.java. 将不同属性即体检表放入子类。男雇员类MaleEmployee.java有一个男体检表类型(MaleHealthForm.java)的属性。女的类似。
在查询雇员信息时,将以上锁关联的信息查询出来,resultMap如下配置
<resultMap type="com.ssm.pojo.Employee" id="employee">
<id column="id" property="id"/>
<result column="real_name" property="realName"/>
<result column="sex" property="sex" typeHandler="com.ssm.handler.SexTypeHandler"/>
<result......../>
<association property="workCard" column="id"
select="com.ssm.mapper.WorkCardMapper.getWorkCardByEmpId"/>
<collection property="employeeTaskList" column="id"
select="com.ssm.mapper.EmployeeTaskMapper.getEmployeeTashByEmpId"/>
<discriminator javaType="long" column="sex">
<case value="1" resultMap="maleHealthFormMapper"/>
<case value="2" resultMap="femaleHealthFormMapper"/>
</discriminator>
</resultMap>
<resultMap type="com.ssm.pojo.FemaleEmployee"
id="femaleHealthFormMapper" extends="employee">
<association property="femaleHealthForm" column="id"
select="com.ssm.mapper.FemaleHealthFormMapper.getFemaleHealthForm"/>
</resultMap>
<resultMap type="com.ssm.pojo.MaleEmployee"
id="maleHealthFormMapper" extends="employee">
<association property="maleHealthForm" column="id"
select="com.ssm.mapper.MaleHealthFormMapper.getMaleHealthForm"/>
</resultMap>
7.3.4、延迟加载
之前介绍的Mybatis配置项setting中有两个元素配置级联lazyLoadingEnabled和aggressiveLazyLoading
lazyLoadingEnabled 延迟加载开关 默认关闭
aggressiveLazyLoading 启动时,对任意属性的调用会使带有延迟加载属性的对象完整加载 默认3.4.1(包含)前true,后false
前一个很好理解,后一个较为特殊
(例子我暂时没有测试,过几天吧)
-----将两个都设置为true,调用上述的获取雇员方法,发现雇员基本信息和体检表被查了出来,再次访问雇员对象的工牌属性,发现雇员任务和工牌属性都被查询出来。但是雇员任务表中的一对一关联属性Task任务没有查询出来。
可以得出,aggressiveLazyLoading配置项是一个层级开关,设置为true,当访问级联属性时,鉴别器和实体是同一个层级,所以首先被加载,雇员任务和工卡是同一层级,故而一起被加载。
-----设置为true和false(按照上述前后顺序),同样查询雇员,发现只有雇员基本信息被查询出来
-----有时候会有特殊需求,在加载雇员过程中同时只加载它的级联属性工卡
可以使用fetchType属性,他可以处理上述全局配置无法解决的问题,只出现在association和collection元素中
值有两个:eager(获得当前POJO对象后立即加载对应属性) 和 lazy(延迟加载)
配置fetchType属性会忽略上述两个全局配置
7.3.5、另一种级联
查询雇员的SQL使用left join 将所有信息在一条SQL中查询出来,对应resultMap中也将对应关系一一映射。
这样可以消除N+1问题(???)。会引发其他问题,SQL复杂,配置复杂,维护会有一定困难,这样做法值得商榷。
7.3.6、多对多级联
拆分为两个一对多关系,例如角色表和用户表的多对多,还有一个用户角色表
示例RoleMapper.xml(大概):
<resultMap type="com.ssm.pojo.Role" id="roleMapper">
..............
<collection property ="userList" column="id" fetchType="lazy"
select="com.ssm.mapper.UserMapper.findUserByRoleId"/>
</resultMap>
<select id ="findRoleByUserId" parameterType="long" resultMap="roleMapper">
select r.id,r.role_name,r.note from role r,user_role ur
where r.id=ur.role_id and ur.user_id =#{userId}
</select>
UserMapper.xml也差不多吧,就不写了吧
8、缓存
8.1、一级缓存和二级缓存
一级缓存在SqlSession上的缓存,二级缓存是在SqlSessionFactory上的缓存,默认情况下,Mybatis会开启一级缓存,一级缓存不需要对象可序列化(实现Serializable),二级缓存需要
示例:在同一个SqlSession下,两次相同的查询只会执行一次SQL,因为第一次获取对象后会缓存起来,若缓存没有超时或者没有声明需要刷新时,就会从缓存获取数据,对不同的SqlSession不共享
RoleMapper roleMapper = SqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(1);
logger.info("再获取一次");
Role role1 = roleMapper.getRole(1);
为了使不同SqlSession之间共享缓存,需要开启二级缓存,在RoleMapper.xml中加入如下代码,需要对POJO对象实现序列化
<cache/>
8.2、缓存配置项、自定义、引用
加入上述cache元素后,Mybatis会将对应命名空间的所有select元素查询结果进行缓存,而其中的insert,delete,update在操作时会刷新缓存
8.2.1、缓存cache配置项
----blocking 是否使用阻塞性缓存,在读写时会加入JNI的锁进行操作 默认false,可保证读写安全,但性能不佳
----readOnly 缓存内容是否只读 ,默认false ,若为只读,则不会造成多线程读写的不一致
----eviction 缓存策略 分为:
1》LRU 最近最少使用:移除最长时间不被使用的对象
2》FIFO 先进先出:按对象进入缓存的顺序移除
3》SOFT 软引用:移除基于垃圾回收器状态和软引用规则的对象
4》WEAK弱引用:更积极移除基于垃圾收集器状态和弱引用规则的对象
----flushInterval 刷新时间,正数,单位毫秒,超过时间缓存失效,默认null,即没有刷新时间,旨在执行增删改语句时才刷新
----type 自定义缓存类,需要实现接口org.apache.ibatis.cache.Cache
----sizze 缓存对象个数 正整数,默认1024
8.2.2、自定义
假设存在一个缓存实现类com.ssm.cache.RedisCache
配置如下:
<cache type="com.ssm.cache.RedisCache">
<property name="host" value="localhost"/>
</cache>
这样配置后,Mybatis会启用缓存,同时调用setHost(String host)方法,去设置配置内容。
对于一些查询语句并不想要它进行缓存,可以配置:
<select ..... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>
以上是默认配置,可以根据需要修改。flushCache代表是否刷新缓存,useCache属性是select特有的,代表是否使用缓存。
8.2.3、引用
上述都是在一个映射器RoleMapper.xml中使用,如果需要在其他映射器使用相同的配置,则可以引用缓存的配置
<cache-ref namespace="com.ssm.mapper.RoleMapper"/>
这样就可以引用对于映射器的cache元素的配置了
Mapper中还支持存储过程和动态SQL,另起一篇
--参考书目
JavaEE互联网轻量级框架整合开发--SSM框架(Spring MVC +Spring+Mybatis)和Redis开发