MyBatis学习之多表查询(XML方式)(延迟/懒加载)

Table of Contents

Mybatis 多表查询

●  表之间的关系有⼏种: ⼀对多 ,多对⼀, ⼀对⼀, 多对多

01 ⼀对⼀

    1.1 SQL脚本:

    1.2 实体类

    1.3 映射接口:

    1.4 xml映射文件:

    1.5 最后配置XML映射文件

    1.6 创建测试类:

    1.7 小结:

02 一对多

    2.1 SQL脚本

    2.2 实体类

    2.3 映射接口

    2.4 xml映射文件

    2.5最后配置XML映射文件和懒加载

    2.6 测试(测试一对多以及懒加载)

    2.7 小结:

03 多对多

    3.1 SQL脚本

    3.2 实体类:

   3.3 映射接口

   3.4 xml映射文件:

   3.5 测试

04 总结

   4.1 一对一、多对一、多对多理解 

   4.2 个人对于延迟/懒加载 和嵌套查询、嵌套结果理解

   4.3 column取值依据


Mybatis 多表查询

●  表之间的关系有⼏种: ⼀对多 ,多对⼀, ⼀对⼀, 多对多

举例:

  1. ⼈和身份证号就是⼀对⼀ ⼀个⼈只能有⼀个身份证号 ⼀个身份证号只能属于⼀个⼈
  2. ⽤户和订单就是⼀对多 订单和⽤户就是多对⼀,⼀个⽤户可以下多个订单 ⼀个订单属于同⼀个⽤户
  3. ⽼师和学⽣之间就是多对多 ⼀个学⽣可以被多个⽼师教过 ⼀个⽼师可以教多个学⽣
  • 一对一关系推荐使用唯一主外键关联,即两张表使用外键关联,由于是一对一关联,因此还需要给外键列增加unique唯一约束。
  • 一对多时,表的创建是,外键设在多的一方
  • 多对多时,需要借助一个新的关联表,两个表的主键放在关联表即可

 

01 ⼀对⼀

一个学生只能有一个学号,一个学号只能给一个学生使用,这就是一对一的关系。一对一关系推荐使用唯一主外键关联,即两张表使用外键关联,由于是一对一关联,因此还需要给外键列增加unique唯一约束。下面我们就用一个简单示例来看看MyBatis怎么处理一对一关系。

1.1 SQL脚本:

  • 学号表
-- ----------------------------
-- Table structure for identity
-- ----------------------------
DROP TABLE IF EXISTS `identity`;
CREATE TABLE `identity`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `number` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of identity
-- ----------------------------
INSERT INTO `identity` VALUES (1, '20191216');
INSERT INTO `identity` VALUES (2, '20202020');

SET FOREIGN_KEY_CHECKS = 1;
  •  学生表:
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生名字',
  `sex` varchar(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生性别',
  `identityID` int(11) NULL DEFAULT NULL COMMENT '身份证表对应的ID号',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `identityFkey`(`identityID`) USING BTREE,
  CONSTRAINT `identityFkey` FOREIGN KEY (`identityID`) REFERENCES `identity` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, 'Tom', '男', 1);
INSERT INTO `student` VALUES (2, 'Jerry', '女', 2);

SET FOREIGN_KEY_CHECKS = 1;

tips:

student表的identityID作为外键,其参照identity表的主键id,因为是一对一关系,即一个identity只能让一个student使用,所以identityID做成了唯一键约束。如此一来,当一个student使用了一个identity之后,其他的student就不能使用该identity了。

1.2 实体类

  • Student.class:
public class Student {
    int id;
    String name;
    String sex;
    int identityID;

    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 String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getIdentityID() {
        return identityID;
    }

    public void setIdentityID(int identityID) {
        this.identityID = identityID;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", identityID=" + identityID +
                '}';
    }
}
  • Identity.class:
public class Identity {
    int id;
    String number;

    public int getId() {
        return id;
    }

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

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

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

1.3 映射接口:

  • Mybatis官方手册建议通过mapper接口的代理对象访问mybatis,该对象关联了SqlSession对象,开发者可以通过该对象直接调用方法操作数据库。
  • 官方映射接口取名是xxxMapper,这里个人习惯取xxxDao,都是一样,只要映射接口 和 xml映射文件 的文件名相同,目录相同 即可
  • IStudentDao.class:
public interface IStudentDao {
    /**
     * 查找所有学生,同时获取学生所属identity
     * @return
     */
    public List<Student> findAll();
}
  • IIdentityDao.class
public interface IIdentityDao {
    /**
     * 根据id查找学号
     * @param id
     * @return
     */
    public IIdentityDao findIdentityById(int id);
}

1.4 xml映射文件:

注意:

映射接口、xml映射文件  文件名要相同,目录也要相同:

