全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 8 分钟 ✍️ juanwangdev

@SelectProvider 动态 SQL

当 SQL 需要按条件动态拼接时,注解模式不再适合写在 @Select 中,此时使用 @SelectProvider 系列注解,通过 Java 类动态生成 SQL 语句。

Provider 注解家族

注解对应操作替代 XML
@SelectProvider查询<select>
@InsertProvider插入<insert>
@UpdateProvider更新<update>
@DeleteProvider删除<delete>

@SelectProvider 基础用法

type + method 属性

Java
public interface UserMapper {

    @SelectProvider(type = UserSqlProvider.class, method = "selectByCondition")
    List<User> selectByCondition(UserQuery query);
}

Provider 类:

Java
public class UserSqlProvider {

    public String selectByCondition(UserQuery query) {
        SQL sql = new SQL() {{
            SELECT("*");
            FROM("users");
            if (query.getName() != null) {
                WHERE("name LIKE #{name}");
            }
            if (query.getAge() != null) {
                WHERE("age >= #{age}");
            }
            if (query.getDeptId() != null) {
                WHERE("dept_id = #{deptId}");
            }
            ORDER_BY("id DESC");
        }};
        return sql.toString();
    }
}

SQL Builder 模式

MyBatis 提供 org.apache.ibatis.jdbc.SQL 工具类,以链式方式构建 SQL。

常用方法

方法对应 SQL 片段
SELECT(cols)SELECT col1, col2
FROM(table)FROM table_name
WHERE(condition)WHERE condition(多个 WHERE 自动用 AND 连接)
ORDER_BY(cols)ORDER BY col1, col2
GROUP_BY(cols)GROUP BY col1
INNER_JOIN(table)INNER JOIN table ON ...
LEFT_OUTER_JOIN(table)LEFT OUTER JOIN table ON ...
LIMIT(n)LIMIT n

多条件拼接示例

Java
public String findUsers(UserQuery query) {
    return new SQL() {{
        SELECT("u.id", "u.name", "u.email", "d.name AS dept_name");
        FROM("users u");
        LEFT_OUTER_JOIN("departments d ON u.dept_id = d.id");

        if (query.getName() != null) {
            WHERE("u.name LIKE CONCAT('%', #{name}, '%')");
        }
        if (query.getAgeMin() != null) {
            WHERE("u.age >= #{ageMin}");
        }
        if (query.getAgeMax() != null) {
            WHERE("u.age <= #{ageMax}");
        }
        if (query.getStatusList() != null && !query.getStatusList().isEmpty()) {
            WHERE("u.status IN (" +
                query.getStatusList().stream()
                    .map(s -> "'#{statusList[" + query.getStatusList().indexOf(s) + "]}'")
                    .collect(Collectors.joining(",")) +
            ")");
        }

        ORDER_BY("u.create_time DESC");
    }}.toString();
}

@InsertProvider 动态插入

Java
public interface UserMapper {

    @InsertProvider(type = UserSqlProvider.class, method = "insertUser")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insertUser(User user);
}
Java
public String insertUser(User user) {
    return new SQL() {{
        INSERT_INTO("users");

        if (user.getName() != null) {
            INTO_COLUMNS("name");
            INTO_VALUES("#{name}");
        }
        if (user.getEmail() != null) {
            INTO_COLUMNS("email");
            INTO_VALUES("#{email}");
        }
        if (user.getDeptId() != null) {
            INTO_COLUMNS("dept_id");
            INTO_VALUES("#{deptId}");
        }
    }}.toString();
}

SQL Builder 的 INSERT_INTO + INTO_COLUMNS/INTO_VALUES 组合用于动态插入,仅非空字段参与 INSERT。

@UpdateProvider 动态更新

Java
public interface UserMapper {

    @UpdateProvider(type = UserSqlProvider.class, method = "updateUser")
    int updateUser(User user);
}
Java
public String updateUser(User user) {
    return new SQL() {{
        UPDATE("users");

        if (user.getName() != null) {
            SET("name = #{name}");
        }
        if (user.getEmail() != null) {
            SET("email = #{email}");
        }
        if (user.getDeptId() != null) {
            SET("dept_id = #{deptId}");
        }

        WHERE("id = #{id}");
    }}.toString();
}

@DeleteProvider 动态删除

Java
public interface UserMapper {

    @DeleteProvider(type = UserSqlProvider.class, method = "deleteByCondition")
    int deleteByCondition(UserQuery query);
}
Java
public String deleteByCondition(UserQuery query) {
    return new SQL() {{
        DELETE_FROM("users");
        if (query.getDeptId() != null) {
            WHERE("dept_id = #{deptId}");
        }
        if (query.getExpiredDays() != null) {
            WHERE("create_time < DATE_SUB(NOW(), INTERVAL #{expiredDays} DAY)");
        }
    }}.toString();
}

Provider 方法参数规则

场景方法签名说明
单参数String method(ParamType param)直接用 #{param}
无参数String method()不接收参数
多参数String method(@Param("a") TypeA a, @Param("b") TypeB b)使用 #{a.field} 引用

Provider 方法返回 String 类型,方法体内使用 SQL Builder 构建语句,最终调用 .toString() 返回。

要点总结

  • @SelectProvider / @InsertProvider / @UpdateProvider / @DeleteProvider 通过 Java 类动态生成 SQL
  • type 指定 Provider 类,method 指定生成 SQL 的方法
  • 使用 SQL Builder 类链式构建 SQL,WHERE / SET 多个条件自动用 AND 连接
  • 动态 INSERT 使用 INSERT_INTO + INTO_COLUMNS / INTO_VALUES 组合
  • Provider 方法必须返回 String,参数通过 @Param 标注后可在 SQL 中引用

📝 发现内容有误?点击此处直接编辑

← 上一篇 @Select 等基础注解
下一篇 → Mapper 接口注入
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库