Mybatis的多表查询操作


1 mybatis多表查询XML方式

1.1 一对一查询

1.1.1 表关系

一对一关系中,表关系由任意一方维护,以人和身份证为例,一个人对应一个身份证,一个身份证对应一个人。本案例中由身份证表维护表关系

1589801843745

1.1.2 需求

查询所有的身份证,并且要将身份证对应的人也查询出来

1.1.3 实现步骤

第一步:建表

DROP TABLE IF EXISTS `person`;
CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL COMMENT '姓名',
  `age` int(11) NOT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='用户表';
insert  into `person`(`id`,`name`,`age`) values (1,'陈乔恩',39),(2,'钟汉良',44),(3,'林志颖',44),(4,'吴奇隆',48);
DROP TABLE IF EXISTS `card`;
CREATE TABLE `card` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `number` varchar(30) NOT NULL COMMENT '身份证号',
  `pid` int(11) NOT NULL COMMENT '所属用户',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='身份证表';
insert  into `card`(`id`,`number`,`pid`) values (1,'	110101199003079008',1),(2,'110101199003079331',2),(3,'	11010119900307299X',3),(4,'	110101199003070791',4);

第二步:创建实体类

Person 人类

package cn.oldlu.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    
    
    private Integer id;
    private String name;
    private Integer age;
}

Card 身份证类,因为需求是查询身份证,并且查询对应的人,所以应该是Card实体类中存放Perosn属性

package cn.oldlu.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Card {
    
    
    private Integer id;
    private String number;
    private Person person;
}

第三步:创建Dao接口


package cn.oldlu.dao;

import cn.oldlu.domain.Card;

import java.util.List;

public interface CardDao {
    
    
    List<Card> findAll();
}

第四步:创建映射文件

resultMap的分析与编写

1589812615436

在src或resources目录创建CardMapper.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的值是UserDao的类全名-->
<mapper namespace="cn.oldlu.dao.CardDao">
    <resultMap id="cardMap" type="card">
        <!--把结果集中cid这一列赋值给id属性-->
        <id column="cid" property="id"></id>
        <!--把结果集中cnumber这一列的值赋值给number属性-->
        <result column="cnumber" property="number"></result>
        <!--给person属性赋值-->
        <association property="person" javaType="person">
            <!--把结果集中的pid这一列的值赋值给id属性-->
            <id column="pid" property="id"></id>
            <!--把结果集中的pname这一列的值赋值给name属性-->
            <result column="pname" property="name"></result>
            <!--把结果集中的page这一列的值赋值给age属性-->
            <result column="page" property="age"></result>
        </association>
    </resultMap>
    
    <select id="findAll" resultMap="cardMap">
        SELECT
            c.id 'cid',
            c.number 'cnumber',
            p.id 'pid',
            p.name 'pname',
            p.age 'page'
        FROM card c
        LEFT JOIN person p ON p.id = c.pid
    </select>
</mapper>

在这里插入图片描述

第五步:主配置导入映射文件

在mybatis-config.xml中的mappers标签中引入CardMapper.xml

<mappers>
    <mapper resource="CardMapper.xml"></mapper>
</mappers>

第六步:编写测试方法

@Test
public void testFindAll()throws Exception{
    
    
    /*1.读取配置文件并创建SqlSessionFactory*/
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

    /*2.获取sqlSession[这是mybatis的重要API, 使用它可以操作数据库crud*/
    SqlSession sqlSession = sqlSessionFactory.openSession();

    /*3.由Mybatis框架创建CardDao的实现类对象,我们称为代理对象*/
    CardDao cardDao = sqlSession.getMapper(CardDao.class);

    /*4.查询所有身份证*/
    List<Card> cards = cardDao.findAll();

    /*5.释放资源*/
    sqlSession.close();

    //测试查询结果
    System.out.println(cards);
}

1.1.4 小结

一对一查询,使用resultMap+association标签

1.2 一对多查询

1.2.1 表关系

一对多关系中,表关系由多的一方维护,以班级和学生为例,一个班级可以有多个学生,学生表应该维护表关系。

