java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatisPlus自定义SQL怎么写

MyBatisPlus自定义SQL怎么写?三种方式实现复杂查询与分页

作者:csdn2015_

还在为MyBatisPlus复杂查询发愁吗?本文手把手教你三种自定义SQL写法,包括注解、XML和Wrapper条件构造器,轻松搞定多表关联、动态条件和聚合统计,同时讲解分页查询配置,避免分页失效,并提供防SQL注入和性能优化技巧,让你的数据访问层更高效、更安全

在 MyBatis Plus 中,虽然提供了丰富的内置方法,但在面对复杂查询(如多表关联、自定义条件、聚合统计等)时,仍需编写自定义 SQL。

以下是完整的自定义查询 SQL 编写指南,涵盖主流方式、分页支持、多表查询及最佳实践。

一、自定义 SQL 的三种主流方式

1. 注解方式(@Select)

适用于简单查询,直接在 Mapper 接口中编写 SQL。

@Mapper
public interface UserMapper extends BaseMapper<User> {
    @Select("SELECT * FROM user WHERE age > #{age}")
    List<User> selectByAge(@Param("age") Integer age);
    @Select("SELECT u.*, o.order_no FROM user u " +
            "LEFT JOIN orders o ON u.id = o.user_id " +
            "WHERE u.status = #{status}")
    List<UserOrderVO> selectUserWithOrders(@Param("status") Integer status);
}

2. XML 方式(推荐用于复杂 SQL)

在 resources/mapper/UserMapper.xml 中定义 SQL:

<mapper namespace="com.example.mapper.UserMapper">
    <select id="selectByCondition" resultType="com.example.vo.UserVO">
        SELECT u.*, o.order_no
        FROM user u
        LEFT JOIN orders o ON u.id = o.user_id
        <where>
            <if test="age != null">
                AND u.age > #{age}
            </if>
            <if test="status != null">
                AND u.status = #{status}
            </if>
        </where>
        ORDER BY u.create_time DESC
    </select>
</mapper>

Mapper 接口定义:

List<UserVO> selectByCondition(@Param("age") Integer age, @Param("status") Integer status);

3. 使用 Wrapper 条件构造器(适合动态条件)

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("status", 1)
       .gt("age", 18)
       .orderByDesc("create_time");
List<User> users = userMapper.selectList(wrapper);

支持嵌套条件、LIKE、IN、排序等:

wrapper.and(w -> w.eq("name", "张三").or().eq("name", "李四"))
       .like("email", "@gmail.com")
       .in("status", 1, 2, 3);

二、分页查询支持

1. 注解 + 分页

@Select("SELECT * FROM user WHERE status = #{status}")
IPage<User> selectByStatusPage(IPage<User> page, @Param("status") Integer status);

2. XML + 分页

<select id="selectUserPage" resultType="com.example.vo.UserVO">
    SELECT u.*, o.order_no
    FROM user u
    LEFT JOIN orders o ON u.id = o.user_id
    <where>
        <if test="ew.paramNameValuePairs.status != null">
            AND u.status = #{ew.paramNameValuePairs.status}
        </if>
    </where>
</select>

Java 调用:

IPage<UserVO> page = userMapper.selectUserPage(
    new Page<>(1, 10),
    Wrappers.<User>lambdaQuery().eq(User::getStatus, 1)
);

注意:分页需配置 PaginationInnerInterceptor,否则分页无效。

三、多表关联查询

1. 定义 VO 接收结果

@Data
public class UserOrderVO {
    private Long userId;
    private String userName;
    private String orderNo;
    private BigDecimal amount;
}

2. Mapper 接口

@Select("SELECT u.id AS userId, u.name AS userName, o.order_no, o.amount " +
        "FROM user u JOIN orders o ON u.id = o.user_id " +
        "WHERE u.id = #{userId}")
List<UserOrderVO> selectUserOrders(@Param("userId") Long userId);

3. XML 方式(推荐)

<select id="selectUserOrderDetail" resultType="com.example.vo.UserOrderDetailVO">
    SELECT 
        u.id AS userId, u.name AS userName,
        o.id AS orderId, o.order_no, o.amount,
        oi.product_name, oi.quantity
    FROM user u
    JOIN orders o ON u.id = o.user_id
    JOIN order_item oi ON o.id = oi.order_id
    WHERE u.id = #{userId}
</select>

四、动态 SQL 示例(XML)

<select id="selectDynamic" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="age != null">
            AND age >= #{age}
        </if>
        <if test="statusList != null and statusList.size() > 0">
            AND status IN
            <foreach item="s" collection="statusList" open="(" separator="," close=")">
                #{s}
            </foreach>
        </if>
        <if test="startTime != null">
            AND create_time >= #{startTime}
        </if>
        <if test="endTime != null">
            AND create_time &lt;= #{endTime}
        </if>
    </where>
    ORDER BY create_time DESC
</select>

五、自定义 ResultMap(字段映射)

<resultMap id="UserOrderMap" type="com.example.vo.UserOrderVO">
    <id property="userId" column="user_id"/>
    <result property="userName" column="user_name"/>
    <result property="orderNo" column="order_no"/>
    <result property="amount" column="amount"/>
</resultMap>
<select id="selectUserOrder" resultMap="UserOrderMap">
    SELECT u.id AS user_id, u.name AS user_name, o.order_no, o.amount
    FROM user u JOIN orders o ON u.id = o.user_id
    WHERE u.id = #{userId}
</select>

六、最佳实践与注意事项

项目建议
SQL 编写方式简单查询用注解,复杂查询用 XML
参数传递多参数使用 @Param 注解
字段映射使用 ResultMap 明确字段对应关系
分页支持配置 PaginationInnerInterceptor,使用 IPage
防 SQL 注入使用 #{} 而非 ${}
性能优化避免 SELECT *,按需查询字段
可维护性将复杂 SQL 放入 XML,便于管理
条件构造器动态查询优先使用 QueryWrapper / LambdaQueryWrapper

七、分页拦截器配置(Spring Boot)

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

通过上述方式,

你可以灵活地在 MyBatis Plus 中编写自定义 SQL,满足从简单到复杂的各种查询需求,同时保持代码的可维护性和性能优化。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文