day08-MyBatis的关联映射02
MyBatis的关联映射02
3.一对多
3.1基本介绍
多对一关系也是一个基本的映射关系,多对一,也可以理解为一对多。例如:
User--Pet:一个用户可以有多只宠物
Dep--Emp:一个部门有多个员工
双向的多对一关系:通过User可以查询到对应的所有Pet,反之,通过Pet也可以级联查询到对应的User信息。
多对多的关系就是在多对一的关系上拓展
3.2案例实现
映射方式:
方式1:通过配置映射文件实现多对一
方式2:通过注解的方式实现多对一
需求说明:实现级联查询,通过user的user_id可以查询到User信息和关联的所有pet信息,反之,通过pet的pet_id也可以查询到Pet信息和user的信息
先创建user表和pet表:
-- 创建user表 CREATE TABLE `user`( `id` INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(32) NOT NULL DEFAULT '' )CHARSET=utf8 DESC `user`; -- 创建pet表 CREATE TABLE `pet`( `id` INT PRIMARY KEY AUTO_INCREMENT, `nickname` VARCHAR(32) NOT NULL DEFAULT '', `user_id` INT, FOREIGN KEY (user_id) REFERENCES `user`(id) )CHARSET=utf8

3.2.1方式一:配置方式
(1)User和Pet实体类
package com.li.entity; /** * @author 李 * @version 1.0 */ public class User { private Integer id; private String name; //因为一个User可以养多个宠物,mybatis使用集合体现这个关系 private List<Pet> pets; //setter、getter方法省略 //双向映射不要使用toString方法,否则会造成栈溢出错误 }
package com.li.entity; /** * @author 李 * @version 1.0 */ public class Pet { private Integer id; private String nickname; //一个pet对应一个user对象 private User user; //setter、getter方法省略 //双向映射不要使用toString方法,否则会造成栈溢出错误 }
(2)UserMapper接口和PetMapper接口
public interface UserMapper { //通过id获取User对象 public User getUserById(Integer id); }
public interface PetMapper { //通过user的id获取pet对象,可能有多个因此使用集合接收 public List<Pet> getPetByUserId(Integer userId); }
(3)UserMapper.xml,思路:
1)先通过user_id查询得到user信息
2)再根据user_id,查询对应的pet信息,并映射到user-List< Per> pets
多对多的映射思路和一对一的实现类似,不同的使用使用resultMap映射属性时使用的是collecting标签。
<mapper namespace="com.li.mapper.UserMapper"> <!--通过id获取User对象 public User getUserById(Integer id);--> <select id="getUserById" parameterType="Integer" resultMap="resultUserMap"> SELECT * FROM `user` WHERE id = #{id}; </select> <!--User的属性映射--> <resultMap id="resultUserMap" type="User"> <id property="id" column="id"/> <result property="name" column="name"/> <!--1.因为pets属性是一个集合,因此要使用collection标签 2.column="id"的id是SELECT * FROM `user` WHERE id=#{id} 返回的字段 3.ofType="Pet"指定返回的集合存放的数据类型--> <collection property="pets" column="id" ofType="Pet" select="com.li.mapper.PetMapper.getPetByUserId"/> </resultMap> </mapper>
(4)PetMapper.xml,思路和前面大体相同
<mapper namespace="com.li.mapper.PetMapper"> <!--通过user的id获取pet对象,可能有多个因此使用集合接收 public List<Pet> getPetByUserId(Integer userId);--> <select id="getPetByUserId" parameterType="Integer" resultMap="resultPetMap"> SELECT * FROM `pet` WHERE user_id =#{userId}; </select> <resultMap id="resultPetMap" type="Pet"> <id property="id" column="id"/> <result property="nickname" column="nickname"/> <association property="user" column="user_id" select="com.li.mapper.UserMapper.getUserById"/> </resultMap> </mapper>
(5)测试getUserById()方法,通过UserId查找user对象和联系的pet信息
@Test public void getUserById() { User user = userMapper.getUserById(2); System.out.println("user信息=" + user.getId() + "-" + user.getName()); for (Pet pet : user.getPets()) { System.out.println("宠物信息=" + pet.getId() + "-" + pet.getNickname()); } if (sqlSession != null) { sqlSession.close(); } }
测试结果:

(6)测试getPetByUserId()方法,通过user的id获取pet对象
@Test public void getPetByUserId() { List<Pet> pets = petMapper.getPetByUserId(1); for (Pet pet : pets) { System.out.println("UserId=" + pet.getUser().getId() + "-PetId=" + pet.getId() + "-PetNickName=" + pet.getNickname()); } if (sqlSession != null) { sqlSession.close(); } }
测试结果:

resultMap可以复用,如果有其他方法是返回的和resultMap一样的类型,可以在实现该方法时引用该resultMap。
比如PetMapper接口中新声明了一个方法:
//通过pet的id获取Pet对象,同时查询到pet对象关联的user对象 public Pet getPetById(Integer id);
PerMapper.xml文件:
<!--这里可以直接复用之前的resultPetMap--> <select id="getPetById" parameterType="Integer" resultMap="resultPetMap"> SELECT * FROM `pet` where id =#{id}; </select>
3.2.2方式二:注解方式
需求说明:通过注解的方式,实现双向的级联查询。
在实际开发中推荐使用配置的方式来做
(1)User和Pet实体类不变
(2)直接在接口中,通过注解实现级联查询
UserMapperAnnotation.java
package com.li.mapper; import com.li.entity.User; import org.apache.ibatis.annotations.*; /** * @author 李 * @version 1.0 * 以注解的方式来实现多对一 */ public interface UserMapperAnnotation { //通过id获取User对象 @Select(value = "SELECT * FROM `user` WHERE id = #{id}") @Results({ @Result(id = true, property = "id", column = "id"), @Result(property = "name", column = "name"), //这里对应返回List类型属性pets,使用注解的many属性 @Result(property = "pets", column = "id", many = @Many(select = "com.li.mapper.PetMapperAnnotation.getPetByUserId")) }) public User getUserById(Integer id); }
PetMapperAnnotation.java
package com.li.mapper; import com.li.entity.Pet; import org.apache.ibatis.annotations.*; import java.util.List; /** * @author 李 * @version 1.0 */ public interface PetMapperAnnotation { //通过user的id获取pet对象 @Select(value = "SELECT * FROM `pet` WHERE user_id =#{userId}") //配置了id之后就可以复用PetResuleMap @Results(id = "PetResuleMap", value = { @Result(id = true, property = "id", column = "id"), @Result(property = "nickname", column = "nickname"), @Result(property = "user", column = "user_id", one = @One(select = "com.li.mapper.UserMapperAnnotation.getUserById")) }) public List<Pet> getPetByUserId(Integer userId); //通过pet的id获取pet信息 @Select(value = " SELECT * FROM `pet` where id =#{id}") @ResultMap("PetResuleMap")//复用上面的PetResuleMap public Pet getPetById(Integer id); }
3.3练习
自己设计dept(部门)和emp(雇员)表,它们是一对多的关系。
- 通过查询dept,可以级联查询到所有的emp信息
- 通过查询emp,也可以级联查询到对应的dept信息
- 拓展思考:多对多关系
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?