一、resultMap

1、resultMap和resultType

标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射关系

resultMap和resultType的关系与区别

resultType: 自动映射,用于属性名和表中字段名一致的情况
resultMap: 自定义映射,用于一对多或多对一或字段名和属性名不一致的情况。

resultType设置的是一个已经存在的、具体的类型
resultMap设置的是某个resultMap标签的id属性


2、字段名和属性名不一致

字段名和实体类中的属性名不一致

如下:emp_id 和 empId       emp_name  和 empName

 这时候,我们是这么使用mybatis的,mapper接口中:

    /**
     * 通过员工id获取员工信息
     * @param empId
     * @return
     */
    Emp getEmpByEmpId(@Param("empId") String empId);

mapper映射文件中:

    <select id="getEmpByEmpId" resultType="Emp">
        select * from t_emp where emp_id = #{empId}
    </select>

测试类里:

        Emp emp = empMapper.getEmpByEmpId("1");
        System.out.println(emp);

测试结果:查询回来以后emp_id是无法封装到EMP的empId中


3、方式:起别名

方法1:起别名,在mapper映射文件中

    <select id="getEmpByEmpId" resultType="Emp">
        select emp_id empId,emp_name empName, age, gender from t_emp where emp_id = #{empId}
    </select>

4、方式:配置下划线转驼峰

方法2:全局配置,让下划线自动映射为驼峰,在mybatis-config.xml中,

放在起别名标签<typeAliases>的上方,注意标签的顺序。

    <settings>
        <!--将表中字段的下划线映射为驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

5、方式:自定义映射

方法3:自定义映射 resultMap

标签select中resultMap和resultType不能同时存在也不能同时没有  ,注意不管字段和属性名一样不一样都要设置。如age、gender

    <resultMap id="map1" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
    </resultMap>
    <select id="getEmpByEmpId" resultMap="map1">
        select * from t_emp where emp_id = #{empId}
    </select>

6、resultMap标签

resultMap标签有俩个属性
    id:自定义映射的唯一标识,不能重复
    type:查询的数据要映射的实体类的类型

resultMap标签的子标签:
    id子标签:设置主键的映射关系
    result子标签:设置普通字段的映射关系
    association子标签:设置多对一、一对一的映射关系(处理实体类类型的数据)
    collection子标签:设置一对多、多对多的映射关系(处理集合类型的数据)

resultMap标签的子标签属性:
    column:设置映射关系中表中的字段名,或者说是sql查出来的字段名更准确
    property:设置映射关系中实体类中的属性名


二、resultMap对一关系

1、对一关系

"对一"对应实体类类型,实际上处理实体类类型的属性。

如:查询员工信息以及员工所对应的部门信息

public class Emp {
    private Integer empId;

    private String empName;

    private Integer age;

    private String gender;

    private Dept dept;
}

2、方式:级联(用的不多)

    <resultMap id="map1" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <result column="dept_id" property="dept.deptId"></result>
        <result column="dept_name" property="dept.deptName"></result>
    </resultMap>

<!--    Emp getEmpByEmpId(@Param("empId") String empId);-->
    <select id="getEmpByEmpId" resultMap="map1">
        select * from t_emp left join t_dept on t_emp.dept_id=t_dept.dept_id where t_emp.emp_id=#{empId}
    </select>

结果如下:


3、方式:association

association中的俩个属性:

  1.     property:设置映射关系中实体类中的属性名
  2.     javaType="Dept" 设置该属性的类型
    <resultMap id="map1" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>

        <association property="dept" javaType="Dept">
            <id column="dept_id" property="deptId"></id>
            <result column="dept_name" property="deptName"></result>
        </association>
    </resultMap>

<!--    Emp getEmpByEmpId(@Param("empId") String empId);-->
    <select id="getEmpByEmpId" resultMap="map1">
        select * from t_emp left join t_dept on t_emp.dept_id=t_dept.dept_id where t_emp.emp_id=#{empId}
    </select>