1589806452924

1.2.2 需求

查询所有班级信息,并且要将班级对应的学生信息也同时查询出来

1.2.3 实现步骤

第一步:建表

DROP TABLE IF EXISTS `classes`;
CREATE TABLE `classes` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) DEFAULT NULL COMMENT '班级名称',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='班级表';
INSERT  INTO `classes`(`id`,`name`) VALUES (1,'一班'),(2,'二班');

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(24) NOT NULL,
  `age` INT(11) NOT NULL,
  `cid` INT(11) NOT NULL COMMENT '所属班级',
  PRIMARY KEY (`id`),
  KEY `cid` (`cid`)
) ENGINE=INNODB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

INSERT  INTO `student`(`id`,`name`,`age`,`cid`) VALUES (8,'王力宏',23,1),(9,'张韶涵',22,1),(10,'张晋',24,1),(11,'罗晋',25,1),(12,'唐僧',12,2),(13,'孙悟空',22,2),(14,'猪八戒',21,2);

第二步:创建实体类

Student学生类

package cn.oldlu.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    
    
    private Integer id;
    private String name;
    private Integer age;
}

Classes班级类,因为需求是查询班级,以及对应的学生,所以应该是班级类中定义List表示一堆学生

package cn.oldlu.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Classes {
    
    
    private Integer id;
    private String name;
    private List<Student> students;
}

第三步:创建Dao接口

package cn.oldlu.dao;

import cn.oldlu.domain.Classes;

import java.util.List;

public interface ClassesDao {
    
    
    List<Classes> findAll();
}

第四步:创建映射文件

resultMap的分析与编写

1589807864329

在src或者resources目录下创建ClassesMapper.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">
<mapper namespace="cn.oldlu.dao.ClassesDao">
    <resultMap id="classesMap" type="classes">
        <id column="cid" property="id"></id>
        <result column="cname" property="name"></result>
        <collection property="students" ofType="student">
            <id column="sid" property="id"></id>
            <result column="sname" property="name"></result>
            <result column="sage" property="age"></result>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="classesMap">
        SELECT
            c.id 'cid',
            c.name 'cname',
            s.id 'sid',
            s.name 'sname',
            s.age 'sage'
        FROM classes c
        LEFT JOIN student s ON s.`cid` = c.`id`
    </select>
</mapper>

第五步:主配置导入映射文件

在mybatis-config.xml中的mappers标签中引入ClassesMapper.xml

<mappers>
    <mapper resource="ClassesMapper.xml"></mapper>
</mappers>

第六步:编写测试方法

@Test
public void testFindAll()throws Exception{
    
    
    /*1.读取配置文件并创建SqlSessionFactory*/
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

    /*2.获取sqlSession[这是mybatis的重要API, 使用它可以操作数据库crud*/
    SqlSession sqlSession = sqlSessionFactory.openSession();

    /*3.由Mybatis框架创建UserDao的实现类对象,我们称为代理对象*/
    ClassesDao classesDao = sqlSession.getMapper(ClassesDao.class);

    /*4.查询所有班级*/
    List<Classes> classes = classesDao.findAll();

    /*5.释放资源*/
    sqlSession.close();

    //测试结果
    System.out.println(classes);
}

1.2.4 小结

一对多查询,使用resultMap+collection标签

总结:javatype和ofType 都是用来指明对象类型, 区别在于使用的场合不一样, javatype是在pojo属性类型, ofType指定是当对象需要Collection进行list集合映射对象的时候使用 ofType,也就是一对多映射的时候使用

1.3 多对多关系

1.3.1 表关系

多对多关系中表关系由中间表维护,以学生和课程为例,一个学生对应多们课程,一个课程同时对应多个学生

1589814558313

1.3.2 需求

查询所有的学生,并且要将学生对应的课程也查询出来

1.3.3 实现步骤

第一步: 建表

