1. Mybatis多表查询概念
在学习多表查询的之前,我们要先明确多表的关系都有哪些,如何划分。
1.1 多表关系的划分
一对一
一对一的关系是两张表之间 A表的一条数据只能对应B表的一条数据。比如 订单表和用户表 一个订单只能属于一个用户,所以是一对一的关系。
一对多
一对多的关系是两张表之间 A表的一条数据可以对应B表的多条数据。比如用户表和订单表, 一个用户可以拥有多个订单,所以是一对多的关系
多对多
多对多的关系是两张表之前 A表的一条数据可以对应B表的多条数据,反之,B表的数据也可 以对应A表的多条数据,但这只是在业务理解层面的意义。实际上两张表如果是多对多的关 系,那么这两张表不能直接关联,需要一张中间表来联系起来。
划分表关系的技巧
-
先从业务层面理解2张表的关系,然后看需求要查询的主表是哪张表
-
比如订单表和用户表,如果从订单的表的角度来看,那么就是一对一,如果从用户的角度来看,那么就是一对多
-
看需求:如果需求是查询订单表以及关联查询该订单的所属用户,那么就是一对一,如果是查询用户以及关联查询该用户的订单,那么就是一对多
-
在写之前一定要明确表关系!不然怎么写都是错的。
-
多表关系大多数都是通过id字段来关联的
-
其实多对多的需求本质上就是双向的一对多
1.2表关系划分的练习
- 文章表和评论表
从文章表的角度来看 是1对多
从评论表的角度来看 是1对1
- 学生表和班级表
从学生表的角度来看 是1对1
从班级表点的角度来看 是1对多
- 用户表和角色表
从用户表的角度来看 是1对多
从角色表的角度来看 是1对多
表的关系是 多对多
- 用户表和身份证表
从用户表的角度来看 是1对1
从身份证表的角度来看 是1对1
表的关系是1对1
- 桌子表和椅子表
从桌子的角度来看 1对多
从椅子的角度来看 1对1
2. Mybatis多表查询
Mybatis多表查询,只要编写好Sql语句,设置好查询结果的映射就非常简单了
2.1 一对一查询
已知有用户表和订单表 表结构如下
需求:
查询出订单信息,并且查询出每个订单信息所属的用户信息
分析表关系
从订单表角度来看,那就是一对一
订单表的uid字段 代表 所属用户的id 和用户表的id关联
2.1.1 查询的sql语句
select * from orders o ,user u where o.uid = u.id
2.1.2 创建实体类
在订单类中声明一个用户对象
public class Order {
private int id;
private Date ordertime;
private double total;
//代表当前订单从属于哪一个客户
private User user;
//省略set get。。。。
}
public class User {
private int id;
private String username;
private String password;
private String birthday;
//省略set
2.1.3 创建IOrderMapper接口
public interface OrderMapper {
List<Order> findAll();
}
2.1.4 创建OrderMapper.xml文件
方式一
<mapper namespace="com.demo.mapper.OrderMapper">
<resultMap id="orderMap" type="com.demo.bean.Orders">
<result column="username" property="user.username"></result>
<result column="password" property="user.password"></result>
<result column="birthday" property="user.birthday"></result>
</resultMap>
<select id="findAll" resultMap="orderMap">
select * from orders o,user u where o.uid=u.id
</select>
</mapper>
方式二
<resultMap id="orderMap" type="com.demo.bean.Orders">
<result property="id" column="id"></result>
<result property="ordertime" column="ordertime"></result>
<result property="total" column="total"></result>
<result property="uid" column="uid"></result>
<association property="user" javaType="com.demo.bean.User">
<result property="id" column="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
resutMap知识点:
作用:
-
当表中的列名和类中的属性名 不一致的时候 可以手动的修改映射
-
做多表查询的时候 给表中没有的字段 但是类中有的属性赋值
一对一:
2种方式映射
-
<resultMap> + <association>
-
直接用<resultMap>种的<result>
-
强烈推荐使用第一种
2.1.5 测试结果
编写单元测试 通过mapper调用方法 查看结果
//查询全部订单信息 包含用户
@Test
public void findAllOrders(){
List<Orders> ordersList = mapper.findAllOrder();
//遍历结果
for (Orders orders : ordersList) {
System.out.println(orders);
}
}
2.2 一对多查询
同样还是上面的订单表和用户表
需求:
查询每个用户以及该用户关联的订单列表
分析表关系
从用户表角度来看,那就是一对多
用户表的主键id和订单表的uid进行关联
2.2.1 查询的Sql语句
select *,o.id oid from user u left join orders o on u.id=o.uid;
一对多的查询 其实可以和一对一的查询同样的sql 但是不建议。建议使用左连接来查询。
2.2.2 修改实体类
在User实体类中声明一个代表订单的集合
public class User {
private Integer id;
private String username;
private String password;
private String birthday;
private List<Orders> ordersList;
}
2.2.3 创建IUserMapper接口
public interface IUserMapper {
List<User> findAll();
}
2.2.4 创建UserMapper.xml文件
<mapper namespace="com.demo.mapper.IUserMapper">
<resultMap id="userMap" type="com.zrrd.bean.User">
<result column="id" property="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<collection property="ordersList" ofType="com.demo.bean.Orders">
<result column="uid" property="uid"></result>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
select * from user u left join orders o on u.id=o.uid
</select>
</mapper>
2.2.5 测试结果
编写单元测试 通过mapper调用方法 查看结果
//查询全部订单信息 包含用户
@Test
public void findAllOrders(){
List<User> userList = mapper.findAllUser();
for (User user : userList) {
System.out.println("当前用户:"+user.getUsername());
System.out.println("关联订单:"+user.getOrdersList());
System.out.println("================================");
}
}
小总结:
1对1和1对多的写法区别:
-
一对一用<resultMap> + <association> 用javaType
-
一对多用<resultMap> + <collection> 用ofType
2.3 多对多查询
完成学生表和课程表的需求
学生表(id name age sex )
课程表(id name hour)
多对多表之间不能有直接关联 要创建中间表
中间表(id sid cid)
需求:
查询每个学生的信息以及学生学习了哪些课程
查询每个课程的信息以及课程被哪些学生学习了
表关系分析:
其实多对多直接可以拆分成一对多
从学生表的角度查询 一对多
从课程表的角度查询 一对多
2.3.1 查询的sql语句
select * from student s left join stu_cour sc on sc.sid = s.id inner join course c on sc.cid = c.id
参照一对多的写法 完成功能
步骤:
-
创建实体类和接口
-
编写核心配置文件MapperConfig
-
分别创建Mapper映射文件并编写返回的resultMap
-
测试结果
实操:
-
创建实体类
public class Student {
private Integer id; private String name; private Integer age; private String sex; //关联的课程集合 private List<Course> courseList; 省略 get和set方法
}
public class Course {
private Integer id;
private String name;
private Integer hour;
//关联的学生的集合
private List<Student> studentList;
省略get和set方法
}
创建接口
public interface StudentMapper {
//查询全部学生 并且查询出学生关联的课程集合
List<Student> findAllStudent();
}
public interface CourseMapper {
//查询全部学生 并且查询出学生关联的课程集合
List<Course> findAllCouerse();
}
关联查询情况:
-
编写核心配置文件MapperConfig
<?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>
</configuration><!--加载其他的配置文件--> <properties resource="jdbcConfig.properties"></properties> <!--给实体类起别名--> <typeAliases> <typeAlias type="com.demo.bean.Student" alias="s"></typeAlias> <typeAlias type="com.demo.bean.Course" alias="c"></typeAlias> </typeAliases> <!--必须要放这里不然报错--> <plugins> <!-- 注意:分页助手的插件 配置在通用馆mapper之前 --> <plugin interceptor="com.github.pagehelper.PageHelper"> <!-- 指定方言 --> <property name="dialect" value="mysql"/> </plugin> </plugins> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="StudentMapper.xml"/> <mapper resource="CourseMapper.xml"/> </mappers>
-
分别创建Mapper映射文件并编写返回的resultMap
<mapper namespace="com.demo.mapper.StudentMapper">
</mapper> <mapper namespace="com.demo.mapper.CourseMapper"><resultMap id="studenrtMap" type="s"> <result column="id" property="id"></result> <result column="name" property="name"></result> <result column="age" property="age"></result> <result column="sex" property="sex"></result> <collection property="courseList" ofType="c"> <result column="cid" property="id"></result> <result column="cname" property="name"></result> <result column="chour" property="hour"></result> </collection> </resultMap> <!--查询全部学生 需要起别名,因为两个表id都是这个属性名,name也是相同属性名,容易识别不出来--> <select id="findAllStudent" resultMap="studenrtMap"> select s.*,c.id cid,c.name cname,c.hour chour from student s left join stu_cour sc on s.id = sc.sid inner join course c on c.id = sc.cid; </select>
</mapper><resultMap id="courseMap" type="c"> <result column="id" property="id"></result> <result column="name" property="name"></result> <result column="hour" property="hour"></result> <collection property="studentList" ofType="s"> <result column="sid" property="id"></result> <result column="sname" property="name"></result> <result column="sage" property="age"></result> <result column="ssex" property="sex"></result> </collection> </resultMap> <select id="findAllCouerse" resultMap="courseMap"> select c.*, s.id sid, s.name sname, s.age sage, s.sex ssex from course c left join stu_cour sc on c.id = sc.cid inner join student s on s.id = sc.sid; </select>
-
测试结果
public class StudentTest {
SqlSession sqlSession;
StudentMapper mapper;
@Before
public void init() throws IOException {
//加载核心配置文件
InputStream stream = Resources.getResourceAsStream("MapperConfig.xml");
//创建Sqlseesion工厂对象
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(stream);
//通过工厂对象 获取Sqlseesion对象
sqlSession = ssf.openSession();
//获取Mapper对象
mapper = sqlSession.getMapper(StudentMapper.class);
}//查询全部订单信息 包含用户 @Test public void findAllOrders(){ List<Student> students = mapper.findAllStudent(); for (Student student : students) { System.out.println("学生姓名:"+student.getName()); System.out.print("学习课程:"); List<Course> courseList = student.getCourseList(); for (Course course : courseList) { System.out.print(course.getName()+"\t"); } System.out.println(); } } @After public void close(){ //提交事务 sqlSession.commit(); //关闭资源 sqlSession.close(); }
}
public class CourseTest {
SqlSession sqlSession;
CourseMapper mapper;
@Before
public void init() throws IOException {
//加载核心配置文件
InputStream stream = Resources.getResourceAsStream("MapperConfig.xml");
//创建Sqlseesion工厂对象
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(stream);
//通过工厂对象 获取Sqlseesion对象
sqlSession = ssf.openSession();
//获取Mapper对象
mapper = sqlSession.getMapper(CourseMapper.class);
}//查询全部订单信息 包含用户 @Test public void findAllOrders(){ List<Course> couerseList = mapper.findAllCouerse(); for (Course course : couerseList) { System.out.println("课程名称:"+course.getName()); System.out.println("课程学生:"+course.getStudentList()); } } @After public void close(){ //提交事务 sqlSession.commit(); //关闭资源 sqlSession.close(); }
}