4、方式:分步查询

需要2个sql语句。他有自己优点,可以延迟加载。

先查出来员工的信息

property:设置映射关系中实体类中的属性名
select:分步查询,对应sql的唯一标识(namespace.SQLId) 
column:写的是分步查询的条件,将当前sql语句查询的某个字段作为条件
fetchType="lazy"/eager  分步查询延迟加载/立即加载

    <!--
        association实现分步查询:
    -->
    <resultMap id="empAndDeptThree" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <association property="dept"
                     select="com.atguigu.mybatis.mapper.DeptMapper.getEmpByStepTwo"
                     column="dept_id" fetchType="eager">
        </association>
    </resultMap>

    <!--Emp getEmpByStepOne(@Param("empId") String empId);-->
    <select id="getEmpByStepOne" resultMap="empAndDeptThree">
        select * from t_emp where emp_id = #{empId}
    </select>

DeptMapper.getEmpByStepTwo如下:

    <!--Dept getEmpByStepTwo(@Param("deptId") String deptId);-->
    <select id="getEmpByStepTwo" resultMap="deptResultMap">
        select * from t_dept where dept_id = #{deptId}
    </select>

小技巧:select中的值是sql的唯一标识,可以如下,在mapper接口的方法中右键,选择

Copy Reference

这里是立即加载模式, fetchType="eager",测试代码如下:

    public void testGetEmpByEmpId(){
        Emp emp = empMapper.getEmpByStepOne("1");
    }

立即加载模式 :结果是2条sql都直接加载了,注意我的测试方法里就只有一行代码


5、开启延迟加载

延迟加载:延迟加载是指在查询主对象时,仅加载其基本属性,而将关联对象(如一对一、一对多关系)的查询推迟到实际访问该属性时执行。

在mybatis-config.xml中(配置全局的延迟加载)

lazyLoadingEnabled,默认false
aggressiveLazyLoading,默认false

    <settings>
        <!--将表中字段的下划线映射为驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--设置按需加载(false表示仅访问关联属性时加载)-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

改为fetchType="lazy",测试代码如下:

@Test
public void testGetEmpByEmpId(){
    Emp emp = empMapper.getEmpByStepOne("1");
}

延迟加载是第二句sql延迟加载了,先不加载第二句sql,用到的时候再加载,只加载第一句sql

    public void testGetEmpByEmpId(){
        Emp emp = empMapper.getEmpByStepOne("1");
        System.out.println(emp.getEmpName());//不会触发第二句sql的加载
        System.out.println(emp.getDept());//会触发第二句sql的加载
    }


6、延迟加载原理

MyBatis 使用 CGLIB 动态生成主对象的代理类。当调用未加载的关联属性(如 emp.getDept())时,代理对象会拦截该操作,触发关联查询并填充数据。


三、resultMap对多关系

1、对多关系

"对多"对应集合类型,一对多就是来处理集合类型的属性

根据部门id查询部门以及部门中的员工信息,

public class Dept {
    private Integer deptId;

    private String deptName;

    private List<Emp> emps;
}

2、方式:collection标签

主要在collection中标签内不需要再写<association>获取部门信息,不需要套娃!

 collection:处理一对多的映射关系(处理集合类型的属性)
            属性:
            property:设置需要处理的映射关系中的属性的属性名
            ofType:表示要处理的集合类型的属性中存储的数据的类型

    <resultMap id="deptAndEmpOne" type="Dept">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
        <collection property="emps" ofType="Emp">
            <id column="emp_id" property="empId"></id>
            <result column="emp_name" property="empName"></result>
            <result column="age" property="age"></result>
            <result column="gender" property="gender"></result>
        </collection>
    </resultMap>

    <!--Dept getDeptById(@Param("deptId") String deptId);-->
    <select id="getDeptById" resultMap="deptAndEmpOne">
        SELECT * FROM t_dept LEFT JOIN t_emp ON t_dept.dept_id = t_emp.dept_id WHERE t_dept.dept_id = #{deptId}
    </select>


