不灭的火

革命尚未成功,同志仍须努力下载JDK17

作者:AlbertWen  添加时间:2025-04-14 00:13:39  修改时间:2025-04-25 14:06:26  分类:02.MyBatis-Flex  编辑

一、工具类UpdateEntity

在 MyBatis-Flex 中,UpdateEntity 是一个用于部分字段更新的核心工具类,尤其适用于需要更新某些字段为 null 或仅修改特定字段的场景。以下是其核心用法和特性总结:

1. UpdateEntity 的作用

  • 部分字段更新:通过 UpdateEntity 创建的对象,仅会更新调用了 setter 方法的字段,未调用的字段即使实体类中存在值,也不会更新到数据库。
  • 支持 null 值更新:若需要将字段更新为 null,直接通过 setter 方法设置即可,无需额外配置。

2. 创建 UpdateEntity 实例

通过 UpdateEntity.of 方法创建实例,需指定实体类类型及主键值:

// 方式1:自动设置主键(需实体类主键字段已赋值)
Account account = UpdateEntity.of(Account.class);
account.setId(100); // 必须设置主键

// 方式2:直接传入主键值
Account account = UpdateEntity.of(Account.class, 100); // 主键自动赋值

3. 更新字段的示例

通过 setter 方法设置需要更新的字段,未调用的字段不会被修改:

Account account = UpdateEntity.of(Account.class, 100);
account.setUserName(null);  // 更新为 null
account.setAge(10);         // 更新为 10
accountMapper.update(account); // 生成 SQL:UPDATE tb_account SET user_name=?, age=? WHERE id=100

生成的 SQL 仅包含调用 setter 的字段,性能高效。

4. 注意事项

  • 主键必须有效UpdateEntity 必须通过主键定位记录,否则会抛出异常。
  • 字段覆盖规则:即使实体类其他字段有值,未通过 setter 方法调用的字段不会被更新。
  • 性能优化UpdateEntity 生成的 SQL 仅包含必要字段,避免全字段更新的性能损耗。

5. 适用场景

  • 需要更新少量字段且部分字段为 null
  • 需要避免覆盖数据库中其他字段的默认值或现有值。

通过合理使用 UpdateEntity,可以显著简化部分字段更新的代码逻辑,同时提升数据库操作的灵活性和安全性。

二、工具类UpdateWrapper

1. 使用UpdateWrapper 新增数据

在某些场景下,我们希望在新增数据时,新增数据字段内容是数据库的某个 函数 或者 SQL片段 生成的内容,而非我们手动设置的内容。 例如,我们希望执行的 SQL 如下:

INSERT INTO `tb_account`(`user_name`,  `birthday`)
VALUES (?, now())

以上 SQL 中,birthday 是由 now() 函数生成的内容。

那么,Java 代码如下:

@Test
public void testInsertWithRaw() {
    Account account = new Account();
    account.setUserName("michael");

    Account newAccount = UpdateWrapper.of(account)
//       .setRaw("birthday", "now()")
//       .setRaw(ACCOUNT.BIRTHDAY, "now()")
        .setRaw(Account::getBirthday, "now()")
        .toEntity();

    accountMapper.insert(newAccount);
}

或者复杂一点的:

@Test
public void testInsertWithRaw() {
    Account account = new Account();
    account.setUserName("michael");

    Account newAccount = UpdateWrapper.of(account)
        .setRaw(Account::getBirthday, "(select xxx from ...)")
        .toEntity();

    accountMapper.insert(newAccount);
}

其生成的 SQL 如下:

INSERT INTO `tb_account`(`user_name`,  `birthday`)
VALUES (?, (select xxx from ...))

注意,通过 UpdateWrapper.setRaw() 的设置,会覆盖注解 @Column.onUpdateValue 配置的内容。

2.使用UpdateWrapper部分字段更新(增强)

在以上的部分字段更新中,只能更新为用户传入的数据,但是有些时候我们想更新为数据库计算的数据,比如 SQL:

update tb_account
set user_name = ?, age = age + 1 where id = ?

此时,我们可以直接把 Account 强转为 UpdateWrapper 然后进行更新,例如:

Account account = UpdateEntity.of(Account.class, 100);

