Mybatis的一对多与多对一

一、Mybatis一对多与多对一

  • XML映射器

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

  • resultMap

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

  • resultMap的属性列表

1、constructor - 用于在实例化类时,注入结果到构造方法中
idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
arg - 将被注入到构造方法的一个普通结果
2、id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
3、result – 注入到字段或 JavaBean 属性的普通结果
4、association – 一个复杂类型的关联;许多结果将包装成这种类型
嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
5、collection – 一个复杂类型的集合
嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
6、discriminator – 使用结果值来决定使用哪个 resultMap
case – 基于某些值的结果映射
嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

二、一对多

  • 在Mybatis中实现一对多和多对一有两种方法:嵌套结果映射和嵌套 Select 查询
  • 1、resultMap结果映射

在一对多或者是多对一中我们都不可避免要使用 resultMap这个元素和标签,上面我们列出了resultMap的所有属性,但是经常用的就只有4个(id,result,association,collection)。

  • 2、实体类编写

一对多:顾名思义一个对象对应多个对象,例如一个老师教多个学生,也就是说这个老师对象包含了多个学生对象。因此在我们编写实体类时应该要考虑到如何把多个学生对象作为老师对象的属性,我们很快就可以想到使用集合来解决这一问题,把所用学生对象都存放到一个集合当中,然后让这个集合老师对象的一个属性。
Teacher对象:

package com.my.pojo;

import java.util.List;

public class Teacher {

    private int id;
    private String name;
    private List<Student> students;

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", students=" + students +
                '}';
    }

    public Teacher(int id, String name, List<Student> students) {
        this.id = id;
        this.name = name;
        this.students = students;
    }

    public Teacher() {
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Student对象:

package com.my.pojo;

public class Student {

    private int id;
    private String name;
    private int tid;

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", tid=" + tid +
                '}';
    }

    public Student(int id, String name, int tid) {
        this.id = id;
        this.name = name;
        this.tid = tid;
    }

    public Student() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getTid() {
        return tid;
    }

    public void setTid(int tid) {
        this.tid = tid;
    }
}

  • 方法一、嵌套结果映射

 <!--一对多,方法一:结果映射-->
    <select id="teacher" resultMap="teacherstudent" >
        SELECT s.id sid,s.name sname,t.id tid,t.name tname
        FROM student s,teacher t WHERE t.id = #{id} AND t.id = s.tid
    </select>

    <resultMap id="teacherstudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" ofType="Student" >
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

这种方法的核心就是SQL语句的编写和ResultMap的结果集映射。

  • 使用这种方法的SQL语句比较复杂,我们必须要使用联表查询同时查询出我们所需要的数据,然后再通过ResultMap把读取到的数据映射到been对象当中去。
  • id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
  • id 和 result 元素的属性:
    property 映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。
    column 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
    javaType 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
    jdbcType JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。
    typeHandler 使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
  • collection元素的属性:
    property 映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。
    javaType 一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
    jdbcType JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。
    typeHandler 使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
    column 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
    select 用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
    fetchType 可选的。有效值为 lazy 和 eager。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled,使用属性的值。
    oftype 集合的泛型,它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来。
  • 方法二、嵌套 Select 查询

 <select id="teacher2" resultMap="teacherstudent2">
        select * from teacher where id = #{id}
    </select>

    <resultMap id="teacherstudent2" type="Teacher">
        <collection property="students" javaType="ArrayList" column="id" ofType="Student" select="student"/>
    </resultMap>

    <select id="student" resultType="Student">
        select * from student where tid = #{id}
    </select>
  • 这种方法的SQL语句十分简单,由2个最简单的查询语句组成,就是这么简单。我们有两个 select 查询语句:一个用来加载老师(Teacher),另外一个用来加载学生(Student),而且l老师的结果映射描述了应该使用student 语句加载它的 students属性。其它所有的属性将会被自动加载,只要它们的列名和属性名相匹配。这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。
  • 其它的东西我们在上一个方法中已经介绍了,在这里就不再啰嗦了。

三、多对一

  • 多对一与一对多大致上是差不多的同样有与一对多一样的两种方法:嵌套结果映射和嵌套 Select 查询。大部分1知识点是一样的这里就不重复了。

  • 实体类编写

  • 多对一顾名思义就是多个对象都与一个对象有关系例如:多个学生对象都是由一个老师对象教的。因此我们在创建学生类时应该要考虑到如何体现这个学生类关联一个老师对象,所以我们可以把这个老师对象作为学生类的一个属性。
    Teacher对象:

package com.my.pojo;

public class Teacher {

    private int id;
    private String name;

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public Teacher() {
    }

    public Teacher(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Student对象:

package com.my.pojo;

public class Student {

    private int id;
    private String name;
    private Teacher teacher;

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", teacher=" + teacher.toString() +
                '}';
    }

    public Student() {
    }

    public Student(int id, String name, Teacher teacher) {
        this.id = id;
        this.name = name;
        this.teacher = teacher;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
}
  • 方法一、嵌套结果映射

<select id="student" resultMap="studentteacher">
        SELECT s.id sid,s.name sname,t.id tid,t.name tname
        FROM student s,teacher t WHERE s.tid = t.id
    </select>

    <resultMap id="studentteacher" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" column="tid" javaType="Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>
  • 相信大家一下就发现了一对多和多对一的区别了,没错就是把collection换成了association,以及缺少了oftype属性,因为这不是集合没有泛型,所以不需要有这个属性。
  • association元素的属性:
    property 映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。
    javaType 一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
    jdbcType JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。
    typeHandler 使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
    column 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
    select 用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
    fetchType 可选的。有效值为 lazy 和 eager。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled,使用属性的值。
  • 方法二、嵌套 Select 查询

<select id="student2" resultMap="studentteacher2">
        select * from student
    </select>

    <resultMap id="studentteacher2" type="Student">
        <association property="teacher" column="tid" javaType="Teacher" select="teacher2"/>
    </resultMap>

    <select id="teacher2" resultType="Teacher">
        select * from teacher where id = #{tid}
    </select>
  • 结合前面的一对多进行理解,不再重复。

  • 总结

总的来说Mybatis的一对多与多对一并不难理解,只要我们理好每个对象之间的关系就会很容易了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高山无涯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值