02MyBatisPlus条件构造器,自定义SQL,Service接口

一、条件构造器

1.MyBatis 支持各种复杂的where条件,满足开发的需求

Wrapper是条件构造器,构建复杂的where查询

AbstractWrapper有构造where条件的所有方法,QueryWrapper继承后并有自己的select指定查询字段。UpdateWrapper有指定更新的字段的方法

2. 案例:基于QueryWrapper查询

①查询出名字带"o"的,存款大于等于1000元的人的id,username,info,balance字段

java 复制代码
@Test
void test(){
    // 构建查询条件
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .select("id","username","info","balance")
            .like("username","o") // where条件
            .ge("balance",1000);
    // 查询
    user2Mapper.selectList(wrapper);
}

LambdaQueryWrapper 方法

java 复制代码
@Test
void testl() {
   // 构建查询条件
   LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
         .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
         .like(User::getUsername, "o") // where条件
         .ge(User::getBalance, 1000);
   // 查询
   List<User> users = user2Mapper.selectList(wrapper);
   System.out.println(users);
}

②更新用户名为jack的用户的余额为2000

java 复制代码
@Test
void test2(){
    // 更新字段
    User user = new User();
    user.setBalance(2000);
    // 构建查询条件
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .eq("username","jack");
    user2Mapper.update(user, wrapper);
}

update(更新的数据,更新的条件)

3. 案例:基于UpdateWrapper更新

①更新id为1,2,4的用户的金额扣200

java 复制代码
@Test
void test3() {
   // 更新条件的ids
   List<Long> ids = new ArrayList<>();
   ids.add(1L);
   ids.add(2L);
   ids.add(4L);
   // 更新的内容和条件
   UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
         .setSql("balance = balance - 200")
         .in("id",ids);
   user2Mapper.update(null, wrapper);
}

4. 条件构造器的用法总结

①QueryWrapper和LambdaQueryWrapper通常构建select,delete,update的wher条件部分

②UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用

③尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码。

二、自定义SQL

1. 用法

通过MP的Wrapper来构建复杂的where条件,然后自己定义SQL语句剩下的部分。

2. 案例将id在指定范围内的用户(1,2,4)的余额扣减指定值

把mp构建好的条件传递到mapper,进行sql组装

①基于Wrapper构建where条件

java 复制代码
@Test
void test3() {
   // 更新条件的ids
   List<Long> ids = new ArrayList<>();
   ids.add(1L);
   ids.add(2L);
   ids.add(4L);
   // 更新的内容
   int amount = 200;
   // 编写where更新条件
   LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
         .in(User::getId,ids);
   // 用户自定义SQL
   user2Mapper.updateBalanceByIds(wrapper,amount);
}

②用户自定义mapper方法参数中用Param注解声明wrapper变量,必须是ew

java 复制代码
void updateBalanceByIds(@Param("ew") LambdaQueryWrapper<User> wrapper, @Param("amount")int amount);

③编写sql语句,进行where拼接

XML 复制代码
<update id="updateBalanceByIds">
    update tb_user set balance = balance - #{amount} ${ew.customSqlSegment}
</update>

三、Service接口

1.IService 接口的方法

2.IService 接口的继承

①自定义Service接口继承IService接口

②自定义Service实现类,实现自定义接口并继承ServiceImpl类

③新增用户案例

java 复制代码
@PostMapping
@ApiOperation("新增")
public void addUser(@RequestBody UserFormDTO userFormDTO){
   // DTO拷贝到PO
   User user = new User();
   BeanUtils.copyProperties(userFormDTO,user);
   // 保存
   userService.save(user);
}

3. 案例:IService的Lambda查询

需求:实现一个根据复杂条件查询用户的接口,查询条件如下:

name:用户名关键字,可以为空

status:用户状态,可以为空

原始的MyBatis

LambdaQuery

java 复制代码
@Override
public List<User> getUserList(UserQuery query) {
   // 获取参数
   String name = query.getName();
   Integer status = query.getStatus();
   // 构造查询语句
   return lambdaQuery()
         .like(name != null, User::getUsername, name)
         .eq(status != null, User::getStatus, status)
         .list();
}

4. 案例:IService的Lambda更新

需求:改造根据id修改用户余额的接口,要求如下