DROP TABLE IF EXISTS `classes`;
CREATE TABLE `classes` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) DEFAULT NULL COMMENT '班级名称',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='班级表';
INSERT  INTO `classes`(`id`,`name`) VALUES (1,'一班'),(2,'二班');

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(24) NOT NULL,
  `age` INT(11) NOT NULL,
  `cid` INT(11) NOT NULL COMMENT '所属班级',
  PRIMARY KEY (`id`),
  KEY `cid` (`cid`)
) ENGINE=INNODB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

INSERT  INTO `student`(`id`,`name`,`age`,`cid`) VALUES (8,'王力宏',23,1),(9,'张韶涵',22,1),(10,'张晋',24,1),(11,'罗晋',25,1),(12,'唐僧',12,2),(13,'孙悟空',22,2),(14,'猪八戒',21,2);

DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(30) NOT NULL COMMENT '课程名',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='课程表';
INSERT  INTO `course`(`id`,`name`) VALUES (1,'语文'),(2,'数学');

DROP TABLE IF EXISTS `stu_cr`;
CREATE TABLE `stu_cr` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `sid` INT(11) NOT NULL,
  `cid` INT(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
INSERT  INTO `stu_cr`(`id`,`sid`,`cid`) VALUES (1,8,1),(2,8,2),(3,9,1),(4,10,2),(5,13,2),(6,13,1),(7,14,1),(8,11,2),(9,12,1),(10,12,2);

第二步:创建实体类

Course 课程类

package cn.oldlu.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Course {
    
    
    /**课程ID*/
    private Integer id;
    /**课程名*/
    private String name;
}

Student 学生类,学生类之前已经定义过了,只需要添加新的course属性即可。因为需求是查询学生信息,以及对应的课程,所以应该在学生类中定义List,表示一堆课程

package cn.oldlu.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    
    
    private Integer id;
    private String name;
    private Integer age;
    private List<Course> courses;
}

第三步:创建Dao接口

package cn.oldlu.dao;
import cn.oldlu.domain.Student;
import java.util.List;

public interface StudentDao {
    
    
    List<Student> findAll();
}

第四步:创建映射文件

resultMap的分析与编写

1589815904713

在src或resources目录下创建StudentMapper.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的值是UserDao的类全名-->
<mapper namespace="cn.oldlu.dao.StudentDao">
    <resultMap id="studentMap" type="student">
        <!--把结果集中sid这一列赋值给id属性-->
        <id column="sid" property="id"></id>
        <!--把结果集中sname这一列的值赋值给name属性-->
        <result column="sname" property="name"></result>
        <!--把结果集中sage这一列的值赋值给age属性-->
        <result column="sage" property="age"></result>
        <!--给courses属性赋值-->
        <collection property="courses" ofType="course">
            <!--把结果集中的cid这一列的值赋值给id属性-->
            <id column="cid" property="id"></id>
            <!--把结果集中的cname这一列的值赋值给name属性-->
            <result column="cname" property="name"></result>
        </collection>
    </resultMap>
    
    <select id="findAll" resultMap="studentMap">
        SELECT
            s.id 'sid',
            s.name 'sname',
            s.age 'sage',
            c.id 'cid',
            c.name 'cname'
        FROM student s
        LEFT JOIN stu_cr sc ON s.id = sc.`sid`
        LEFT JOIN course c ON c.`id` = sc.`cid`
    </select>

</mapper>

第五步:主配置导入映射文件

在mybatis-config.xml中的mappers标签中引入StudentMapper.xml

<mappers>
    <mapper resource="StudentMapper.xml"></mapper>
</mappers>

第六步:编写测试方法

@Test
public void testFindAll()throws Exception{
    
    
    /*1.读取配置文件并创建SqlSessionFactory*/
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

    /*2.获取sqlSession[这是mybatis的重要API, 使用它可以操作数据库crud*/
    SqlSession sqlSession = sqlSessionFactory.openSession();

    /*3.由Mybatis框架创建UserDao的实现类对象,我们称为代理对象*/
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);

    /*4.查询所有学生*/
    List<Student> students = studentDao.findAll();

    /*5.释放资源*/
    sqlSession.close();

    //测试结果
    System.out.println(students);
}

1.3.5 小结

多对多查询,使用resultMap+collection标签

