MyBatisPlus详解(二)条件构造器Wrapper、自定义SQL、Service接口

文章目录

  • 前言
  • 2 核心功能
    • 2.1 条件构造器
      • 2.1.1 Wrapper
      • 2.1.2 QueryWrapper
      • 2.1.3 UpdateWrapper
      • 2.1.4 LambdaQueryWrapper
    • 2.2 自定义SQL
      • 2.2.1 基本用法
      • 2.2.2 多表关联
    • 2.3 Service接口
      • 2.3.1 IService
        • 2.3.1.1 save
        • 2.3.1.2 remove
        • 2.3.1.3 update
        • 2.3.1.4 get
        • 2.3.1.5 list
        • 2.3.1.6 count
        • 2.3.1.7 page
      • 2.3.2 基本用法

前言

MyBatisPlus详解系列文章:

MyBatisPlus详解(一)项目搭建、@TableName、@TableId、@TableField注解与常见配置

2 核心功能

2.1 条件构造器

2.1.1 Wrapper

在BaseMapper接口提供的相关方法中,除了以id作为where条件,还支持更加复杂的where条件,即条件构造器Wrapper

Wrapper是条件构造器的抽象类,其下有很多默认实现,继承关系如图:

Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:

而QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:

而UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:

2.1.2 QueryWrapper

使用QueryWrapper构建查询条件,例如:

  • select id, username, info, balance from t_user where id = 3 and username like '%o%' and balance >= 1000
@Test
public void testQueryWrapper() {// select id, username, info, balance from t_user// where id = 3 and username like '%o%' and balance >= 1000QueryWrapper<User> wrapper = new QueryWrapper<User>()// 设置需要查询的字段.select("id", "username", "info", "balance")// id = 3.eq("id", 3)// like '%o%'.like("username", "o")// balance >= 1000.ge("balance", 1000);List<User> userList = userMapper.selectList(wrapper);userList.forEach(System.out::println);
}

执行以上单元测试结果如下:

==>  Preparing: SELECT id,username,info,balance FROM t_user WHERE (id = ? AND username LIKE ? AND balance >= ?)
==> Parameters: 3(Integer), %o%(String), 1000(Integer)
<==      Total: 1
User(id=3, username=Hope, password=null, phone=null, info={"age": 25, "intro": "上进青年", "gender": "male"}, status=null, balance=20000, createTime=null, updateTime=null)
  • update t_user set balance = 2000 where username = 'Jack'
@Test
void testUpdateByQueryWrapper() {// update t_user set balance = 2000 where username = 'Jack'QueryWrapper<User> wrapper = new QueryWrapper<User>()// username = 'Jack'.eq("username", "Jack");// 2.更新数据User user = new User();user.setBalance(2000);userMapper.update(user, wrapper);
}

执行以上单元测试结果如下:

==>  Preparing: UPDATE t_user SET balance=? WHERE (username = ?)
==> Parameters: 2000(Integer), Jack(String)
<==    Updates: 0

2.1.3 UpdateWrapper

如上面的单元测试testUpdateByQueryWrapper()所示,update()方法更新时只能直接赋值,对于一些复杂的需求就难以实现,例如:

UPDATE t_user SET balance = balance - 200 WHERE id in (1, 2, 4);

SET语句的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql()方法了

@Test
void testUpdateWrapper() {List<Long> ids = Arrays.asList(1L, 2L, 4L);// 1.生成SQLUpdateWrapper<User> wrapper = new UpdateWrapper<User>()// SET balance = balance - 200.setSql("balance = balance - 200")// WHERE id in (1, 2, 4).in("id", ids);// 2.更新// 注意第一个参数为null,则会基于UpdateWrapper中的setSQL来更新userMapper.update(null, wrapper);
}

执行以上单元测试结果如下:

==>  Preparing: UPDATE t_user SET balance = balance - 200 WHERE (id IN (?,?,?))
==> Parameters: 1(Long), 2(Long), 4(Long)
<==    Updates: 2

2.1.4 LambdaQueryWrapper

