动态 SQL
@Dynamic 注解,字段为空时自动跳过
概述
@Dynamic 注解用于标记方法为动态 SQL,当字段值为空时自动跳过该条件。
基本用法
@Dynamic
List<User> findByNameAndAge(String name, Integer age);
name为 null 时,不生成name = ?条件age为 null 时,不生成age = ?条件
动态范围
新增
所有插入字段动态:
@Dynamic
int insertSelective(User user);
-- 只插入非空字段
INSERT INTO user (id, name) VALUES (?, ?) -- age 为 null 时跳过
修改
所有修改字段和条件动态:
@Dynamic
int updateByIdSelective(User user);
@Dynamic
int updateNameByAge(String name, Integer age);
-- 只更新非空字段,只添加非空条件
UPDATE user SET name = ? WHERE age = ? -- 其他字段为 null 时跳过
删除
所有条件动态:
@Dynamic
int deleteByNameAndAge(String name, Integer age);
-- 只添加非空条件
DELETE FROM user WHERE name = ? -- age 为 null 时跳过
查询
所有条件动态:
@Dynamic
List<User> findByNameAndAge(String name, Integer age);
-- 只添加非空条件
SELECT * FROM user WHERE name = ? -- age 为 null 时跳过
内置动态方法
SelectDao 中的内置方法已标记 @Dynamic:
public interface SelectDao<ENTITY, QUERY_ENTITY> extends Dao {
@Dynamic
ENTITY findOne(QUERY_ENTITY entity);
@Dynamic
List<ENTITY> findList(QUERY_ENTITY entity);
@Dynamic
Page<ENTITY> findPage(QUERY_ENTITY entity, Pageable pageable);
}
使用场景
1. 条件可选查询
// 根据传入条件动态查询
@Dynamic
List<User> findByNameAndAgeAndDept(String name, Integer age, String dept);
// 调用示例
userDao.findByNameAndAgeAndDept("张三", null, "研发部");
// 生成 SQL: SELECT * FROM user WHERE name = '张三' AND dept = '研发部'
2. 选择性更新
// 只更新非空字段
@Dynamic
int updateByIdSelective(User user);
// 调用示例
User user = new User();
user.setId(1L);
user.setName("新名字");
// age 为 null,不会被更新
userDao.updateByIdSelective(user);
// 生成 SQL: UPDATE user SET name = '新名字' WHERE id = 1
3. 动态条件删除
// 根据非空条件删除
@Dynamic
int deleteByConditions(String name, Integer age, String dept);
// 调用示例
userDao.deleteByConditions(null, 25, null);
// 生成 SQL: DELETE FROM user WHERE age = 25
与静态 SQL 对比
静态 SQL
// 不加 @Dynamic
int updateById(User user);
-- 所有字段都会被更新
UPDATE user SET name = ?, age = ? WHERE id = ?
-- 如果 name 或 age 为 null,数据库字段会被设为 NULL
动态 SQL
// 加 @Dynamic
@Dynamic
int updateByIdSelective(User user);
-- 只有非空字段会被更新
UPDATE user SET name = ? WHERE id = ?
-- age 为 null 时不会被更新
空值判断规则
| 类型 | 判断为空的条件 |
|---|---|
| 基本类型包装类 | == null |
| String | == null 或 isEmpty() |
| Collection | == null 或 isEmpty() |
| 数组 | == null 或 length == 0 |
自定义动态方法
@Repository
public interface UserDao extends SimpleDao<User, UserQuery, Long> {
// 动态查询
@Dynamic
List<User> search(String name, Integer age, String dept);
// 动态更新
@Dynamic
int updateSelective(User user, UserQuery condition);
// 动态删除
@Dynamic
int deleteByCondition(UserQuery condition);
}
注意事项
-
内置方法无需重复标记:
findOne、findList、findPage已内置@Dynamic -
空字符串也会被跳过:String 类型空字符串视为空值
-
集合为空时跳过:
In条件的集合为空或 null 时跳过 -
所有条件为空时:
- 查询:生成无条件查询(查询全部)
- 更新/删除:不生成 WHERE 子句(影响全表,需谨慎)