目录
1. 多表关系回顾
2. 一对一查询
2.1 一对一多表查询方式一:基于标签进行手动结果映射封装
2.2 一对一多表查询方式二 - 通过标签来封装属性中所关联的对象
3. 一对多查询
4. 多对多查询
1. 多表关系回顾
- 在项目开发当中一对一关系的表不常见,因为一对一关系的两张表通常会合并为一张表。
2. 一对一查询
一张表对应一个实体类,一个实体类对应一个Mapper接口。
例如:查询菜品,同时查询出该菜品所属的分类。
分析:查询菜品是主查询,因此将结果封装到菜品当中,直接在菜品类的属性当中声明一个菜品分类的对象。
Mybatis多表查询的难点是难在于怎样进行查询数据的封装!
Dish
package com.gch.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 菜品表
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dish {
/** 主键ID */
private Integer id;
/** 菜品名称 */
private String name;
/** 菜品分类ID */
private Integer categoryId;
/** 菜品图片 */
private String image;
/** 描述信息 */
private String description;
/** 状态:0停售,1起售 */
private Short status;
/** 创建时间 */
private LocalDateTime createTime;
/** 更新时间 */
private LocalDateTime updateTime;
/** 记录当前菜品所属的分类信息 */
private Category category;
}
Category
package com.gch.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 分类表
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Category {
/** 主键ID */
private Integer id;
/** 分类名称 */
private String name;
/** 类型 1-菜品分类 2-套餐分类*/
private Short type;
/** 顺序 */
private Integer sort;
/** 状态 0-禁用 1-启用 */
private Short status;
/** 创建时间 */
private LocalDateTime createTime;
/** 更新时间 */
private LocalDateTime updateTime;
}
DishMapper
package com.gch.mapper;
import com.gch.pojo.Dish;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface DishMapper {
/**
* 查询菜品,同时查询出该菜品所属的分类
* @return 将查询出来的多条结果封装到List集合当中
*/
public List<Dish> findAllDishWithCategory();
}
DishMapper.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="com.gch.mapper.DishMapper">
<!--查询菜品,同时查询出该菜品所属的分类-->
<select id="findAllDishWithCategory" resultType="com.gch.pojo.Dish">
select dish.*,
c.id,
c.name as 分类名称,
c.type 类型,
c.sort 顺序,
c.status 状态,
c.create_time 创建时间,
c.update_time 更新时间
from dish
left join category c on dish.category_id = c.id
</select>
</mapper>
注意:记得在配置文件当中来更新数据库的连接
测试类:
@Autowired
private DishMapper dishMapper;
@Test
public void testFindAllDishWithCategory() {
List<Dish> dishList = dishMapper.findAllDishWithCategory();
dishList.forEach(s ->{
System.out.println(s);
});
}
运行测试方法,看控制台输出:
可以看到catrgory为null,说明查询出来的菜品所属的分类信息没有被正确封装!
为什么category为null呢?
- 原因就在于最后是把查询出来的结果封装到Dish对象当中,但是Dish对象当中的属性名就是category,与数据库当中Category表中的字段名完全不一致,因此封装不上。
2.1 一对一多表查询方式一:基于标签<resultMap>进行手动结果映射封装
- 既然默认的规则{实体类类名与表中字段名相同}不能给我们自动封装上,那我们就需要手动封装!
- 这里要用到一个标签 - <resultMap>,Map是映射的意思,resultMap - 结果映射!
<?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="com.gch.mapper.DishMapper">
<!--定义resultMap,手动封装 type属性代表单条记录所封装的类型-->
<resultMap id="dishMap" type="com.gch.pojo.Dish">
<!--column-字段名 property-属性名-->
<!--表中字段名(如果下面sql语句有给字段起别名,就用别名)与实体类当中的属性名保持一致-->
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="category_id" property="categoryId"></result>
<result column="image" property="image"></result>
<result column="description" property="description"></result>
<result column="status" property="status"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
<result column="主键ID" property="category.id"></result>
<result column="分类名称" property="category.name"></result>
<result column="类型" property="category.type"></result>
<result column="顺序" property="category.sort"></result>
<result column="状态" property="category.status"></result>
<result column="创建时间" property="category.createTime"></result>
<result column="更新时间" property="category.updateTime"></result>
</resultMap>
<!--查询菜品,同时查询出该菜品所属的分类-->
<select id="findAllDishWithCategory" resultMap="dishMap">
select dish.*,
c.id as 主键ID,
c.name as 分类名称,
c.type as 类型,
c.sort as 顺序,
c.status as 状态,
c.create_time as 创建时间,
c.update_time as 更新时间
from dish
left join category c on dish.category_id = c.id
</select>
</mapper>
再次运行测试方法,看控制台查询结果,已经成功封装!
注意:
- 由于上述字段符合规则,因此也可以删掉名,但是为了可读性,因此我们没有删除。
- 还有下面爆红的字段,这是IDEA的误报。
2.2 一对一多表查询方式二 - 通过<association>标签来封装属性中所关联的对象
<?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="com.gch.mapper.DishMapper">
<!-- 方式一 -->
<!--定义resultMap,手动封装 type属性代表单条记录所封装的类型-->
<resultMap id="dishMap1" type="com.gch.pojo.Dish">
<!--column-字段名 property-属性名-->
<!--表中字段名(如果下面sql语句有给字段起别名,就用别名)与实体类当中的属性名保持一致-->
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="category_id" property="categoryId"></result>
<result column="image" property="image"></result>
<result column="description" property="description"></result>
<result column="status" property="status"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
<result column="主键ID" property="category.id"></result>
<result column="分类名称" property="category.name"></result>
<result column="类型" property="category.type"></result>
<result column="顺序" property="category.sort"></result>
<result column="状态" property="category.status"></result>
<result column="创建时间" property="category.createTime"></result>
<result column="更新时间" property="category.updateTime"></result>
</resultMap>
<!-- 方式二-->
<resultMap id="dishMap2" type="com.gch.pojo.Dish">
<!-- column-字段名 property-属性名-->
<!-- 表中字段名(如果下面SQL查询语句有给字段起别名,就用别名)与实体类当中的属性名保持一致-->
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="category_id" property="categoryId"></result>
<result column="image" property="image"></result>
<result column="description" property="description"></result>
<result column="status" property="status"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
<!-- assocation标签封装的是一个对象类型 property指定要封装对象的属性名
javaType属性来指定property属性是什么类型的,也就是要填写该属性的全类名-->
<association property="category" javaType="com.gch.pojo.Category">
<id column="主键ID" property="id"></id>
<result column="分类名称" property="name"></result>
<result column="类型" property="type"></result>
<result column="顺序" property="sort"></result>
<result column="状态" property="status"></result>
<result column="创建时间" property="createTime"></result>
<result column="更新时间" property="updateTime"></result>
</association>
</resultMap>
<!--查询菜品,同时查询出该菜品所属的分类-->
<select id="findAllDishWithCategory" resultMap="dishMap2">
select dish.*,
c.id as 主键ID,
c.name as 分类名称,
c.type as 类型,
c.sort as 顺序,
c.status as 状态,
c.create_time as 创建时间,
c.update_time as 更新时间
from dish
left join category c on dish.category_id = c.id
</select>
</mapper>
运行测试方法,验证结果是否正确:
如果在写属性的全类名时,想省略前面的包名不写,那么就可以开启MyBatis的实体类别名配置:
#MyBatis配置(关键字camel、log-impl)
mybatis:
configuration:
#开启MyBatis的日志输出{配置MyBatis的日志,指定输出到控制台}
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#开启数据库表字段 到 实体类属性的驼峰映射 a_column ---> aColumn
map-underscore-to-camel-case: true
#开启MyBatis的实体类别名配置,相当于如果要写该包下类的全类名,直接写类名即可,前面的包名可以省略
#别名就是类名的简写,并且首字母不区分大小写
type-aliases-package: com.gch.pojo
3. 一对多查询
SQL语句:
-- 查询所有分类,同时查询出该分类下的菜品数据
select category.*,
dish.id as 主键ID,
dish.name as 菜品名称,
dish.category_id as 菜品分类ID,
dish.price as 菜品价格,
dish.image as 菜品图片,
dish.description as 描述信息,
dish.status as 状态,
dish.create_time as 创建时间,
dish.update_time as 更新时间
from category
left outer join dish on category.id = dish.category_id;
Category
package com.gch.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 分类表
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Category {
/** 主键ID */
private Integer id;
/** 分类名称 */
private String name;
/** 类型 1-菜品分类 2-套餐分类*/
private Short type;
/** 顺序 */
private Integer sort;
/** 状态 0-禁用 1-启用 */
private Short status;
/** 创建时间 */
private LocalDateTime createTime;
/** 更新时间 */
private LocalDateTime updateTime;
/** 一个分类对应多个菜品,记录该分类下的菜品集合 */
private List<Dish> dishList = new ArrayList<>();
}
细节:如果定义集合,一般需要初始化一下,防止拿到的结果为null。
Dish
package com.gch.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 菜品表
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dish {
/** 主键ID */
private Integer id;
/** 菜品名称 */
private String name;
/** 菜品分类ID */
private Integer categoryId;
/** 菜品图片 */
private String image;
/** 描述信息 */
private String description;
/** 状态:0停售,1起售 */
private Short status;
/** 创建时间 */
private LocalDateTime createTime;
/** 更新时间 */
private LocalDateTime updateTime;
/** 记录当前菜品所属的分类信息 */
private Category category;
}
CategoryMapper
package com.gch.mapper;
import com.gch.pojo.Category;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CategoryMapper {
/**
* 查询所有分类,同时查询出该分类下的菜品数据
* @return
*/
public List<Category> findAllCategoryWithDish();
}
CategoryMapper.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="com.gch.mapper.CategoryMapper">
<!-- 定义resultMap,手动封装,type属性代表单条记录所封装的类型-->
<resultMap id="categoryMap" type="category">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="type" property="type"></result>
<result column="sort" property="sort"></result>
<result column="status" property="status"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
<!-- 封装单个对象用<association>标签,封装集合用<collection>标签-->
<!-- property指的是集合的属性名 ofType属性指的是集合当中单条记录所封装的类型-->
<collection property="dishList" ofType="Dish">
<id column="主键ID" property="id"></id>
<result column="菜品名称" property="name"></result>
<result column="菜品分类ID" property="categoryId"></result>
<result column="菜品图片" property="image"></result>
<result column="描述信息" property="description"></result>
<result column="状态" property="status"></result>
<result column="创建时间" property="createTime"></result>
<result column="更新时间" property="updateTime"></result>
</collection>
</resultMap>
<!-- 查询所有分类,同时查询出该分类下的菜品数据-->
<select id="findAllCategoryWithDish" resultMap="categoryMap">
select category.*,
dish.id as 主键ID,
dish.name as 菜品名称,
dish.category_id as 菜品分类ID,
dish.image as 菜品图片,
dish.description as 描述信息,
dish.status as 状态,
dish.create_time as 创建时间,
dish.update_time as 更新时间
from category
left outer join dish on category.id = dish.category_id;
</select>
</mapper>
测试方法:
@Autowired
private CategoryMapper categoryMapper;
@Test
public void testFindAllCategoryWithDish(){
List<Category> categoryList = categoryMapper.findAllCategoryWithDish();
categoryList.forEach(s ->{
System.out.println(s);
});
}
运行测试方法,查看控制台输出结果:
4. 多对多查询
学生表、课程表、学生课程关系表。
注意:学生课程这张中间关系表它不是业务表!
-- ======================================多对多=============================
create table tb_student(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
no varchar(10) comment '学号'
) comment '学生表';
insert into tb_student(name, no) values ('黛绮丝', '2000100101'),('谢逊', '2000100102'),('殷天正', '2000100103'),('韦一笑', '2000100104');
create table tb_course(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '课程名称'
) comment '课程表';
insert into tb_course (name) values ('Java'), ('PHP'), ('MySQL') , ('Hadoop');
create table tb_student_course(
id int auto_increment comment '主键' primary key,
student_id int not null comment '学生ID',
course_id int not null comment '课程ID',
constraint fk_courseid foreign key (course_id) references tb_course (id),
constraint fk_studentid foreign key (student_id) references tb_student (id)
)comment '学生课程中间表';
insert into tb_student_course(student_id, course_id) values (1,1),(1,2),(1,3),(2,2),(2,3),(3,4);
SQL
-- 查询所有学生,同时查询出该学生选修的所有课程
select tb_student.*, tc.id as 主键ID, tc.name as 课程名称
from tb_student
left outer join tb_student_course as tsc on tb_student.id = tsc.student_id
left outer join tb_course as tc on tsc.course_id = tc.id;
细节:用到了两次左连接!
Student
package com.gch.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
/**
学生表
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
/** 主键ID */
private Integer id;
/** 姓名 */
private String name;
/** 学号 */
private String no;
/** 定义集合,记录该学生选修的所有课程 */
public List<Course> courseList = new ArrayList<>();
}
Course
package com.gch.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
课程表
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Course {
/** 主键ID */
private Integer id;
/** 课程名称 */
private String name;
}
StudentMapper
package com.gch.mapper;
import com.gch.pojo.Student;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface StudentMapper {
/**
* 查询所有学生,同时查询出该学生选修的所有课程
* @return
*/
public List<Student> findAllStudentWithCourse();
}
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">
<mapper namespace="com.gch.mapper.StudentMapper">
<!-- 定义resultMap,手动封装,type属性代表的单条记录所封装的类型-->
<resultMap id="studentMap" type="student">
<id property="id" column="id"></id>
<result column="name" property="name"></result>
<result column="no" property="no"></result>
<!-- 封装集合用<collection>标签,property指的是集合的名称,ofType属性指的是集合当中单条记录所封装的类型-->
<collection property="courseList" ofType="Course">
<id column="主键ID" property="id"></id>
<result column="课程名称" property="name"></result>
</collection>
</resultMap>
<!-- 查询所有学生,同时查询出该学生选修的所有课程-->
<select id="findAllStudentWithCourse" resultMap="studentMap">
select tb_student.*, tc.id as 主键ID, tc.name as 课程名称
from tb_student
left outer join tb_student_course as tsc on tb_student.id = tsc.student_id
left outer join tb_course as tc on tsc.course_id = tc.id;
</select>
</mapper>
编写测试方法,运行测试方法:文章来源:https://www.toymoban.com/news/detail-691266.html
@Autowired
private StudentMapper studentMapper;
@Test
public void testFindAllStudentWithCourse(){
List<Student> studentList = studentMapper.findAllStudentWithCourse();
studentList.forEach(s ->{
System.out.println(s);
});
}
文章来源地址https://www.toymoban.com/news/detail-691266.html
到了这里,关于MyBatis多表查询的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!