拓冰建站拓冰建站
首页 / 资讯中心 / 正文

MyBatis多表查询与延迟加载实战解析

1. MyBatis多表查询实战指南作为一名使用MyBatis多年的开发者我深知在实际项目中单表查询往往无法满足复杂业务需求。当系统需要处理关联表查询时掌握MyBatis的多表映射技巧就显得尤为重要。本文将深入探讨MyBatis中一对一、一对多和多对多关系的实现方式并分享性能优化的延迟加载策略。2. 多表联合查询基础2.1 多表查询的必要性在真实业务场景中数据往往分散在多个表中。例如电商系统中的订单与用户信息、学生选课系统中的学生与课程关系等。MyBatis提供了强大的结果映射功能能够优雅地处理这些关联关系。2.2 关联关系类型MyBatis支持四种主要的关联关系映射一对一如订单与用户一对多如用户与订单多对一订单对用户实际与一对一相同多对多如学生与课程3. 一对一关系实现3.1 数据准备我们以订单和用户为例首先创建相关表结构CREATE TABLE t_user ( id int NOT NULL AUTO_INCREMENT COMMENT 编号id, username varchar(50) DEFAULT NULL COMMENT 用户名, password varchar(50) DEFAULT NULL COMMENT 密码, age int DEFAULT NULL COMMENT 年龄, sex varchar(2) DEFAULT NULL COMMENT 性别, email varchar(50) DEFAULT NULL COMMENT 邮箱, PRIMARY KEY (id) ); CREATE TABLE orders ( id int NOT NULL AUTO_INCREMENT, user_id int DEFAULT NULL COMMENT 用户ID, number varchar(50) DEFAULT NULL COMMENT 订单编号, create_time datetime DEFAULT NULL COMMENT 创建时间, note varchar(100) DEFAULT NULL COMMENT 备注, PRIMARY KEY (id), KEY user_id (user_id) );3.2 嵌套结果方式3.2.1 实体类设计在Order类中添加User属性public class Order { private Integer id; private Integer userId; private String number; private String createTime; private String note; // 一对一关系 private User user; // getters and setters }3.2.2 Mapper接口定义public interface OrderMapper { ListOrder queryOrderUserResultMap(); }3.2.3 XML映射配置select idqueryOrderUserResultMap resultMaporderUserResultMap SELECT o.id oid, o.user_id, o.number, o.create_time, o.note, u.id uid, u.username, u.password, u.age, u.sex, u.email FROM orders o, t_user u WHERE o.user_id u.id /select resultMap idorderUserResultMap typeorder id columnoid propertyid/ result columnuser_id propertyuserId/ result columnnumber propertynumber/ result columncreate_time propertycreateTime/ result columnnote propertynote/ association propertyuser javaTypecom.sy.pojo.User id columnuid propertyid/ result columnusername propertyusername/ result columnpassword propertypassword/ result columnage propertyage/ result columnsex propertysex/ result columnemail propertyemail/ /association /resultMap3.3 嵌套查询方式3.3.1 Mapper接口public interface OrderMapper { ListOrder queryOrderUserResultMap02(); } public interface UserMapper { User findUserById(int id); }3.3.2 XML映射配置!-- OrderMapper.xml -- select idqueryOrderUserResultMap02 resultMaporderUserResultMap02 SELECT * FROM orders /select resultMap idorderUserResultMap02 typeorder id columnid propertyid/ result columnuser_id propertyuserId/ result columnnumber propertynumber/ result columncreate_time propertycreateTime/ result columnnote propertynote/ association propertyuser javaTypeuser selectcom.sy.mapper.UserMapper.findUserById columnuser_id /association /resultMap !-- UserMapper.xml -- select idfindUserById parameterTypeint resultTypeuser SELECT * FROM t_user WHERE id #{id} /select4. 一对多关系实现4.1 嵌套结果方式4.1.1 实体类设计在User类中添加订单集合public class User { private int id; private String username; // 其他字段... // 一对多关系 private ListOrder orderList; // getters and setters }4.1.2 Mapper接口public interface UserMapper { ListUser findUserOrders(); }4.1.3 XML映射配置select idfindUserOrders resultMapuserResultMap SELECT u.id uid, u.username, u.password, u.age, u.sex, u.email, o.id oid, o.user_id, o.number, o.create_time, o.note FROM t_user u, orders o WHERE u.id o.user_id /select resultMap iduserResultMap typeuser id columnuid propertyid/ result columnusername propertyusername/ !-- 其他用户字段映射 -- collection propertyorderList ofTypeorder id columnoid propertyid/ result columnuser_id propertyuserId/ result columnnumber propertynumber/ result columncreate_time propertycreateTime/ result columnnote propertynote/ /collection /resultMap4.2 嵌套查询方式4.2.1 Mapper接口public interface UserMapper { ListUser findUserOrders02(); } public interface OrderMapper { ListOrder findOrderByUid(int uid); }4.2.2 XML映射配置!-- UserMapper.xml -- select idfindUserOrders02 resultMapuserResultMap02 SELECT * FROM t_user /select resultMap iduserResultMap02 typeuser id columnid propertyid/ !-- 其他用户字段映射 -- collection propertyorderList ofTypeorder selectcom.sy.mapper.OrderMapper.findOrderByUid columnid /collection /resultMap !-- OrderMapper.xml -- select idfindOrderByUid parameterTypeint resultTypeorder SELECT * FROM orders WHERE user_id #{uid} /select5. 多对多关系实现5.1 数据准备创建学生、课程及中间表CREATE TABLE student ( sid int NOT NULL AUTO_INCREMENT, name varchar(50) DEFAULT NULL, PRIMARY KEY (sid) ); CREATE TABLE course ( cid int NOT NULL AUTO_INCREMENT, cname varchar(50) DEFAULT NULL, PRIMARY KEY (cid) ); CREATE TABLE student_course ( id int NOT NULL AUTO_INCREMENT, sid int DEFAULT NULL, cid int DEFAULT NULL, PRIMARY KEY (id), KEY fk_sid (sid), KEY fk_cid (cid), CONSTRAINT fk_cid FOREIGN KEY (cid) REFERENCES course (cid), CONSTRAINT fk_sid FOREIGN KEY (sid) REFERENCES student (sid) );5.2 嵌套结果方式5.2.1 实体类设计public class Student { private Integer sid; private String name; private ListCourse courseList; // getters and setters } public class Course { private Integer cid; private String cname; private ListStudent studentList; // getters and setters }5.2.2 Mapper接口public interface StudentMapper { ListStudent findStudentCourses(); }5.2.3 XML映射配置select idfindStudentCourses resultMapstudentResultMap SELECT stu.sid, stu.name, c.cid, c.cname FROM student stu LEFT JOIN student_course sc ON stu.sid sc.sid LEFT JOIN course c ON sc.cid c.cid /select resultMap idstudentResultMap typestudent id columnsid propertysid/ result columnname propertyname/ collection propertycourseList ofTypecourse id columncid propertycid/ result columncname propertycname/ /collection /resultMap6. 延迟加载策略6.1 延迟加载原理延迟加载的核心思想是按需加载只有在真正需要使用关联数据时才执行查询避免不必要的数据库访问。6.2 配置方式6.2.1 全局配置在mybatis-config.xml中settings !-- 开启延迟加载 -- setting namelazyLoadingEnabled valuetrue/ !-- 禁用积极加载 -- setting nameaggressiveLazyLoading valuefalse/ /settings6.2.2 局部配置在特定的association或collection标签中association propertyuser javaTypeuser selectcom.sy.mapper.UserMapper.findUserById columnuser_id fetchTypelazy /association6.3 使用建议对于频繁访问的关联数据不建议使用延迟加载对于大数据量或不常用的关联数据推荐使用延迟加载注意N1查询问题必要时可以使用批量加载优化7. MyBatis缓存机制7.1 一级缓存默认开启作用域为SqlSession同一会话中相同的查询会直接从缓存返回执行增删改操作或调用clearCache()会清空缓存7.2 二级缓存需要手动配置作用域为Mapper级别实体类需要实现Serializable接口在Mapper.xml中添加 标签启用cache evictionLRU flushInterval60000 size512 readOnlytrue/7.3 缓存使用建议查询多更新少的数据适合使用缓存实时性要求高的数据慎用缓存注意缓存一致性问题的处理8. 实战经验分享在实际项目中使用MyBatis处理多表关系时我总结了以下几点经验关联选择对于简单的一对一或一对多关系优先考虑使用嵌套结果方式减少SQL查询次数对于复杂的多对多关系嵌套查询方式可能更灵活。性能优化当处理大量数据时要注意N1查询问题。我曾遇到一个场景使用嵌套查询方式处理1000条主记录每条主记录有5条关联记录结果产生了1001次查询解决方案是使用批量加载或改为嵌套结果方式。延迟加载陷阱延迟加载在Web应用中要特别注意会话关闭问题。典型的错误是在Controller层获取了主对象然后在视图层尝试访问延迟加载的属性此时会话已关闭会导致异常。解决方法包括使用Open Session In View模式在Service层预先加载需要的关联数据使用DTO代替实体直接返回缓存策略对于配置类等不常变化的数据二级缓存效果显著。但要注意及时清理缓存我曾遇到因为缓存导致配置修改后不生效的问题后来增加了缓存刷新机制。复杂查询处理对于特别复杂的多表关联查询有时直接使用SQL视图或存储过程然后在MyBatis中简单映射比在MyBatis中处理复杂的关联关系更高效。结果映射技巧当字段名和属性名不一致时除了使用resultMap还可以使用SQL别名简化映射。例如SELECT u.id as userId, u.name as userName FROM user u这样即使不定义resultMap也能正确映射。分页优化在多表关联查询中使用分页时要注意不同数据库的分页语法差异。MySQL的LIMIT在复杂关联查询中可能导致性能问题有时需要先分页再关联。通过合理运用这些技巧MyBatis能够高效处理各种复杂的数据关联场景为应用程序提供灵活且高性能的数据访问层解决方案。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门