一、条件构造器
1.MyBatis支持各种复杂的where条件,满足开发的需求
Wrapper是条件构造器,构建复杂的where查询
AbstractWrapper有构造where条件的所有方法,QueryWrapper继承后并有自己的select指定查询字段。UpdateWrapper有指定更新的字段的方法
2.案例:基于QueryWrapper查询
①查询出名字带“o”的,存款大于等于1000元的人的id,username,info,balance字段
@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方法
@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
@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
@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条件
@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
void updateBalanceByIds(@Param("ew") LambdaQueryWrapper<User> wrapper, @Param("amount")int amount);
③编写sql语句,进行where拼接
<update id="updateBalanceByIds">
update tb_user set balance = balance - #{amount} ${ew.customSqlSegment}
</update>
三、Service接口
1.IService接口的方法
2.IService接口的继承
①自定义Service接口继承IService接口
②自定义Service实现类,实现自定义接口并继承ServiceImpl类
③新增用户案例
@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
@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
@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的批量插入
①构建批量插入的方法
// 批量插入的方法
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循环插入
@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
②使用批处理
@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,性能最好