1.4 多表查询总结

什么情况使用resultMap标签

情况1:如果查询的结果集的列名和实体类的属性名不匹配,就需要使用resultMap建立匹配规则

情况2:如果涉及到多表查询也需要使用resultMap标签,一对一查询用resultMap+association,一对多和多对多 resultMap+collection

2 实际开发mybatis多表查询

2.1 前言

表之间的关系有几种:一对多、多对一、 一对一、多对多
在多对一关系中,把多的部分拆成一个一个对象其实就是一对一关系,如账户和用户是多对一关系,但每个账户只对应一个用户。所以在mybatis中,多对一的关系可以看成一对一的关系。
这里我把一对多和多对一的xml配置方式总结了一下,同时还有加载方式。
一对多,多对多:通常情况下我们都是采用延迟加载。
多对一,一对一:通常情况下我们都是采用立即加载。
至于注解方式和多对多查询的xml和注解方式我会另外写博客。

2.2 数据库表及关系

我们以用户和账户为例,用户可以有多个账户,账户只能对应一个用户。所以用户对账户是一对多关系,账户对用户是多对一关系。表如下图所示,用户表user,账户表account,账户表UID对应用户表id。
用户表user
账户表account

2.3 一对多查询

首先我们要在User实体类中添加List accounts的集合成员变量,表示一对多映射关系,主表实体含有从表实体的集合引用。

public class User implements Serializable{
    
    
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    //一对多映射关系,主表实体含有从表实体的集合引用
    private List<Account> accounts;

    public List<Account> getAccounts() {
    
    
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
    
    
        this.accounts = accounts;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }

    public Integer getId() {
    
    
        return id;
    }

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

    public String getUsername() {
    
    
        return username;
    }

    public void setUsername(String username) {
    
    
        this.username = username;
    }

    public String getAddress() {
    
    
        return address;
    }

    public void setAddress(String address) {
    
    
        this.address = address;
    }

    public String getSex() {
    
    
        return sex;
    }

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

    public Date getBirthday() {
    
    
        return birthday;
    }

    public void setBirthday(Date birthday) {
    
    
        this.birthday = birthday;
    }
}

同时在User Dao接口中提供查询所有方法findAll,在Account Dao接口中提供根据id查询user的方法findById,以便延时加载时调用。
这里说明因为用户可能对应许多账户,当我们查询用户时可能并不需要账户信息,而且如果我们每次查询用户时都立即查询用户的账户信息,并且账户信息有很多,势必对内存有很大的开销。所以当我们需要账户信息时再调用findById方法去查询用户对应的账户信息。

public interface IUserDao {
    
    
    /**
     * 查询所有操作,并携带账户信息
     * @return
     */
    List<User> findAll();

    /**
     * 根据id查询一个用户
     * @param uid
     */
    User findById(Integer uid);


}
public interface IAccountDao {
    
    
    /**
     * 查询所有账户
     * @return
     */
    List<Account> findAll();

    /**
     * 根据用户id查询账户
     * @param uid
     * @return
     */
    List<Account> findByUid(Integer uid);

}

然后配置userDao.xml,说明会在代码中给出。

<mapper namespace="com.cc.dao.IUserDao">
    <!--定义resultMap-->
    <!--因为在主配置文件中配置了domain包下的所有实体类别名,所以这里封装类型只需要写实体类名即可,不分大小写-->
    <resultMap id="userWithAccount" type="user">
        <!--封装user对象-->
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!--配置user队形中account集合的映射-->
        <!--定义一对多的关系映射,实现对account的封装,用collection标签
        ofType属性指定内容:要封装的实体对象类型
        select属性指定内容:查询用户的唯一标识
        column属性指定内容:用户根据id查询是所需要的参数
        -->
        <collection property="accounts" ofType="account" column="id" 
                  select="com.cc.dao.IAccountDao.findByUid">
        </collection>
    </resultMap>
    <!--查询所有-->
    <select id="findAll" resultMap="userWithAccount">
        select * from user
    </select>
    <!--根据id查询一个用户-->
    <select id="findById" parameterType="java.lang.Integer" resultType="user">
        select * from user where id=#{
    
    uid};
    </select>