无论是QueryWrapper还是UpdateWrapper,在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。

而要不写字段名,又能知道字段名,其中一种办法是基于变量的gettter方法结合反射技术。为此,MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:LambdaQueryWrapper和LambdaUpdateWrapper。

@Test
public void testLambdaQueryWrapper() {// select id, username, info, balance from t_user// where id = 3 and username like '%o%' and balance >= 1000LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()// 设置需要查询的字段.select(User::getId, User::getUsername, User::getInfo, User::getBalance)// id = 3.eq(User::getId, 3)// username like '%o%'.like(User::getUsername, "o")// balance >= 1000.ge(User::getBalance, 1000);List<User> userList = userMapper.selectList(wrapper);userList.forEach(System.out::println);
}

执行该单元测试,可以得到和上面的testQueryWrapper()一样的结果,但这种写法不需要将字段名写死。未来如果需要修改字段名,也不需要这里的逻辑代码。

2.2 自定义SQL

在单元测试testUpdateWrapper(),编写了这样一行代码:.setSql("balance = balance - 200"),这种写法其实也是不好的,因为SQL语句最好都维护在持久层,而不是业务层

为此,MyBatis提供了自定义SQL功能,即利用Wrapper构建复杂的where条件,然后自己定义SQL语句剩余的部分。

也就是说,UPDATE t_user SET balance = balance - 200自己定义,WHERE id in (1, 2, 4)利用Wrapper构建。

2.2.1 基本用法

例如单元测试testUpdateWrapper()可以这样修改:

@Test
public void testUpdateWrapper2() {List<Long> ids = Arrays.asList(1L, 2L, 4L);// 1.生成SQLUpdateWrapper<User> wrapper = new UpdateWrapper<User>()// SET balance = balance - 200// .setSql("balance = balance - 200")// WHERE id in (1, 2, 4).in("id", ids);// 2.更新// userMapper.update(null, wrapper);// 改为调用自定义的Mapper方法,直接传递WrapperuserMapper.deductBalanceByIds(200, wrapper);
}

然后在UserMapper中定义deductBalanceByIds()方法:

// com.star.learning.mapper.UserMapper@Update("UPDATE t_user SET balance = balance - #{money} ${uw.customSqlSegment}")
void deductBalanceByIds(@Param("money") int money, @Param("uw") UpdateWrapper<User> wrapper);

执行修改后的单元测试,执行结果和原来的是一样的。

2.2.2 多表关联

理论上来讲MyBatisPlus是不支持多表查询的,不过仍然可以利用Wrapper构建复杂where条件结合自定义SQL来实现多表查询的效果。

例如,要查询出所有收货地址在北京并且用户id在1、2、4之中的用户,其SQL语句为:

SELECT * FROM t_user u
INNER JOIN t_address a ON a.user_id = u.id
WHERE u.id IN (1,2,4)
AND a.city = '北京'

编写单元测试,利用Wrapper构建复杂where条件,并调用自定义的Mapper方法:

@Test
public void testMultiTableQuery() {List<Long> ids = Arrays.asList(1L, 2L, 4L);// 1.利用Wrapper构建复杂where条件QueryWrapper<User> wrapper = new QueryWrapper<User>().in("u.id", ids).eq("a.city", "北京");// 2.调用自定义的Mapper方法User user = userMapper.queryUserByIdAndCity(wrapper);System.out.println(user);
}
// com.star.learning.mapper.UserMapper@Select("SELECT * FROM t_user u INNER JOIN t_address a ON a.user_id = u.id ${ew.customSqlSegment}")
User queryUserByIdAndCity(@Param("ew") QueryWrapper<User> wrapper);

执行以上单元测试,结果如下:

==>  Preparing: SELECT * FROM t_user u INNER JOIN t_address a ON a.user_id = u.id WHERE (u.id IN (?,?,?) AND a.city = ?)
==> Parameters: 1(Long), 2(Long), 4(Long), 北京(String)
<==      Total: 1
User(id=2, username=Rose, password=123, phone=13900112223, info={"age": 19, "intro": "青涩少女", "gender": "female"}, status=1, balance=200, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T16:10:20)

