1. QueryWrapper和QueryChainWrapper的比较

QueryWrapper和QueryChainWrapper都是条件构造器,其中QueryChainWrapper支持链式操作。且name使用的表字段。

  • QueryWrapper示例
// 假设有一个 QueryWrapper 对象,设置查询条件为 age > 25
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 25);
List<User> users = userService.list(queryWrapper); 
for (User user : users) {
    System.out.println("User: " + user);
}
  • QueryChainWrapper示例
List<User> users = userService.query().gt("age", 25).list(); 
for (User user : users) {
    System.out.println("User: " + user);
}

2. LambdaQueryWrapper和LambdaQueryChainWrapper的比较

LambdaQueryWrapper和LambdaQueryChainWrapper都是条件构造器,支持lambda表达式,其中LambdaQueryChainWrapper支持链式操作。且name使用的实体字段。

  • LambdaQueryWrapper示例
// 假设有一个 QueryWrapper 对象,设置查询条件为 age > 25
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.gt(User::getAge, 25);
List<User> users = userService.list(queryWrapper); 
for (User user : users) {
    System.out.println("User: " + user);
}
  • LambdaQueryChainWrapper示例
List<User> users = userService.lambdaQuery().gt(User::getAge, 25).list(); 
for (User user : users) {
    System.out.println("User: " + user);
}

3. 更新实体是否使用UpdateWrapper的区别

update支持boolean update(Wrapper updateWrapper)和update(T updateEntity, Wrapper whereWrapper),如果只想更新某些字段,推荐使用 前者,避免 updateEntity 里 null 误更新。其中,Wrapper whereWrapper既可以使用QueryWrapper也可以使用UpdateWrapper。

  • updateEntity 和 UpdateWrapper 更新相同字段
User updateEntity = new User();
updateEntity.setName("Entity Name"); // 在实体对象中设置 name

UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("name", "Wrapper Name").eq("id", 1); // 在 UpdateWrapper 中设置 name

boolean result = userService.update(updateEntity, updateWrapper);

#最终sql语句如下(UpdateWrapper 的 set优先级更高):
UPDATE user SET name = 'Wrapper Name' WHERE id = 1;
  • 示例:更新字段为null
User user = new User();
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", 1)  // 只更新 id 为 1 的用户
            .set("name", null);  // 显式设置 name 为 null

boolean result = userService.update(user, updateWrapper);

注意

  1. 使用 update(T updateEntity, Wrapper whereWrapper) 方法时,如果你没有显式地设置字段为 null(即 user.setName(null)),那么即使 name 是 null,它也不会更新为 null。这是因为 MyBatis-Plus 默认情况下不会将字段值为 null 的属性纳入更新操作。为了防止无意中将字段更新为 null,MyBatis-Plus 会忽略 null 值的字段。
  2. 要显式更新 null 值,可以使用 UpdateWrapper 的 set 方法来确保将字段更新为 null,而不是依赖于实体类的字段值。

4. updateById和alwaysUpdateSomeColumnById的区别

alwaysUpdateSomeColumnById 是 MyBatis-Plus 的一个扩展方法,用于强制更新某些字段,即使这些字段的值是 null。

  • updateById示例
User updateEntity = new User();
updateEntity.setId(1);
updateEntity.setName(null); // 默认 updateById 不会更新 null
updateEntity.setAge(25);
boolean result = userService.updateById(updateEntity);

#最终的sql语句
UPDATE user SET age = 25 WHERE id = 1;
  • alwaysUpdateSomeColumnById示例
// 创建 User 对象
User updateEntity = new User();
updateEntity.setId(1); // 必须提供 id
updateEntity.setName(null); // 这里设为 null
updateEntity.setAge(25); // 这里设为 25
// 指定要更新的字段
boolean result = userService.alwaysUpdateSomeColumnById(updateEntity);

#最终的sql语句
UPDATE user SET name = NULL, age = 25 WHERE id = 1;

注意:

  • updateById 会忽略 null 值的字段(name 不更新);
  • alwaysUpdateSomeColumnById 会强制更新 null(name 变为 NULL)。

5. insertBatchSomeColumn介绍

insertBatchSomeColumn 是 MyBatis-Plus 提供的批量插入方法,可以让你在批量插入数据时指定只插入部分字段,而不插入所有字段。这个方法非常有用,特别是在你不想插入某些字段(例如 null 字段)时。

  • 示例
#方法insertBatchSomeColumn(Collection<T> entityList, String... column);

List<User> userList = new ArrayList<>();

User user1 = new User();
user1.setName("John");
user1.setAge(30);

User user2 = new User();
user2.setName("Alice");
user2.setAge(25);

userList.add(user1);
userList.add(user2);

// 使用 insertBatchSomeColumn 方法批量插入数据
boolean result = userService.insertBatchSomeColumn(userList, "name", "age");

if (result) {
    System.out.println("Batch insert successful!");
} else {
    System.out.println("Batch insert failed!");
}

注意:

  • 在 insertBatchSomeColumn 中,字段的顺序和类型要与数据库表一致。
  • 如果插入的字段是 null,它会被插入到数据库中,但一般建议避免插入 null 字段。