  • IStudentDao.xml:(这里用的是嵌套查询,没用嵌套结果)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IStudentDao">
    <!--    映射Student对象的resultMap -->
    <resultMap id="studentMap" type="mybatis.demo.domain.Student">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="sex" column="sex"/>
<!--        这里使用的是 嵌套查询-->
        <association property="identity" javaType="mybatis.demo.domain.Identity" column="id" select="mybatis.demo.dao.IIdentityDao.findIdentityById"/>
    </resultMap>

    <!--    查找所有学生,同时获取学生所属identity,结果类型返回resultMap而不是resultType-->
    <select id="findAll" resultMap="studentMap">
        select * from Student
    </select>
    
</mapper>
  • IIdentityDao.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IIdentityDao">
    <!--    查找identity,结果类型返回resultType而不是resultMap-->
    <select id="findIdentityById" resultType="mybatis.demo.domain.Identity">
        select * from identity where id=#{id}
    </select>
</mapper>

说明;

  1. 命名空间(Namespaces)的作用并不大,是可选的。 但现在,随着命名空间越发重要,你必须指定命名空间。

    命名空间的作用有两个,一个是利用更长的完全限定名来将不同的语句隔离开来,同时也实现了对应接口绑定。

  2. <select id ="">中的id属性,对应接口中某个方法名

  3.  <association >:用于映射一对一的关联关系,JavaType映射到一个 JavaBean 的数据类型,select=“” 嵌套一个查询,column是传给嵌套查询的条件参数

  4. 而有个<collection> 是⽤于建⽴⼀对多中集合属性的对应关系,ofType⽤于指定集合元素的 数据类型<即泛型>

1.5 最后配置XML映射文件

在MyBatis的XML配置中,配置映射接口  和 映射文件的绑定,因为需要告诉 MyBatis 到哪里去找到这些SQL语句,用来让MyBatis实现映射接口,最后使用代理对象操作这些接口中的方法,从而操作数据库

mybatis-config.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <properties
        >
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatisdemo?characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </properties>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--
            <mapper> 用于配置映射文件
            class属性用于注解的方式
            resource属性用于XML文件的方式
            -->
        <mapper resource="mybatis.demo.dao/IIdentityDao.xml"/>
        <mapper resource="mybatis.demo.dao/IStudentDao.xml"/>
    </mappers>
</configuration>

1.6 创建测试类:

test.java:

public class test {
    InputStream inputStream;
    SqlSession session;
    SqlSessionFactory sqlSessionFactory;
    @Before
    public void init() throws IOException {
        //1.读取配置⽂件
        inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        //2.创建SqlSessionFactory的构建者对象,使⽤构建者创建⼯⼚对象SqlSessionFactory
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //3.使⽤SqlSessionFactory⽣产SqlSession对象
        session = sqlSessionFactory.openSession();
    }
    @After
    public void destroy() throws IOException{
        //6.释放资源
        session.close();
        inputStream.close();
    }
    @Test
    public void test() throws IOException {
        //4.使⽤SqlSession创建dao接⼝的代理对象
        IStudentDao dao = session.getMapper(IStudentDao.class);
        //5.使⽤代理对象执⾏查询所有⽅法
        List<Student> students = dao.findAll();
        for (Student student : students){
            System.out.println(student);
        }
    }
}
  • 测试结果:

1.7 小结:

由于Student类除了简单的属性id、name、sex之外,还有一个关联对象identity,所以返回的是一个名为studentMap的resultMap。studentMap中使用了<association .../>元素映射一对一的关联关系。其中,select属性表示会使用column属性的id值作为参数执行IIdentityDao.xml中定义的selectCardById语句,查询对应的 Identity 数据(即嵌套查询),查询出的数据将被封装到property表示的identity对象当中。

嵌套查询、嵌套结果后面再总结,官方教程地址:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps

02 一对多

一对多是非常常见的关系,比如,一个班级可以有多个学生,一个学生只能属于一个班级,班级和学生是一对多的关系,而学生和班级是多对一的关系。数据库中一对多关系通常使用主外键关联,外键列应该在多方,即多方维护关系

2.1 SQL脚本