3、方式:分步查询

 分步查询部门和部门中的员工

public class Dept {
    private Integer deptId;

    private String deptName;

    private List<Emp> emps;
}

 DeptMapper接口中:

   Dept getDeptByStepOne(@Param("deptId") String deptId);

collection实现分步查询

    <resultMap id="deptAndEmpTwo" type="Dept">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
        <collection property="emps"
                    select="com.atguigu.mybatis.mapper.EmpMapper.getDeptByStepTwo"
                    column="dept_id"
                    fetchType="lazy"></collection>
    </resultMap>

    <!--Dept getDeptByStepOne(@Param("deptId") String deptId);-->
    <select id="getDeptByStepOne" resultMap="deptAndEmpTwo">
        select * from t_dept where dept_id = #{deptId}
    </select>

<collection>标签的属性解释:
        property:需要处理的映射关系中的属性的属性名
        select:分步查询的sql的唯一标识(namespace.SQLId)
        column:将当前sql语句查询的某个字段作为分步查询的条件
        fetchType:设置当前的分步查询为 eager(立即加载)|lazy(延迟加载)

----------------------

EmpMapper接口中

getDeptByStepTwo()方法对应的mapper映射文件如下:

    <!--List<Emp> getDeptByStepTwo(@Param("deptId") String deptId);-->
    <select id="getDeptByStepTwo" resultType="Emp">
        select * from t_emp where dept_id = #{deptId}
    </select>

运行结果如下:也是有延迟加载效果的


四、动态sql 多条件查询

mapper接口中定义一个多条件查询, 条件越多,查出来的越精确。用and拼接

//多条件查询
List <Emp> getEmpByCondition(Emp emp);

在mapper映射文件中怎么写嘞

方式一:<if test="">

<if test="表达式">,表达式为true则拼接到sql中

这时候就必须要加恒成立条件1=1了。。因为要是所有条件都不成立,那么就会多出来where关键字。

<!-- List <Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="emp">
    select * from t_emp where 1=1
    <if test="ename !=null and ename!='' ">
    and ename=#{ename}
    <if>
    <if test="age!=null and age!='' ">
    and age=#{age}
    <if>
    <if test="sex!=null and sex!='' ">
    and sex=#{sex}
    <if>
</select>

方式二:<where>标签

  1. 当where标签有内容时,会自动生成where关键字,并且将内容最前方的多余的and或者or去掉
  2. 当where标签没有内容时,此时where标签没有任何效果,就是不会生成where关键字
<!-- List <Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="emp">
    select * from t_emp 
    <where>
        <if test="ename !=null and ename!='' ">
        ename=#{ename}
        <if>
        <if test="age!=null and age!='' ">
        and age=#{age}
        <if>
        <if test="sex!=null and sex!='' ">
        and sex=#{sex}
        <if>
    </where>
</select>

<where>注意事项:

要是把and放后面,<where>会自动帮我们去掉and或者or吗,如下....就会报错。因为<where>不能将其中内容后面多余的and或者or去掉。

<!-- List <Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="emp">
    select * from t_emp 
    <where>
        <if test="ename !=null and ename!='' ">
        ename=#{ename} and
        <if>
        <if test="age!=null and age!='' ">
        age=#{age} and
        <if>
        <if test="sex!=null and sex!='' ">
        sex=#{sex} 
        <if>
    </where>
</select>

方式三:<trim>

1、若<trim>中有内容时:

       prefix | suffix: 向trim标签中内容前面或者内容后面添加指定内容

       prefixOverrides|suffixOverrides :在trim标签中内容的前面或者后面去掉指定内容

2、若<trim>标签中没有内容时,trim标签没有任何效果(同where标签)

其实,where标签底层就是用trim

