Mybatis之三:Mybatis关系映射、ResultMap(超级重点)

1.养成习惯,最好是
  1. CRUD 标签声明返回值类型 ResultType,声明参数类型 ParamType,省的之后报错
  2. 实体类如果存在有参构造,最好补上无参构造
  3. 在配置 ResultMap 时,可以自动一一映射的不用配置
  4. 在配置 ResultMap 时,除了配置对应的列名和属性,最好同时声明 javaType 和 jdbcType,省的出错
2.研究一下 ResultMap 结果映射的细节
3.研究一下 TypeHandler 类型转换器的细节

一、TypeHandler 数据类型转换器


  1. 定义类型转换器 TypeHandler
  2. 配置自定义的 TypeHandler

1.定义类型转换器 TypeHandler
  1. 可以双向转换
    • 在数据插入的时候,将 List 集合转为字符串
    • 在数据读取的时候,将字符串自动转为 List
  2. implements TypeHandler 实现接口
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
public class ListStringTypeHandler implements TypeHandler<List<String>> {
    /**
     * 插入数据时,手动赋值
     *
     * @param ps
     * @param i
     * @param favorites 这就是 user 对象的 favorites 属性的值
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setParameter(PreparedStatement ps, int i, List<String> favorites, JdbcType jdbcType) throws SQLException {
        //将 List 集合转为字符串
        String s = favorites.stream().map(f -> f + ",").collect(Collectors.joining());
        //设置转换后的参数
        ps.setString(i, s);
    }

    /**
     * 查询的时候调用
     *
     * @param rs
     * @param columnName
     * @return 手动解析查询结果,返回 List 集合
     * @throws SQLException
     */
    @Override
    public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
        String s = rs.getString(columnName);
        if (s != null && !"".equals(s)) {
            //查询到的 足球,篮球 按照 , 拆分成数组,再将数组转为集合
            List<String> list = Arrays.asList(s.split(","));
            return list;
        }
        return null;
    }

    @Override
    public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
        String s = rs.getString(columnIndex);
        if (s != null && !"".equals(s)) {
            //查询到的 足球,篮球 按照 , 拆分成数组,再将数组转为集合
            List<String> list = Arrays.asList(s.split(","));
            return list;
        }
        return null;
    }

    /**
     * 如果调用的是存储过程的话,会触发该方法
     *
     * @param cs
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    @Override
    public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
        String s = cs.getString(columnIndex);
        if (s != null && !"".equals(s)) {
            //查询到的 足球,篮球 按照 , 拆分成数组,再将数组转为集合
            List<String> list = Arrays.asList(s.split(","));
            return list;
        }
        return null;
    }
}
2.配置自定义的 TypeHandler
  1. 对 Mapper.xml 中的值指定 TypeHandler
  2. 在 mybatis-config.xml 全局配置文件中配置,应用全局
  3. 在 mybatis-config.xml 全局配置文件中配置,在 TypeHandler 上加两个注解指定两边的数据类型映射
    • @MappedJdbcTypes(JdbcType.VARCHAR)
    • @MappedTypes(List.class)

2.1. 对 Mapper.xml 中的值指定 TypeHandler

typeHandler = com.ze.demo.typehandler.ListStringTypeHandler

    <insert id="addUser" parameterType="com.ze.demo.model.User">
        insert into user (username,address,favorites) values (#{username},#{address},#{favorites,typeHandler=com.ze.demo.typehandler.ListStringTypeHandler});
    </insert>
2.2在 mybatis-config.xml 全局配置文件中配置,应用全局
    <typeHandlers>
        <typeHandler handler="com.ze.demo.typehandler.ListStringTypeHandler" javaType="java.util.List" jdbcType="VARCHAR"/>
    </typeHandlers>
3. 在 mybatis-config.xml 全局配置文件中配置,在 TypeHandler 上加两个注解指定两边的数据类型映射
    <typeHandlers>
        <!--
        全局配置的时候,可以不加后面两个属性,通过注解来配置后面两个
        -->
        <typeHandler handler="com.ze.demo.typehandler.ListStringTypeHandler"/>
    </typeHandlers>
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
public class ListStringTypeHandler implements TypeHandler<List<String>> {
    ......
}

二、++Mybatis 的核心重点 ORM 对象关系映射++重点

  1. Mybatis 自动 ORM 映射
  2. 通过起别名来达到自动映射
  3. 通过 ResultMap 指定映射关系,然后再引用

1. Mybatis 自动 ORM 映射

MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系,二者不同时,无法自动ORM。

自动ORM失效
7GgAu6.png
2. 方案一:通过起别名来达到自动映射

在SQL中使用 as 为查询字段添加列别名,以匹配属性名。

<mapper namespace="com.qf.mybatis.part2.orm.ManagerDao">
    <select id="selectManagerByIdAndPwd" resultType="com.qf.mybatis.part2.orm.Manager">
        SELECT mgr_id AS id , mgr_name AS username , mgr_pwd AS password
        FROM t_managers
        WHERE mgr_id = #{id} AND mgr_pwd = #{pwd}
    </select>
</mapper>
3 方案二:结果映射(ResultMap - 查询结果的封装规则)

通过< resultMap id="" type="" >映射,匹配列名与属性名。

当列名和属性名相同时,会自动匹配,不用写映射关系

<mapper namespace="com.qf.mybatis.part2.orm.ManagerDao">
    <!--定义resultMap标签-->
    <resultMap id="managerResultMap" type="com.qf.mybatis.part2.orm.Manager">
      	<!--关联主键与列名-->
        <id property="id" column="mgr_id" />
      
      	<!--关联属性与列名-->
        <result property="username" column="mgr_name" />
        <result property="password" column="mgr_pwd" />
    </resultMap>
  
     <!--使用resultMap作为ORM映射依据-->
    <select id="selectAllManagers" resultMap="managerResultMap">
        SELECT mgr_id , mgr_name , mgr_pwd
        FROM t_managers
    </select>
</mapper>

三、++详细说说 ResultMap 结果映射++超级重点

  1. 一次定义,可以多个地方引用
  2. 可以有继承关系,extends = “XXXResultMap”,然后再写具体的映射

1. 定义 ResultMap 结果映射

重点:

  1. id 标签是指定主键, result 标签是指定其他单个的属性,association 和 collection 标签分别指定一个对象、一个集合
  2. 列名和属性名如果相同,则 Mybatis 会自动映射,不用配置
  3. 列名和属性名如果不相同,则要手动配置,同时指定两边的数据类型
    <resultMap id="GradeResultMap" type="com.ze.demo.model.Grade">
        <!--定义查询的列和属性之间的一一对应关系
        主键使用 id 标签来做映射;其他的字段统一使用 result 标签来做映射
        property 是指属性的名字
        column 是指查询出来的列的名字
        jdbcType 数据库中的字段的类型
        javaType 是属性的类型
        -->
        <id property="id" column="id" jdbcType="INTEGER" javaType="java.lang.Integer"/>
        <result property="gradeName" column="name" javaType="java.lang.String" jdbcType="VARCHAR"/>
    </resultMap>
2. 继承结果集 extends
    <!--
    可以继承自另外一个 ResultMap
    -->
    <resultMap id="GradeWithClazz" type="com.ze.demo.model.Grade" extends="GradeResultMap">
    
    </resultMap>
3. association 和 collection 表示一个对象、一个集合
3.1 一对一时,获取的结果是一个对象,用 association 标签
    <!-- 关系表中数据的封装规则 -->	 <!-- 指定关系表的实体类型 -->
        <association property="passport" javaType="com.qf.mybatis.part2.one2one.Passport">
            <id property="id" column="passport_id" />
            <result property="nationality" column="nationality" />
            <result property="expire" column="expire" />
          	<result property="passenger_id" column="passenger_id" />
        </association>
3.2 collection 标签 + ofType 属性 --> 指定集合类型
    <resultMap id="GradeWithClazz" type="com.ze.demo.model.Grade" extends="GradeResultMap">
        <!--指定一对多中多的一方
        通过 ofType 来指定集合中的类型
        -->
        <collection property="clazzes" ofType="com.ze.demo.model.Clazz">
            <id column="cid" property="id"/>
            <result column="cname" property="clazzName"/>
        </collection>
    </resultMap>
3.3 collection 标签 + ofType 属性 + select 属性 + column 属性 --> 指定集合通过调用 select 指定的方法,根据 column 指定的列名来查询出集合
  • 具体的查询次数,一般是 1 + 1的结果对应的 column 的个数
  • column 指定的列名是之前已经查询出来的列名,亦即准备据此列名查从表(多的一方)的数据
    <!--
    select 标签表示 调用 getClazzByGid 方法去查询班级数据,查询班级数据的时候,需要年级 id,column 则指定了年级 id 是当前查询出来的 id
    fetchType="lazy" 表示延迟加载,只有当调用 getClazzes 方法的时候,才会去执行查询
    -->
    <resultMap id="GradeWithClazz2" type="com.ze.demo.model.Grade" extends="GradeResultMap">
        <collection property="clazzes" ofType="com.ze.demo.model.Clazz" fetchType="lazy"
                    select="com.ze.demo.mapper.ClazzMapper.getClazzByGid" column="id"/>
    </resultMap>

四、MyBatis处理关联关系-多表连接【重点


实体间的关系:关联关系(拥有 has、属于 belong)

  • OneToOne:一对一关系(Passenger— Passport)

  • OneToMany:一对多关系(Employee — Department)

  • ManyToMany:多对多关系(Student — Subject)

Table建立外键关系
7G7RvF.png
Entity添加关系属性
7G7hDJ.jpg
Mapper中将属性与列名对应
7G7T4x.png
1. OneToOne

SQL参考OneToOneExample.sql

<mapper namespace="com.qf.mybatis.part2.one2one.PassengerDao">

  	<!-- 结果映射(查询结果的封装规则) -->
    <resultMap id="passengerResultMap" type="com.qf.mybatis.part2.one2one.Passenger">
        <id property="id" column="id"/>
        <result property="name" column="name" />
        <result property="sex" column="sex" />
        <result property="birthday" column="birthday" />

      	<!-- 关系表中数据的封装规则 -->	 <!-- 指定关系表的实体类型 -->
        <association property="passport" javaType="com.qf.mybatis.part2.one2one.Passport">
            <id property="id" column="passport_id" />
            <result property="nationality" column="nationality" />
            <result property="expire" column="expire" />
          	<result property="passenger_id" column="passenger_id" />
        </association>
    </resultMap>

  	<!-- 多表连接查询 -->					  	<!-- 结果映射(查询结果的封装规则)-->
    <select id="selectPassengerById" resultMap="passengerResultMap">
        <!-- 别名(避免与p1.id冲突) -->
        SELECT p1.id , p1.name , p1.sex , p1.birthday , p2.id as passport_id , p2.nationality , p2.expire 			, p2.passenger_id
        FROM t_passengers p1 LEFT JOIN t_passports p2
        ON p1.id = p2.passenger_id
        WHERE p1.id = #{id}
    </select>
</mapper>
2. OneToMany

SQL参考OneToManyExample.sql

<mapper namespace="com.qf.mybatis.part2.one2many.DepartmentDao">

  	<!-- 封装规则 -->
    <resultMap id="departmentResultMap" type="com.qf.mybatis.part2.one2many.Department">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="location" column="location" />
        
      	<!-- 关系表中数据的封装规则 -->		<!-- 指定关系表的实体类型 -->
        <collection property="emps" ofType="com.qf.mybatis.part2.one2many.Employee">
            <id property="id" column="emp_id" />
            <result property="name" column="emp_name" />
            <result property="salary" column="salary" />
            <result property="dept_id" column="dept_id" />
        </collection>
    </resultMap>

  	<!-- 多表连接查询 -->			      <!-- 封装规则 -->
    <select id="selectDepartmentById" resultMap="departmentResultMap" >
      	<!-- 别名(避免与d.id、d.name冲突)-->
        SELECT d.id , d.name , d.location , e.id AS emp_id , e.name emp_name , e.salary , e.dept_id
        FROM t_departments d LEFT JOIN t_employees e
        ON d.id = e.dept_id
        WHERE d.id = #{id}
    </select>

</mapper>
3. 关系总结

一方,添加集合;多方,添加对象。

双方均可建立关系属性,建立关系属性后,对应的Mapper文件中需使用< ResultMap >完成多表映射。

持有对象关系属性,使用< association property=“dept” javaType=“department” >

持有集合关系属性,使用< collection property=“emps” ofType=“employee” >

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值