2.3 Service接口

MybatisPlus不仅提供了BaseMapper接口,还提供了通用的Service接口及默认实现ServiceImpl,封装了一些常用的service模板方法。

2.3.1 IService

2.3.1.1 save

  • save:插入一条记录
  • saveBatch:批量插入多条记录
  • saveOrUpdate:记录存在则更新记录,否则插入一条新记录
  • saveOrUpdateBatch:记录存在则批量修改,否则批量插入
2.3.1.2 remove

  • remove:根据条件删除
  • removeById:根据ID删除
  • removeByIds:根据ID批量删除
  • removeByMap:根据Map中的键值对为条件删除
  • removeBatchByIds:批量删除(jdbc批量提交)
2.3.1.3 update

  • update(Wrapper<T>):根据UpdateWrapper修改,Wrapper中包含set和where部分
  • update(T,Wrapper<T>):按照T内的数据修改与Wrapper匹配到的数据
  • updateById:根据id修改
  • updateBatchById:根据id批量修改
2.3.1.4 get

  • getById:根据id查询
  • getOne(Wrapper<T>):根据Wrapper查询1条记录
  • getBaseMapper:获取对应实体的BaseMapper
  • getMap(Wrapper<T>):根据Wrapper查询1条记录,封装为Map
2.3.1.5 list

  • list():查询所有
  • list(Wrapper<T>):根据Wrapper条件查询列表
  • listByIds():根据ID集合查询列表
2.3.1.6 count

  • count():查询总记录数
  • count(Wrapper<T>):根据Wrapper条件查询总记录数
2.3.1.7 page

  • page(IPage):无条件翻页查询
  • page(IPage, Wrapper<T>):根据Wrapper条件翻页查询

2.3.2 基本用法

创建一个IUserService接口,继承IService接口以拓展方法;同时,自定义UserServiceImpl实现类,继承ServiceImpl实现类,并实现IUserService接口。

// com.star.learning.service.IUserServicepublic interface IUserService extends IService<User> {
}
// com.star.learning.service.impl.UserServiceImpl@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}

这样配置之后,导入IUserService接口,即可以使用IService接口中定义的各种方法。

接下来,实现下面这个接口:

接口请求方式请求路径请求参数返回值
新增用户POST/user/addUser

首先,导入相关依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

然后创建一个UserController类,并编写一个addUser()方法:

// com.star.learning.controller.UserController@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate IUserService userService;@PostMapping("/add")public void addUser(@RequestBody User user) {boolean save = userService.save(user);System.out.println("新增用户结果 => " + save);}
}

最后进行功能测试,调用/user/add接口,查看控制台打印信息:

==>  Preparing: INSERT INTO t_user ( username, password, phone, info, balance ) VALUES ( ?, ?, ?, ?, ? )
==> Parameters: Jim(String), 123456(String), 999(String), {"age": 20, "intro": "佛系青年", "gender": "male"}(String), 500(Integer)
<==    Updates: 1
新增用户结果 => true

接下来再来实现3个接口:

接口请求方式请求路径请求参数返回值
根据id查询用户GET/user/{id}idUser
根据id集合批量查询用户GET/user/{id}idList<User>
删除用户DELETE/user/{id}id

其代码如下:

// com.star.learning.controller.UserController@GetMapping("/{id}")
public User getById(@PathVariable("id") Long userId) {User user = userService.getById(userId);System.out.println("根据id查询用户 => " + user);return user;
}@GetMapping
public List<User> queryUserByIds(@RequestParam("ids") List<Long> ids){List<User> users = userService.listByIds(ids);System.out.println("根据id集合查询用户列表 => " + users);return users;
}@DeleteMapping("/{id}")
public void removeUserById(@PathVariable("id") Long userId){boolean remove = userService.removeById(userId);System.out.println("根据id删除用户 => " + remove);
}

调用这3个接口,查看控制台打印信息:

// 根据id查询用户
==>  Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM t_user WHERE id=?
==> Parameters: 2(Long)
<==      Total: 1
根据id查询用户 => User(id=2, username=Rose, password=123, phone=13900112223, info={"age": 19, "intro": "青涩少女", "gender": "female"}, status=1, balance=200, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T16:10:20)// 根据id集合批量查询用户
==>  Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM t_user WHERE id IN ( ? , ? , ? )
==> Parameters: 1(Long), 2(Long), 3(Long)
<==      Total: 2
根据id集合查询用户列表 => [User(id=2, username=Rose, password=123, phone=13900112223, info={"age": 19, "intro": "青涩少女", "gender": "female"}, status=1, balance=200, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T16:10:20), User(id=3, username=Hope, password=123, phone=13900112222, info={"age": 25, "intro": "上进青年", "gender": "male"}, status=1, balance=20000, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T11:12:48)]// 根据id删除用户
==>  Preparing: DELETE FROM t_user WHERE id=?
==> Parameters: 2(Long)
<==    Updates: 1
根据id删除用户 => true

可以看到,上述4个接口都直接在Controller类中即可实现,无需编写任何Service代码,非常方便。

不过,一些带有业务逻辑的接口则需要在Service中自定义实现了。例如下面这个接口:

接口请求方式请求路径请求参数返回值
根据id扣减用户余额PUT{id}/deduction/{money}id,money
// com.star.learning.controller.UserController@PutMapping("{id}/deduction/{money}")
public void deductBalance(@PathVariable("id") Long id, @PathVariable("money")Integer money){System.out.println("扣减id=" + id + "的用户的余额" + money);userService.deductBalance(id, money);
}
// com.star.learning.service.IUserServicepublic interface IUserService extends IService<User> {void deductBalance(Long userId, Integer money);
}
// com.star.learning.service.impl.UserServiceImpl@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Autowiredprivate UserMapper userMapper;@Overridepublic void deductBalance(Long userId, Integer money) {// 1.查询用户User user = getById(userId);System.out.println(user);// 2.判断用户状态if (user == null || user.getStatus() == 2) {throw new RuntimeException("用户状态异常");}// 3.判断用户余额if (user.getBalance() < money) {throw new RuntimeException("用户余额不足");}// 4.扣减余额// 2.2节自定义SQL创建了deductBalanceByIds方法可以直接使用UpdateWrapper<User> wrapper = new UpdateWrapper<User>().eq("id", userId);userMapper.deductBalanceByIds(money, wrapper);}
}

调用/user/3/deduction/200接口,查看控制台打印信息:

扣减id=3的用户的余额200
==>  Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM t_user WHERE id=?
==> Parameters: 3(Long)
<==      Total: 1
User(id=3, username=Hope, password=123, phone=13900112222, info={"age": 25, "intro": "上进青年", "gender": "male"}, status=1, balance=20000, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T11:12:48)
==>  Preparing: UPDATE t_user SET balance = balance - ? WHERE (id = ?)
==> Parameters: 200(Integer), 3(Long)
<==    Updates: 1

本节完,更多内容请查阅分类专栏:MyBatisPlus详解

感兴趣的读者还可以查阅我的另外几个专栏:

  • SpringBoot源码解读与原理分析(已完结)
  • MyBatis3源码深度解析(已完结)
  • Redis从入门到精通(已完结)
  • 再探Java为面试赋能(持续更新中…)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/638003.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PaddleOCRV4训练自己的模型(4)------模型推理及导出

一、Det模型推理&#xff1a; &#xff08;1&#xff09;上一篇文章只讲了推理的实现方法&#xff0c;没有展示结果&#xff0c;这里顺带展示一下结果。 因为训练定位模型的时候是整图训练&#xff0c;所以推理的时候也是整图推理。 &#xff08;2&#xff09;在推理的时候可以…

解决Django中调页面时出现“Did you forget to register or load this tag”报错

解决Django中调页面时出现“Did you forget to register or load this tag?”报错 1.问题收录 2.分析问题 在HTML文件中&#xff0c;{{title}}&#xff0c;{{lanyy}}&#xff0c;django 默认规定的语法&#xff0c;用{{}}包起来的变量叫做模板变量。 django渲染模板时会将大…