account.setUserName(null);

// 通过 UpdateWrapper 操作 account 数据
UpdateWrapper wrapper = UpdateWrapper.of(account);
wrapper.setRaw("age", "age + 1");

accountMapper.update(account);

其执行的 SQL 为:

update tb_account
set user_name = null, age = age + 1 where id = 100

更高级的用法

示例1:

Account account = UpdateEntity.of(Account.class, 100);

account.setUserName("Michael");

// 通过 UpdateWrapper 操作 account 数据
UpdateWrapper wrapper = UpdateWrapper.of(account);
wrapper.set(ACCOUNT.AGE, ACCOUNT.AGE.add(1));

accountMapper.update(account);

其执行的 SQL 为:

update tb_account
set user_name = "michael", age = age + 1 where id = 100

示例2:

Account account = UpdateEntity.of(Account.class, 100);

account.setUserName("Michael");

// 通过 UpdateWrapper 操作 account 数据
UpdateWrapper wrapper = UpdateWrapper.of(account);
wrapper.set(ACCOUNT.AGE, select().from(...));

accountMapper.update(account);

其执行的 SQL 为:

update tb_account
set user_name = "michael", age = (select ... from ... )
where id = 100

三、工具类UpdateChain

UpdateChain 是一个对 UpdateEntityUpdateWrapper 等进行封装的一个工具类,方便用户用于进行链式操作。

假设我们要更新 Account 的 userName 为 "张三",更新年龄在之前的基础上加 1,更新代码如下:

@Test
public void testUpdateChain() {
    UpdateChain.of(Account.class)
        .set(Account::getUserName, "张三")
        .setRaw(Account::getAge, "age + 1")
        .where(Account::getId).eq(1)
        .update();
}

以上方法调用时,MyBatis-Flex 内部执行的 SQL 如下:

UPDATE `tb_account` SET `user_name` = '张三' , `age` = age + 1
WHERE `id` = 1

更多关于 链式操作,请点击这个 这里

四、set() 和 setRaw() 的区别

在 RowUpdateWrapperUpdateChain 中,都提供了 set() 和 setRaw() 两个方法用于设置数据。 那么,他们有什么区别呢?

  • set() 方法用于设置参数数据。
  • setRaw() 用于设置 SQL 拼接数据。

例如:

UpdateChain.of(Account.class)
    .set(Account::getUserName, "张三")
    .where(Account::getId).eq(1)
    .update();

其执行的 SQL 如下:

UPDATE `tb_account` SET `user_name` = ? WHERE `id` = 1

如果是使用 setRaw() 方法:

UpdateChain.of(Account.class)
    .setRaw(Account::getUserName, "张三")
    .where(Account::getId).eq(1)
    .update();

以上代码执行时,参数 "张三" 会直接参与 SQL 拼接,可能会造成 SQL 错误,其 SQL 如下:

UPDATE `tb_account` SET `user_name` = 张三 WHERE `id` = 1

因此,需要用户 【特别注意!!!】setRaw() 传入不恰当的参数时,可能会造成 SQL 注入的危险。 因此,调用 setRaw() 方法时,需要开发者自行对其参数进行 SQL 注入过滤。

setRaw() 经常使用的场景:

  • 场景1: 用户充值,更新用户金额:
UpdateChain.of(Account.class)
    .setRaw(Account::getMoney, "money + 100")
    .where(Account::getId).eq(1)
    .update();

 其执行的 SQL 如下:

UPDATE `tb_account` SET  `money` = money + 100
WHERE `id` = 1
  • 场景2:执行某些特殊函数:
UpdateChain.of(Account.class)
    .setRaw(Account::getUserName, "UPPER(user_name)")
    .where(Account::getId).eq(1)
    .update();

其执行的 SQL 如下:

UPDATE tb_account SET  user_name = UPPER(user_name)
WHERE id = 1

或者

UpdateChain.of(Account.class)
    .setRaw(Account::getUserName, "utl_raw.cast_to_raw('some magic here')")
    .where(Account::getId).eq(1)
    .update();

其执行的 SQL 如下:

UPDATE tb_account SET  user_name = utl_raw.cast_to_raw('some magic here')
WHERE id = 1