  • 班级表:
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `className` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '班级名称',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of class
-- ----------------------------
INSERT INTO `class` VALUES (1, '16软件1班');
INSERT INTO `class` VALUES (2, '16软件2班');

SET FOREIGN_KEY_CHECKS = 1;
  • 学生表:
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生名字',
  `sex` varchar(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生性别',
  `classID` int(11) NULL DEFAULT NULL COMMENT '身份证表对应的ID号',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `classFk`(`classID`) USING BTREE,
  CONSTRAINT `classFk` FOREIGN KEY (`classID`) REFERENCES `class` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, 'Tom', '男', 1);
INSERT INTO `student` VALUES (2, 'Jerry', '女', 2);
INSERT INTO `student` VALUES (3, 'Jack', '男', 1);

SET FOREIGN_KEY_CHECKS = 1;

student表的classID作为外键参照class表的主键id。

2.2 实体类

创建一个Class对象和一个Student对象分别映射class和student表。

  • Student.class:
package mybatis.demo.domain;

public class Student {
    int id;
    String name;
    String sex;
    int classID;

    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 String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getClassID() {
        return classID;
    }

    public void setClassID(int classID) {
        this.classID = classID;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", classID=" + classID +
                '}';
    }
}
  • Class.class:

班级和学生是一对多的关系,即一个班级可以有多个学生。在Class类当中定义了一个students属性,该属性是一个List集合,用来映射一对多的关联关系,表示一个班级有多个学生。

public class Class {
    private int id;             //对应数据库表Class中的id字段
    private String className;   //对应数据库表Class中的className字段

    private List<Student> students; //该属性是一个List集合,用来映射一对多的关联关系,表示一个班级有多个学生。

    public int getId() {
        return id;
    }

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

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

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

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

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

2.3 映射接口

  • IClassDao.class:
public interface IClassDao {
    /**
     * 查找所有班级,并查询旗下包含的学生
     * @return
     */
    public List<Class> findAllClass();
}
  • IStudentDao.class:
public interface IStudentDao {
    /**
     * 根据班级id查询学生信息
     * @param id
     * @return
     */
    public Student findStudentByClassId(int id);
}

2.4 xml映射文件

  • IClassDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IClassDao">
    <!--查找所有班级-->
    <select id="findAllClass" resultMap="classMap">
        select * from class
    </select>

    <!-- 映射Class对象的resultMap -->
    <resultMap id="classMap" type="mybatis.demo.domain.Class">
        <id property="id" column="id"/>
        <result property="className" column="className"/>

        <!-- 一对多关联映射:collection ,students是List集合,ofType是List中的<泛型>  ,
select是嵌套select查询,column是传给select的参数 fetchType="lazy"延迟加载    -->
        <collection property="students" ofType="mybatis.demo.domain.Student" column="id" select="mybatis.demo.dao.IStudentDao.selectStudentByClazzId" fetchType="lazy"/>
    </resultMap>
</mapper>

注意

  • collection中的column=“id”是 班级的id,因为此resultMap的type=“mybatis.demo.domain.Class”
  • 一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载;
  • IStudentDao.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IStudentDao">

    <!-- 根据班级id查询学生信息,多表连接,返回resultMap -->
    <select id="selectStudentByClazzId" parameterType="int" resultType="mybatis.demo.domain.Student">
        SELECT * FROM student where classID=#{id}
    </select>
</mapper>

2.5最后配置XML映射文件和懒加载

在MyBatis的XML配置中,配置映射接口  和 映射文件的绑定,因为需要告诉 MyBatis 到哪里去找到这些SQL语句,用来让MyBatis实现映射接口,最后使用代理对象操作这些接口中的方法,从而操作数据库

在实际开发中,一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载; 而多对一只是关联到一个对象,所以通常使用多表连接直接把数据提取出来。

mybatis-config.xml:



......

    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
......
    <mappers>
        <!--
            <mapper> 用于配置映射文件
            class属性用于注解的方式
            resource属性用于XML文件的方式
            -->
        <mapper resource="mybatis.demo.dao/IClassDao.xml"/>
        <mapper resource="mybatis.demo.dao/IStudentDao.xml"/>
    </mappers>

lazyLoadingEnabled属性表示延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认为false。
agressiveLazyLoading属性启用时,会使带有延迟加载属性的对象立即加载; 反之,每种属性将会按需加载。默认为true,所以这里需要设置成false。

2.6 测试(测试一对多以及懒加载)

