天天看点

Mapper.xml映射器续(学习笔记)

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开发

继续阅读