Linux-内存文件

1. 基础IO操作 1.1 c语言的IO接口 fopen&#xff1a;打开一个文件&#xff0c;按照指定方式 参数&#xff1a;filename 文件名&#xff0c;也可以是路径&#xff0c;mode&#xff1a;打开方式 返回打开的文件指针 fread&#xff1a;从指定流中读数据 参数&#xff1a;从FIL…

上海亚商投顾:沪指缩量调整 油气、低空经济概念逆势走强

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指4月19日震荡调整&#xff0c;深成指跌超1%&#xff0c;创业板指盘中跌超2%。周期股逆势走强&#xff0c;油…

木马——文件上传

目录 1、WebShell 2.一句话木马 靶场训练 3.蚁剑 虚拟终端 文件管理 ​编辑 数据操作 4.404.php 5.文件上传漏洞 客户端JS检测 右键查看元素&#xff0c;删除检测代码 BP拦截JPG修改为php 服务端检测 1.MIME类型检测 2.文件幻数检测 3.后缀名检测 1、WebShell W…

将记录从excel当中导出为.sql文件,再新增到数据库

一、背景 临时遇到了一个需求&#xff0c;比如根据人员的名字查询对应记录&#xff0c;看起来还是很简单的&#xff0c;直接用select查询就可以&#xff0c;然而如果此时存在以下情况&#xff1a; 数据库根本就没有人员信息表&#xff1b;------这个倒是好操作&#xff1b;现…

SpringMVC--RESTful

1. RESTful 1.1. RESTful简介 REST&#xff1a;Representational State Transfer&#xff0c;表现层资源状态转移。 RESTful是一种网络架构风格&#xff0c;它定义了如何通过网络进行数据的交互。这种风格基于HTTP协议&#xff0c;使得网络应用之间的通信变得更加简洁和高效。…

安信可 ESP_01SWIFI模块的使用 (电脑通过usb转tll模块连接wifi模块进行调试)

一&#xff1a;需要用到的模块 &#xff08;1&#xff09;安信可的ESP_01wifi模块 ESP-01是深圳安信可科技基于ESP8266芯片开发的串口wifi模块&#xff0c;模组集成了透传功能&#xff0c;即买即用&#xff0c;支持串口指令集&#xff0c;用户通过串口即可实现网络访问…

[前端]NVM管理器安装、nodejs、npm、yarn配置

NVM管理器安装、nodejs、npm、yarn配置 NVM管理器安装 nvm(Node.js version manager) 是一个命令行应用&#xff0c;可以协助您快速地 更新、安装、使用、卸载 本机的全局 node.js 版本。 nvm下载地址&#xff1a;https://github.com/coreybutler/nvm-windows/releases 1.全部…

用全连接对手写数字识别案例(附解决TensorFlow2.x没有examples问题)

数据集介绍 数据集直接调用可能出现问题&#xff0c;建议从官网直接下载下来&#xff0c;下载存在这四个文件 手写数字识别数据集下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1nqhP4yPNcqefKYs91jp9ng?pwdxe1h 提取码&#xff1a;xe1h 55000行训练数据集&a…

git简介及安装

Git | Git简介与安装 文章目录 Git | Git简介与安装一、Git简介二、Git安装Linux-centosLinux-ubuntu 一、Git简介 存在需求&#xff1a;对于一个文档&#xff0c;由于编写思路或者当前文档丢失&#xff0c;可能存在想要历史版本的需求&#xff0c;并且需要知道每个版本都修改了…

ASP.NET Core 3 高级编程(第8版) 学习笔记 04

第 19 章主要介绍 Restful Service 的相关知识。Restful Service 的核心内容是&#xff1a;&#xff08;1&#xff09;HTTP 请求或 HTTP 动词&#xff0c;用 HTTP 请求表达不同的操作&#xff0c;最好遵守惯例。&#xff08;2&#xff09;资源&#xff0c;通过 PATH 结合 paylo…