  • 测试类
public class test {
    ...
    其余代码略,和上面一样的

    ...    


    @Test
    public void test() throws IOException {
        //4.使⽤SqlSession创建dao接⼝的代理对象
        IStudentDao studentDao = session.getMapper(IStudentDao.class);
        IClassDao classDao=session.getMapper(IClassDao.class);
        //5.使⽤代理对象执⾏查询所有⽅法


        //一对多
        List<Class> classes = classDao.findAllClass();
        for (Class cla:classes) {
            System.out.println("className="+cla.getClassName());
        }
    }
}
  • 测试结果

可以看到,MyBatis只是执行了查询班级的SQL语句,由<colletion fethType=lazy">使用的是懒加载,因此,当没有使用到关联的学生对象时,并没有发送查询学生的SQL语句。

  • 修改测试代码:
    @Test
    public void test() throws IOException {
        //4.使⽤SqlSession创建dao接⼝的代理对象
        IStudentDao studentDao = session.getMapper(IStudentDao.class);
        IClassDao classDao=session.getMapper(IClassDao.class);
        //5.使⽤代理对象执⾏查询所有⽅法


        //一对多
        List<Class> classes = classDao.findAllClass();
        for (Class cla:classes) {
            System.out.println("className="+cla.getClassName() + "students=["+cla.getStudents()+"]");
        }
    }

测试结果:

可以看到,MyBatis执行了查询班级的SQL语句之后,又执行了根据class_id 查询学生信息的SQL语句。这就是所谓的懒加载,关联对象,用到才会加载,执行相应的SQL语句,而不是一下全部查询

2.7 小结:

  • 一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载;
  • 多对一只是关联到一个对象,所以通常使用多表连接 直接把数据提取出来。
  • 懒加载,关联对象用到才会加载,执行相应的SQL语句,而不是一下全部查询

多对一,和一对一差不多,都是用到<association>标签,反正,

多的一方(集合)用<collection>,

一的一方(仅仅是一个pojo/javaBean 对象)用<association>标签

所以多对一不做笔记了

 

03 多对多

对于数据库中多对多关系建议使用一个中间表来维护关系

table1(主键id1)和table2(主键id2)为多对多

中间表为t1_t2  :(table1_id,table2_id)为主键,且table1_id 作为外键参照table1的主键id1,table2_id 作为外键参照table2的主键id2

示例:⽤户和⻆⾊ ⼀个⽤户可以有多个⻆⾊ ⼀个⻆⾊可以赋予多个⽤户 步骤: 1、建⽴两张表: ⽤户表,⻆⾊表 让⽤户表和⻆⾊表具有多对多的关系。需要使⽤中间表,中间表中包含各⾃的主 键,在中间表中是外键。 2、建⽴两个实体类:⽤户实体类和⻆⾊实体类 让⽤户和⻆⾊的实体类 能体现出来多对多的关系 各⾃包含对⽅⼀个集合引⽤ 3、建⽴两个配置⽂件 ⽤户的配置⽂件 ⻆⾊ 的配置⽂件 4、实现配置: 当我们查询⽤户时,可以同时得到⽤户所包含的⻆⾊信息 当我们查询 ⻆⾊时,可以同时得到⻆⾊的所赋予的⽤户信息

3.1 SQL脚本

  • student表:
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生名字',
  `sex` varchar(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生性别',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, 'Tom', '男');
INSERT INTO `student` VALUES (2, 'Jerry', '女');
INSERT INTO `student` VALUES (3, 'Jack', '男');
INSERT INTO `student` VALUES (4, 'Marry', '女');

SET FOREIGN_KEY_CHECKS = 1;
  • role表:
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `roleName` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '学生');
INSERT INTO `role` VALUES (2, '班长');
INSERT INTO `role` VALUES (3, '学习委员');

SET FOREIGN_KEY_CHECKS = 1;
  • role_student表:
DROP TABLE IF EXISTS `role_student`;
CREATE TABLE `role_student`  (
  `role_id` int(11) NOT NULL,
  `student_id` int(11) NOT NULL,
  PRIMARY KEY (`role_id`, `student_id`) USING BTREE,
  INDEX `stuFkey`(`student_id`) USING BTREE,
  CONSTRAINT `roleFkey` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `stuFkey` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role_student
-- ----------------------------
INSERT INTO `role_student` VALUES (1, 1);
INSERT INTO `role_student` VALUES (2, 1);
INSERT INTO `role_student` VALUES (1, 2);
INSERT INTO `role_student` VALUES (1, 3);
INSERT INTO `role_student` VALUES (1, 4);
INSERT INTO `role_student` VALUES (3, 4);

SET FOREIGN_KEY_CHECKS = 1;

 

3.2 实体类:

  • Role.class

在Role类中,定义了一个students集合,用来映射多对多的关联关系,表示一个角色可以对应多个学生 

public class Role {
    private int id;
    private String roleName;
    public int getId() {
        return id;
    }

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

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }


    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                '}';
    }
}
  • Student.class:

在Student 类中定义了一个roles 属性,该属性是一个List集合,用来映射多对多的关联关系,表示该学生关联的多个角色。

public class Student {
    int id;
    String name;
    String sex;
    List<Role> roles;//该学生拥有的所有角色
    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 String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public List<Role> getRoles() {
        return roles;
    }
    
    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

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

3.3 映射接口

