030-若依pro(ruoyi-vue-pro)MyBatis 动态SQL与联表查询实战
1. 若依Pro框架中的MyBatis动态SQL入门
第一次接触若依Pro框架的开发者,可能会被MyBatis的动态SQL功能惊艳到。这个功能就像是给SQL语句装上了"智能大脑",让它能够根据不同的条件自动调整查询语句。在实际项目中,我经常用它来处理各种复杂的查询场景,效果相当不错。
动态SQL的核心在于<if>、<where>、<choose>这些标签。举个例子,当我们需要根据用户输入的不同条件来查询用户列表时,传统做法可能要写多个查询方法,而使用动态SQL只需要一个方法就能搞定。在若依Pro中,这个特性被大量应用在各种管理系统的查询功能中。
<select id="selectUserList" resultType="AdminUserDO"> SELECT * FROM system_users <where> <if test="username != null and username != ''"> AND username LIKE CONCAT('%', #{username}, '%') </if> <if test="status != null"> AND status = #{status} </if> </where> </select>上面这段XML配置就是典型的动态SQL用法。<where>标签会自动处理条件之间的AND连接,而<if>标签则根据条件是否存在来决定是否包含某段SQL。我在实际项目中发现,这种写法不仅减少了代码量,还让查询逻辑更加清晰。
2. 动态SQL的进阶用法与实战技巧
2.1 复杂条件组合处理
在实际开发中,我们经常会遇到更复杂的查询条件组合。比如需要同时处理时间范围、多状态筛选等场景。这时候<choose>标签就派上用场了。它类似于Java中的switch-case结构,可以根据不同条件执行不同的SQL片段。
<select id="selectComplexUserList" resultType="AdminUserDO"> SELECT * FROM system_users <where> <choose> <when test="userType == 'admin'"> AND role_id IN (1, 2, 3) </when> <when test="userType == 'vip'"> AND vip_level > 0 </when> <otherwise> AND status = 1 </otherwise> </choose> <if test="startTime != null and endTime != null"> AND create_time BETWEEN #{startTime} AND #{endTime} </if> </where> </select>2.2 动态字段与排序处理
除了条件查询,动态SQL还能处理字段选择和排序。比如在列表页面,用户可能希望自定义显示的列或者按不同字段排序。这时候可以用<trim>和<foreach>标签来实现。
<select id="selectUserWithFields" resultType="map"> SELECT <trim suffixOverrides=","> id, username, <if test="fields.contains('email')">email,</if> <if test="fields.contains('phone')">phone,</if> </trim> FROM system_users ORDER BY <foreach item="item" index="index" collection="sortFields" separator=","> ${item.field} ${item.order} </foreach> </select>这种写法我在用户管理系统中使用过,效果非常好。前端可以灵活指定需要查询的字段和排序方式,而后端不需要为每种组合都写单独的查询方法。
3. 联表查询的两种实现方式
3.1 传统XML方式实现联表
在若依Pro中,联表查询是处理复杂业务场景的利器。最传统的方式就是在XML中直接写JOIN语句。比如查询用户信息时同时获取部门名称:
<select id="selectUserWithDept" resultType="AdminUserDetailDO"> SELECT u.*, d.name AS deptName FROM system_users u LEFT JOIN system_dept d ON u.dept_id = d.id <where> <if test="username != null"> AND u.username LIKE CONCAT('%', #{username}, '%') </if> </where> </select>这种方式简单直接,适合相对固定的联表查询。我在早期项目中大量使用这种写法,它的优点是性能可控,可以精确优化SQL语句。
3.2 使用MyBatis Plus Join实现联表
若依Pro还集成了MyBatis Plus Join框架,可以用Java代码的方式实现联表查询。这种方式更加面向对象,适合喜欢Lambda表达式的开发者。
public List<AdminUserDetailDO> selectUserWithDept(String username) { return selectJoinList(AdminUserDetailDO.class, new MPJLambdaWrapper<AdminUserDO>() .selectAll(AdminUserDO.class) .selectAs(DeptDO::getName, AdminUserDetailDO::getDeptName) .leftJoin(DeptDO.class, DeptDO::getId, AdminUserDO::getDeptId) .like(username != null, AdminUserDO::getUsername, username) ); }这种写法虽然需要一定的学习成本,但熟悉后开发效率很高。我在最近的项目中更倾向于使用这种方式,因为它能更好地利用IDE的代码提示和重构功能。
4. 分页查询的优化实践
4.1 基础分页实现
分页查询是管理系统中最常见的需求之一。在若依Pro中,可以通过MyBatis的XML方式实现基本分页:
<select id="selectUserPage" resultType="AdminUserDO"> SELECT * FROM system_users LIMIT #{pageNo}, #{pageSize} </select>不过这种简单实现有个问题:当数据量很大时,性能会下降。我曾在处理百万级数据表时就遇到过这个问题。
4.2 性能优化分页
对于大数据量的分页,可以使用基于游标的分页方式:
<select id="selectUserPageOptimized" resultType="AdminUserDO"> SELECT * FROM system_users WHERE id > #{lastId} ORDER BY id LIMIT #{pageSize} </select>这种分页方式利用了索引的有序性,避免了传统LIMIT OFFSET在大偏移量时的性能问题。在实际项目中,当分页深度超过100页时,这种方法的性能优势非常明显。
4.3 使用MyBatis Plus的分页插件
若依Pro默认集成了MyBatis Plus的分页插件,使用起来更加方便:
public PageResult<AdminUserDO> getUserPage(UserPageReqVO reqVO) { Page<AdminUserDO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize()); IPage<AdminUserDO> pageResult = adminUserMapper.selectPage(page, Wrappers.<AdminUserDO>lambdaQuery() .like(StringUtils.isNotBlank(reqVO.getUsername()), AdminUserDO::getUsername, reqVO.getUsername()) ); return new PageResult<>(pageResult.getRecords(), pageResult.getTotal()); }这种方式会自动处理总数统计和分页逻辑,开发效率很高。我在大多数标准分页场景中都使用这种方式。
5. 动态SQL与联表查询的综合应用
5.1 复杂报表查询案例
在实际项目中,我们经常需要实现一些复杂的报表查询。比如需要根据多种条件查询用户及其关联的部门、角色信息:
<select id="selectUserReport" resultMap="userReportMap"> SELECT u.*, d.name AS dept_name, r.name AS role_name FROM system_users u LEFT JOIN system_dept d ON u.dept_id = d.id LEFT JOIN system_user_role ur ON u.id = ur.user_id LEFT JOIN system_role r ON ur.role_id = r.id <where> <if test="deptId != null"> AND u.dept_id = #{deptId} </if> <if test="roleId != null"> AND r.id = #{roleId} </if> <if test="status != null"> AND u.status = #{status} </if> </where> </select>这种综合应用展示了动态SQL和联表查询的强大组合能力。我在开发数据分析功能时经常使用这种模式。
5.2 性能优化建议
在进行复杂联表查询时,有几点性能优化建议:
- 尽量避免超过3张表的JOIN操作
- 为所有JOIN条件字段建立索引
- 只查询必要的字段,不要使用SELECT *
- 对于大数据量表,考虑使用冗余字段代替部分JOIN操作
我在实际项目中就曾通过优化联表查询,将某个报表的查询时间从5秒降低到了0.5秒以内。关键是要理解业务需求,找到最适合的查询方式。