6. logicDeleteById和logicDeleteByIdWithFill的区别

logicDeleteById 和 logicDeleteByIdWithFill 都是 MyBatis-Plus 提供的逻辑删除方法,但它们之间有一个关键的区别,主要体现在 是否自动填充字段,尤其是删除相关的字段。

  1. logicDeleteById

作用:根据 ID 执行逻辑删除操作,将 逻辑删除字段(如 deleted)更新为 1,标记记录为已删除。
不自动填充其他字段,如删除时间、删除人等。如果你需要填充额外的字段,需要手动设置。

  • 示例
#需要确保 deleted 字段 被正确地标记为逻辑删除。
@TableLogic
private Integer deleted;  // 逻辑删除字段,值为 1 表示已删除

boolean result = userService.logicDeleteById(1L); // 逻辑删除 id 为 1 的用户
#最终的sql语句
UPDATE user SET deleted = 1 WHERE id = 1;
  1. logicDeleteByIdWithFill

作用:除了进行逻辑删除外,还会根据配置的 MetaObjectHandler 自动填充一些字段(如删除时间、修改时间等)。例如,deletedTime 字段可以自动填充为当前时间。
自动填充字段:通过 MetaObjectHandler 配置的 updateFill 或 insertFill 方法来填充字段,这样无需手动赋值。

  • 示例
#需要确保 deleted 字段 被正确地标记为逻辑删除。
@TableLogic
private Integer deleted;  // 逻辑删除字段,值为 1 表示已删除
#MetaObjectHandler 配置自动填充字段
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        // 在插入时填充字段
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        // 在更新时填充删除时间字段
        if (metaObject.hasSetter("deletedTime")) {
            this.setFieldValByName("deletedTime", LocalDateTime.now(), metaObject);
        }
    }
}

boolean result = userService.logicDeleteByIdWithFill(1L); // 逻辑删除并自动填充删除时间
#最终的sql语句
UPDATE user SET deleted = 1, deleted_time = '2025-03-12 10:00:00' WHERE id = 1;

7. Wrappers介绍

MyBatis-Plus 提供了 Wrappers 类,它是一个静态工厂类,用于快速创建 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 的实例。使用 Wrappers 可以减少代码量,提高开发效率。

  • 示例
// 创建 QueryWrapper
QueryWrapper<User> queryWrapper = Wrappers.query();
queryWrapper.eq("name", "张三");

// 创建 LambdaQueryWrapper
LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
lambdaQueryWrapper.eq(User::getName, "张三");

// 创建 UpdateWrapper
UpdateWrapper<User> updateWrapper = Wrappers.update();
updateWrapper.set("name", "李四");

// 创建 LambdaUpdateWrapper
LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
lambdaUpdateWrapper.set(User::getName, "李四");

注意:

  1. 在使用QueryWrapper、UpdateWrapper查询、设置条件字段部分,一定要保证安全,避免SQL注入攻击。
  2. 任何前端传入的SQL片段都要严格过滤。

8. 自定义sql语句

MyBatis-Plus 提供了强大的 Wrapper 条件构造器,允许开发者自定义 SQL 语句,以满足更复杂的数据库查询需求。为了使用这一功能,请确保你的 mybatis-plus 版本不低于 3.0.7。

  1. Constants.WRAPPER

使用 ${ew.customSqlSegment}:在 SQL 语句中,使用 ${ew.customSqlSegment} 来引用 Wrapper 对象生成的 SQL 片段。不支持基于 entity 的 where 语句。

  • 示例
#service方法
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "张三");
List<User> userList = userMapper.selectByCustomSql(queryWrapper);

#mapper方法
public interface UserMapper extends BaseMapper<User> {
    @Select("SELECT * FROM user ${ew.customSqlSegment}")
    List<User> selectByCustomSql(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
}
  1. map动态参数

使用 Mapper.xml 方式,防止 SQL 注入。

  • 示例
List<User> selectByParams(@Param("params") Map<String, Object> params);

<select id="selectByParams" resultType="User">
    SELECT * FROM user
    WHERE 1=1
    <if test="params.userName != null">
        AND user_name = #{params.userName}
    </if>
    <if test="params.email != null">
        AND email = #{params.email}
    </if>
</select>

9. 线程安全性

Wrapper 实例不是线程安全的,因此建议在每次使用时创建新的 Wrapper 实例。这样可以避免多线程环境下的数据竞争和潜在的错误。

  • 示例
// 每次创建新的 QueryWrapper 实例,避免共享
QueryWrapper<User> wrapper1 = new QueryWrapper<>();
wrapper1.eq("name", "John");

QueryWrapper<User> wrapper2 = new QueryWrapper<>();
wrapper2.eq("age", 30);

注意
QueryWrapper.clear() 方法 不会使 QueryWrapper 变为线程安全,它仅仅是清除当前 QueryWrapper 对象的所有条件,以便于 重复使用同一个实例。但是,仍然存在潜在的风险,特别是在多线程环境中,因为你可能不确定其他线程是否正在使用这个同一个 QueryWrapper 实例,或者在清除后是否会有意外的修改。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