@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 类动态生成 SQLtype指定 Provider 类,method指定生成 SQL 的方法- 使用
SQLBuilder 类链式构建 SQL,WHERE/SET多个条件自动用 AND 连接 - 动态 INSERT 使用
INSERT_INTO+INTO_COLUMNS/INTO_VALUES组合 - Provider 方法必须返回
String,参数通过@Param标注后可在 SQL 中引用
📝 发现内容有误?点击此处直接编辑