</mapper>

当然我们还要在主配置文件中开启延时加载,默认情况下是立即加载。
lazyLoadingEnabled:是否启用延迟加载,mybatis默认为false,不启用延迟加载。lazyLoadingEnabled属性控制全局是否使用延迟加载,特殊关联关系也可以通过嵌套查询中fetchType属性单独配置(fetchType属性值lazy或者eager)。
也就是说我们可以不用在主配置文件中配置而在userDao.xml中配置,这里我们采用全局配置。

<!--配置参数-->
    <settings>
        <!--开启Mybatis支持延时加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"></setting>
    </settings>
    <!--配置domain包下所有实体类别名-->
    <typeAliases>
        <!--<typeAlias type="com.cc.domain.User" alias="user"></typeAlias>-->
        <package name="com.cc.domain"></package>
    </typeAliases>

然后我们就可以测试了

public class UserTest {
    
    
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession sqlSession;
    private IUserDao userDao;
    @Before//在测试方法执行之前执行
    public void init() throws IOException {
    
    
        //1.读取配置文件,生成字节输入流
         in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.生成SqlSessionFactory
         factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取SqlSession
         sqlSession = factory.openSession();
        //4.获取dao的代理对象
         userDao = sqlSession.getMapper(IUserDao.class);
    }
    @After//在测试方法执行之后执行
    public void destory() throws IOException {
    
    
        //提交事务
        sqlSession.commit();
        //关闭资源
        sqlSession.close();
        in.close();
    }

    /**
     * 测试查询所有账户
     */
    @Test
    public void TestFindAll() {
    
    
        //5.执行查询所有方法
        List<User> userList = userDao.findAll();
       for (User user : userList) {
    
    
           System.out.println(user);
            System.out.println(user.getAccounts());
        }

    }

}

先把遍历输出部分代码注释掉,测试可以看出我们只查询了用户信息。
在这里插入图片描述然后去掉注释,发现当我们需要输出用户账户时,他就会去查询用户的账户信息。
在这里插入图片描述

2.4 多对一及一对一查询

步骤其实和一对多差不多。
首先我们在account实体类中加入user成员变量表示一对一映射。

public class Account implements Serializable {
    
    
    private Integer id;
    private Integer uid;
    private Double money;
    //从表实体应该包含一个主表实体的对象引用
    private User user;

    public User getUser() {
    
    
        return user;
    }

    public void setUser(User user) {
    
    
        this.user = user;
    }

    public Integer getId() {
    
    
        return id;
    }

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

    public Integer getUid() {
    
    
        return uid;
    }

    public void setUid(Integer uid) {
    
    
        this.uid = uid;
    }

    public Double getMoney() {
    
    
        return money;
    }

    public void setMoney(Double money) {
    
    
        this.money = money;
    }