完成对用户状态校验

完成对用户余额校验

如果扣减后余额为0,则将用户status修改为冻结状态2

java 复制代码
@Service
public class UserServiceImpl extends ServiceImpl<User2Mapper, User> implements IUserService {
   @Override
   public void deductBalance(String id, Integer money) {
      // 查询用户
      User user = getById(id);
      // 检验用户状态
      if (user == null || user.getStatus() == 2) {
         throw new RuntimeException("用户状态异常");
      }
      // 检验余额
      if (user.getBalance() < money) {
         throw new RuntimeException("余额不足");
      }
      // 扣减余额
      int remainBalance = user.getBalance() - money;
      // 执行更新
      lambdaUpdate()
            .set(User::getBalance,remainBalance)
            .set(remainBalance==0,User::getStatus,2)
            .eq(User::getId,id)
            .update();
   }

5.IService 的批量新增

需求:批量插入10万条用户数据,并作出对比:

  • 普通for循环插入
  • IService的批量插入

①构建批量插入的方法

java 复制代码
// 批量插入的方法
private User buildUser(int i){
   User user = new User();
   user.setUsername("Wang_"+i);
   user.setPassword("123");
   user.setPhone("18688990011"+i+"");
   user.setBalance(200);
   user.setInfo("{\"age\": 26, \"intro\": \"英文老师\", \"gender\": \"female\"}");
   user.setCreateTime(LocalDateTime.now());
   user.setUpdateTime(LocalDateTime.now());
   return user;
}

②使用普通的for循环插入

java 复制代码
@Test
void testSaveOneByOne() {
   long b = System.currentTimeMillis();
   // 循环插入
   for (int i = 1; i < 100000; i++) {
      userService.save(buildUser(i));
   }
   long e = System.currentTimeMillis();
   System.out.println("耗时" + (e - b));
}

耗时:210005ms,210s

②使用批处理

java 复制代码
@Test
void testSaveBatch(){
   // 每次批量插入1000,插入100次就是10万条数据
   // 1.准备1个容量为1000的集合
   List<User> list = new ArrayList<>(1000);
   long b = System.currentTimeMillis();
   for (int i = 0; i < 100000; i++) {
      // 2.添加一个user
      list.add(buildUser(i));
      // 3.每1000条批量插入一次
      if (i % 1000 == 0){
         // 4. 批量插入
         userService.saveBatch(list);
         // 5. 清空
         list.clear();
      }
   }
   long e = System.currentTimeMillis();
   System.out.println("耗时" + (e - b));
}

耗时:26258ms,26s

性能进一步提升,配置jdbc参数,开rewriteBatchedStatements=true

总结:

  • 普通for循环逐条插入速度极差,不推荐
  • MP的批量新增,基于预编译的批处理,性能不错
  • 配置jdbc参数,开rewriteBatchedStatements=true,性能最好
相关推荐
秋野酱3 分钟前
如何在 Spring Boot 中实现自定义属性
java·数据库·spring boot
weisian15130 分钟前
Mysql--实战篇--@Transactional失效场景及避免策略(@Transactional实现原理,失效场景,内部调用问题等)
数据库·mysql
AI航海家(Ethan)34 分钟前
PostgreSQL数据库的运行机制和架构体系
数据库·postgresql·架构
Amd7941 小时前
深入探讨索引的创建与删除:提升数据库查询效率的关键技术
数据结构·sql·数据库管理·索引·性能提升·查询优化·数据检索
Kendra9193 小时前
数据库(MySQL)
数据库·mysql
时光书签4 小时前
Mongodb副本集群为什么选择3个节点不选择4个节点
数据库·mongodb·nosql
人才程序员6 小时前
【C++拓展】vs2022使用SQlite3
c语言·开发语言·数据库·c++·qt·ui·sqlite
极客先躯6 小时前
高级java每日一道面试题-2025年01月23日-数据库篇-主键与索引有什么区别 ?
java·数据库·java高级·高级面试题·选择合适的主键·谨慎创建索引·定期评估索引的有效性
指尖下的技术6 小时前
Mysql面试题----MyISAM和InnoDB的区别
数据库·mysql
永远是我的最爱7 小时前
数据库SQLite和SCADA DIAView应用教程
数据库·sqlite