<!-- List <Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="emp">
    select * from t_emp 
    <trim prefix="where" suffixOverrides="and|or">
        <if test="ename !=null and ename!='' ">
        ename=#{ename} and
        <if>
        <if test="age!=null and age!='' ">
        age=#{age} and
        <if>
        <if test="sex!=null and sex!='' ">
        sex=#{sex} 
        <if>
    </trim>
</select>

五、动态sql  批量删除和添加

<foreach>标签

标签属性

  • collection:设置需要循环的集合或数组,配合@param()使用
  • item:表示集合或者数组中的每一个元素
  • separator:循环之间的分隔符
  • open:所有循环内容的开始符
  • close:所有循环内容的结尾符

批量删除(in方式)

delete from t_emp where eid in ( ? , ? , ? )

<!--int deleteMoreByArray(@Param("eids) Integer[] eids)-->
 <delete id="deleteMoreByArray">
     delete from t_emp where eid in(
        <foreach collection="eids" item="eid" separartor=",">
            #{eid}
        </foreach>
        )
 </delete>

也可以这么写

<!--int deleteMoreByArray(@Param("eids) Integer[] eids)-->
 <delete id="deleteMoreByArray">
     delete from t_emp where eid in
                       <!--open和close表示当前循环由什么内容开始和结束 -->
        <foreach collection="eids" item="eid" separartor="," open="(" close=")">
            #{eid}
        </foreach>
 </delete>

打印出来sql如下,实现批量删除

delete from t_emp where eid in ( ? , ? , ? )

批量删除(or方式)

delete from t_emp where eid=?or eid = ?or eid = ?

参数是数组,则用array。参数是List,则用list,当然最好用@param(),省的麻烦

<!--int deleteMoreByArray(@Param("eids) Integer[] eids)-->
 <delete id="deleteMoreByArray">
     delete from t_emp where
        <foreach collection="eids" item="eid" separartor="or">
           eid= #{eid}
        </foreach>
 </delete>

打印出的sql,也可以批量删除

 delete from t_emp where eid=?or eid = ?or eid = ?

批量添加

先复习一下添加的sql怎么写

--单个插入sql
insert into student (id,name,age,gender) 
values (1,'bob',16,'male'); --每个字段与其值是严格一一对应的
--批量插入sql
insert into student (id,name,age,gender)
values (2,'lucy',17,'female'),(3,'jack',19,'male'),(4,'tom',18,'male');

批量添加 (用的很多很多)

<!-- int insertMoreByList(@Param("emps") List<Emp> emps)-->
<insert>
    insert into t_emp values
    <foreach collection="emps" item="emp" separator=",">
         (null,#{emp.empName},#{emp.age},#{emp.sex})
    </foreach>
</insert>

六、动态sql   sql片段

sql片段,可以记录一段公共sql片段,在使用的地方通过include标签引用。

<sql id="empColumns">
	eid,ename,age,sex,did
</sql>
select <include refid="empColumns"></include> from t_emp

延迟加载

1、mybatis是否支持延迟加载

支持,需要手动开启。比如用户表和订单表,查询用户信息的是否把用户的订单列表也查询出来。

1、局部 在mapper映射文件中 增加fetchType='lazy'。

mybatis支持一对一关联对象和一对多关联集合对象的延迟加载,例如在 <association> 或 <collection> 中指定 fetchType

2、全部 在setting lazyLoadingEnabled 设置 为true,就会开启延迟加载

<settings>
    <!-- 启用延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>

只获取用户信息,则只查询用户,只有获取订单信息的时候,才会执行第二条sql


2、延迟加载原理

延迟加载的主要原理是在当开启了延迟加载功能时,当查询主对象时,MyBatis会生成一个代理对象,并将代理对象返回给调用者。

当后面需要访问这些关联对象时,代理对象会检查关联对象是否已加载。如果未加载,则触发额外的查询。

查询结果返回后,MyBatis会将关联对象的数据填充到代理对象中,使代理对象持有关联对象的引用。这样,下次访问关联对象时,就可以直接从代理对象中获取数据,而无需再次查询数据库。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