    @Override
    public String toString() {
    
    
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

Dao接口中需要的的方法在上面总结一对多查询时的图中已经给出。
然后配置accountDao.xml,这里是立即查询,在我们已经配置全局延时加载的情况下,我们需要配置fetchType=“eager”。

<mapper namespace="com.cc.dao.IAccountDao">
    <!--开启account支持二级缓存-->
    <cache/>
    <!--定义封装account和user的resultMap-->
    <resultMap id="accountAndUser" type="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--定义一对一的关系映射,实现对user的封装
        select属性指定内容:查询用户的唯一标识
        column属性指定内容:用户根据id查询是所需要的参数
        fetchType属性指定内容:lazy延时加载,eager立即加载。
        -->
        <association property="user" column="uid" javaType="user" select="com.cc.dao.IUserDao.findById" fetchType="eager"></association>
    </resultMap>
    <!--查询所有-->
    <select id="findAll" resultMap="accountAndUser">
        SELECT * from account
    </select>
    <!--根据用户id查询-->
    <select id="findByUid" parameterType="java.lang.Integer" resultType="account" useCache="true">
        select * from account where uid = #{
    
    uid}
    </select>
</mapper>

然后我们就可以测试。可以看出当查询账户时就立即查询了对应的用户信息。
在这里插入图片描述
注意:
在这里插入图片描述

3 使用注解多表查询

3.1 一对一

3.1.1 表关系

一对一关系中,表关系由任意一方维护,以人和身份证为例,一个人对应一个身份证,一个身份证对应一个人。本案例中由身份证表维护表关系

1589801843745

3.1.2 需求

查询所有的身份证,并且要将身份证对应的人也查询出来

3.1.3 实现步骤

第一步:建表

DROP TABLE IF EXISTS `person`;
CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL COMMENT '姓名',
  `age` int(11) NOT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='用户表';
insert  into `person`(`id`,`name`,`age`) values (1,'陈乔恩',39),(2,'钟汉良',44),(3,'林志颖',44),(4,'吴奇隆',48);
DROP TABLE IF EXISTS `card`;
CREATE TABLE `card` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `number` varchar(30) NOT NULL COMMENT '身份证号',
  `pid` int(11) NOT NULL COMMENT '所属用户',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='身份证表';
insert  into `card`(`id`,`number`,`pid`) values (1,'	110101199003079008',1),(2,'110101199003079331',2),(3,'	11010119900307299X',3),(4,'	110101199003070791',4);

第二步:创建实体类

Person 人类

package cn.oldlu.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    
    
    private Integer id;
    private String name;
    private Integer age;
}

Card 身份证类,因为需求是查询身份证,并且查询对应的人,所以应该是Card实体类中存放Perosn属性

package cn.oldlu.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Card {
    
    
    private Integer id;
    private String number;
    private Person person;
}

第三步:创建dao接口(核心)

思考 使用2条SQL语句,查询id=1的身份证信息以及对应的人怎么查

-- 第一步 查询id=1的身份证信息,找到对应的pid,值为1
SELECT * FROM card WHERE id = 1;
-- 第二步 根据pid的值查询对应的人
SELECT * FROM person WHERE id=1;

1589902183333

接下来通过编写两个Dao分别完成以上两个查询语句

1.Person实体类比较简单,所以先编写PersonDao中的代码,根据id查询详细信息

package cn.oldlu.dao;

import cn.oldlu.domain.Person;
import org.apache.ibatis.annotations.Select;

public interface PersonDao {
    
    
    /*#{id}对应的是形参Integer的值,名字可以任意*/
    @Select("select * from person where id = #{id}")
    Person findById(Integer id);
}

2.编写CardDao

package cn.oldlu.dao;

import cn.oldlu.domain.Card;
import cn.oldlu.domain.Person;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface CardDao {
    
    
    @Select("select * from card")
    @Results({
    
    
            @Result(column = "id", property = "id"), //将结果集中的id字段的值赋值给id属性
            @Result(column = "number", property = "number"),//将结果集中的number字段的值赋值给number属性
            @Result(
                    column = "pid", 
                    property = "person", 
                    javaType = Person.class, 
                    one = @One(select = "cn.oldlu.dao.PersonDao.findById")
            ),//首先结果集中的pid字段的值传给findeById方法,然后将查询结果封装成Person类的对象,最后赋值给person属性

    })
    List<Card> findAll();
}

第四步:编写测试方法

@Test
public void testFindAll()throws Exception{
    
    
    /*1.读取配置文件*/
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    /*2.解析配置文件*/
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    /*3.获取操作数据库的对象*/
    SqlSession sqlSession = sqlSessionFactory.openSession();
    /*4.获取代理对象*/
    CardDao cardDao = sqlSession.getMapper(CardDao.class);
    /*5.调用代理对象的方法完成查询*/
    List<Card> cards = cardDao.findAll();
    /*6.释放资源*/
    sqlSession.close();

    //测试查询结果
    System.out.println(cards);
}

3.2 一对多

3.2.1 表关系

一对多关系中,表关系由多的一方维护,以班级和学生为例,一个班级可以有多个学生,学生表应该维护表关系。

1589806452924

3.2.2 需求