  • IStudentDao.class:
public interface IStudentDao {
    /**
     * 查询所有学生信息
     * @return
     */
    public List<Student> findAllStudent();
}

3.4 xml映射文件:

IStudentDao.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IStudentDao">

    <!-- 查询学生信息,返回resultMap -->
    <select id="findAllStudent" parameterType="int" resultMap="studentMap">
        SELECT student.id as SID, student.name as SName,student.sex , role.*
        FROM student
        left join role_student on student.id=role_student.student_id
        left join role on role_student.role_id=role.id
    </select>

    <resultMap id="studentMap" type="mybatis.demo.domain.Student">
        <id property="id" column="SID"/>
        <result property="name" column="SName"/>
        <result property="sex" column="sex"/>
          <!-- 这里用的是嵌套结果,并没有用嵌套查询 -->
        <collection property="roles" ofType="mybatis.demo.domain.Role">
            <id property="id" column="id"/>
            <result property="roleName" column="roleName"/>
        </collection>
    </resultMap>

</mapper>

注意:

  •  <resultMap>

        <id property="id" column="SID"/>
        <result property="name" column="SName"/>
        <result property="sex" column="sex"/> 中的

column的值,是根据SQL语句中的字段值,如果select语句中为字段设置了别名,column就是别名的值,没别名就是数据库的字段值

  • 设置别名,就是防止字段名冲突

3.5 测试

  • 测试类:
    @Test
    public void test() throws IOException {
        //4.使⽤SqlSession创建dao接⼝的代理对象
        IStudentDao studentDao = session.getMapper(IStudentDao.class);
        //5.使⽤代理对象执⾏查询所有⽅法

        List<Student> allStudent = studentDao.findAllStudent();
        for (Student student:allStudent) {
            System.out.println(student);
        }
    }
  • 测试结果:

这里命名只有4条显示,为什么Total=条结果了,因为SQL语句的结果就是6条:

而这里用的是嵌套结果,学生里嵌套了角色,所以一个学生的多个角色都嵌套进了同一个学生里,而单纯从数据库直接执行sql语句,并没嵌套,所以6条都显示了(注意看,Tom,Marry都出现了2次了) 

 

04 总结

4.1 一对一、多对一、多对多理解 

多对一,一对一中,

表示 一(关联的是一个JavaBean或pojo对象)用的是<association>标签,

多 (关联的是集合)用的是<collection>标

 

多对多,需要借助中间表,中间表来维护关系

table1(主键id1)和table2(主键id2)为多对多

中间表为t1_t2  :(table1_id,table2_id)为主键,且table1_id 作为外键参照table1的主键id1,table2_id 作为外键参照table2的主键id2

(借助中间表,相当拆分成了 多对一对多,可以这么去理解)

4.2 个人对于懒加载 和嵌套查询、嵌套结果理解

  • 懒加载,关联对象用到才会加载,执行相应的SQL语句,而不是一下全部查询
  • 一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载; 而多对一只是关联到一个对象,所以通常使用多表连接直接把数据提取出来。
  • 个人理解,嵌套查询的懒加载才会生效,因为是嵌套的select是分离的,而嵌套结果是一次性查找所有结果 

 4.3 column取值依据

  •  <resultMap>

        <id property="id" column="SID"/>
        <result property="name" column="SName"/>
        <result property="sex" column="sex"/> 中的

column的值,是根据SQL语句中的字段值,如果select语句中为字段设置了别名,column就是别名的值,没别名就是数据库的字段值

  • 设置别名,就是防止字段名混淆

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值