查询所有班级信息,并且要将班级对应的学生信息也同时查询出来

3.2.3 实现步骤

第一步:建表

DROP TABLE IF EXISTS `classes`;
CREATE TABLE `classes` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) DEFAULT NULL COMMENT '班级名称',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='班级表';
INSERT  INTO `classes`(`id`,`name`) VALUES (1,'一班'),(2,'二班');

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(24) NOT NULL,
  `age` INT(11) NOT NULL,
  `cid` INT(11) NOT NULL COMMENT '所属班级',
  PRIMARY KEY (`id`),
  KEY `cid` (`cid`)
) ENGINE=INNODB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

INSERT  INTO `student`(`id`,`name`,`age`,`cid`) VALUES (8,'王力宏',23,1),(9,'张韶涵',22,1),(10,'张晋',24,1),(11,'罗晋',25,1),(12,'唐僧',12,2),(13,'孙悟空',22,2),(14,'猪八戒',21,2);

第二步:创建实体类

Student学生类,和上文提到的一样。有就不用创建了

package cn.oldlu.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    
    
    private Integer id;
    private String name;
    private Integer age;
}

Classes班级类,因为需求是查询班级,以及对应的学生,所以应该是班级类中定义List表示一堆学生

package cn.oldlu.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Classes {
    
    
    private Integer id;
    private String name;
    private List<Student> students;
}

第三步:创建dao接口(核心)

思考 使用2条SQL语句,查询id=1的班级信息以及对应的学生怎么查

-- 第一步 查询ID=1的班级信息
SELECT * FROM classes WHERE id = 1;
-- 第二步 查询对应的学生信息
SELECT * FROM student WHERE cid = 1;

1589902058933

接下来通过编写两个Dao分别完成以上两个查询语句

1.Student实体类比较简单,所以先编写StudentDao中的代码,根据cid查询学生信息

package cn.oldlu.dao;

import cn.oldlu.domain.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface StudentDao {
    
    
    /*#{id}对应的是形参Integer的值,名字可以任意*/
    @Select("SELECT * FROM student WHERE cid = #{cid};")
    List<Student> findByCid(Integer cid);
}

2.编写ClassesDao

package cn.oldlu.dao;

import cn.oldlu.domain.Classes;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface ClassesDao {
    
    
    @Select("SELECT * FROM classes")
    @Results({
    
    
            @Result(column = "id", property = "id"),//将结果集中的id字段的值赋值给id属性
            @Result(column = "name", property = "name"),//将结果集中的name字段的值赋值给name属性
            @Result(
                    column = "id",
                    property = "students",
                    javaType = List.class,
                    many = @Many(select = "cn.oldlu.dao.StudentDao.findByCid")
            )//首先结果集中的id字段的值传给findByCid方法,然后将查询结果封装成List类的对象,最后赋值给students属性

    })
    List<Classes> findAll();
}

第四步:编写测试方法

@Test
public void testFindAll()throws Exception{
    
    
    /*1.读取配置文件*/
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    /*2.解析配置文件*/
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    /*3.获取操作数据库的对象*/
    SqlSession sqlSession = sqlSessionFactory.openSession();
    /*4.获取代理对象*/
    ClassesDao classesDao = sqlSession.getMapper(ClassesDao.class);
    /*5.调用代理对象的方法完成查询*/
    List<Classes> classes = classesDao.findAll();
    /*6.释放资源*/
    sqlSession.close();

    //测试查询结果
    System.out.println(classes);
}

3.3 多对多

3.3.1 表关系

多对多关系中表关系由中间表维护,以学生和课程为例,一个学生对应多们课程,一个课程同时对应多个学生

1589814558313

3.3.2 需求

查询所有的学生,并且要将学生对应的课程也查询出来

3.3.3 实现步骤

第一步: 建表

DROP TABLE IF EXISTS `classes`;
CREATE TABLE `classes` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) DEFAULT NULL COMMENT '班级名称',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='班级表';
INSERT  INTO `classes`(`id`,`name`) VALUES (1,'一班'),(2,'二班');

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(24) NOT NULL,
  `age` INT(11) NOT NULL,
  `cid` INT(11) NOT NULL COMMENT '所属班级',
  PRIMARY KEY (`id`),
  KEY `cid` (`cid`)
) ENGINE=INNODB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

INSERT  INTO `student`(`id`,`name`,`age`,`cid`) VALUES (8,'王力宏',23,1),(9,'张韶涵',22,1),(10,'张晋',24,1),(11,'罗晋',25,1),(12,'唐僧',12,2),(13,'孙悟空',22,2),(14,'猪八戒',21,2);

DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(30) NOT NULL COMMENT '课程名',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='课程表';
INSERT  INTO `course`(`id`,`name`) VALUES (1,'语文'),(2,'数学');

DROP TABLE IF EXISTS `stu_cr`;
CREATE TABLE `stu_cr` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `sid` INT(11) NOT NULL,
  `cid` INT(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
INSERT  INTO `stu_cr`(`id`,`sid`,`cid`) VALUES (1,8,1),(2,8,2),(3,9,1),(4,10,2),(5,13,2),(6,13,1),(7,14,1),(8,11,2),(9,12,1),(10,12,2);

第二步:创建实体类

Course 课程类

package cn.oldlu.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Course {
    
    
    /**课程ID*/
    private Integer id;
    /**课程名*/
    private String name;
}

Student 学生类,学生类之前已经定义过了,只需要添加新的course属性即可。因为需求是查询学生信息,以及对应的课程,所以应该在学生类中定义List,表示一堆课程

package cn.oldlu.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    
    
    private Integer id;
    private String name;
    private Integer age;
    private List<Course> courses;
}

第三步:创建Dao接口(核心)

思考,使用2条SQL如何查询id=8的学生的信息以及对应的课程信息

Sql

-- 第一步 查询id=8的学生信息
SELECT * FROM student WHERE id = 8;
-- 第二步 根据学生的id查询对应的课程信息
SELECT * FROM course c  INNER JOIN stu_cr sc ON c.id = sc.cid AND sc.sid=8;

1589901385014

接下来通过编写两个Dao分别完成以上两个查询语句

1.Course实体类比较简单,所以先编写CourseDao中的代码,根据sid查询课程信息

package cn.oldlu.dao;

import cn.oldlu.domain.Course;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface CourseDao {
    
    
    /*#{sid}对应的是形参Integer的值,名字可以任意*/
    @Select("SELECT * FROM course c  INNER JOIN stu_cr sc ON c.id = sc.cid AND sc.sid=#{sid};")
    List<Course> findBySid(Integer sid);
}

2.编写StudentDao

package cn.oldlu.dao;
import cn.oldlu.domain.Student;
import org.apache.ibatis.annotations.*;
import java.util.List;

public interface StudentDao {
    
    
    @Select("select * from student")
    @Results({
    
    
            @Result(column = "id", property = "id"),//将结果集中的id字段的值赋值给id属性
            @Result(column = "name", property = "name"),//将结果集中的name字段的值赋值给name属性
            @Result(column = "age", property = "age"),//将结果集中的age字段的值赋值age属性
            @Result(
                    column = "id",
                    property = "courses",
                    javaType = List.class,
                    many = @Many(select = "cn.oldlu.dao.CourseDao.findBySid")
            )//首先结果集中的id字段的值传给findBySid方法,然后将查询结果封装成List类的对象,最后赋值给courses属性
    })
    List<Student> findAll();
}

第四步:编写测试类

@Test
public void testFindAll()throws Exception{
    
    
    /*1.读取配置文件*/
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    /*2.解析配置文件*/
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    /*3.获取操作数据库的对象*/
    SqlSession sqlSession = sqlSessionFactory.openSession();
    /*4.获取代理对象*/
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    /*5.调用代理对象的方法完成查询*/
    List<Student> students = studentDao.findAll();
    /*6.释放资源*/
    sqlSession.close();

    //测试查询结果
    System.out.println(students);
}

猜你喜欢

转载自blog.csdn.net/ZGL_cyy/article/details/107494362