MybatisPlus(MP)基础知识

MP

BaseMapper

为了简化单表CRUD,MybatisPlus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD,因此我们自定义的Mapper只要继承了这个BaseMapper,就无需自己实现单表CRUD了。
在这里插入图片描述

package com.itheima.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;

public interface UserMapper extends BaseMapper<User> {
}

service :


@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void testInsert() {
        User user = new User();
        user.setId(5L);
        user.setUsername("Lucy");
        user.setPassword("123");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userMapper.insert(user);
    }

    @Test
    void testSelectById() {
        User user = userMapper.selectById(5L);
        System.out.println("user = " + user);
    }
}

常用注解

UserMapper在继承BaseMapper的时候指定了一个泛型:泛型中的User就是与数据库对应的PO.
MP基于反射拿到实体类对应的属性。
MybatisPlus就是根据PO实体的信息来推断出表的信息,从而生成SQL的。默认情况下:

  • MybatisPlus会把PO实体的类名驼峰转下划线作为表名
  • MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型
  • MybatisPlus会把名为id的字段作为主键
    但很多情况下,默认的实现与实际场景不符,因此MybatisPlus提供了一些注解便于我们声明表信息。

@TableName指定表名
@TableId指定主键
@TableField普通字段
在这里插入图片描述

需要用@TableField的情况:
is开头的字段名会自动去掉is
和关键字重名
不是数据库字段

常见配置

在这里插入图片描述
classpath*:/mapper/**/*.xml,也就是说我们只要把mapper.xml文件放置这个目录下就一定会被加载。

核心功能

条件构造器

除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。
MP基于basemapper来实现CURD,判断条件基于wrapper

(1)QueryWrapper

查询:查询出名字中带o的,存款大于等于1000元的人

@Autowired
private UserMapper userMapper;
@Test
void testQueryWrapper() {
    // 1.构建查询条件 where name like "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<User>()//指定泛型User是为了确保在构建查询条件时,针对的是 User 类型的数据,提高代码的类型安全性和可读性
            .select("id", "username", "info", "balance")
            .like("username", "o")//模糊查询
            .ge("balance", 1000);//>=
    // 2.查询数据
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

更新:更新用户名为jack的用户的余额为2000,代码如下:

@Test
void testUpdateByQueryWrapper() {
	//1.要更新的数据
	User user = new User();
    user.setBalance(2000);
    // 2.构建查询条件 where name = "Jack"
    QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
    // 3.更新数据,user中非null字段都会作为set语句
    userMapper.update(user, wrapper);
}

(2)UpdateWrapper

更新id为1,2,4的用户的余额,扣200,对应的SQL应该是:

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

SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了,
前面的是直接设置2000,而这里是根据现有的balance去减200

@Test
void testUpdateWrapper() {
    List<Long> ids = List.of(1L, 2L, 4L);
    // 1.生成SQL
    UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
            .setSql("balance = balance - 200") // SET balance = balance - 200 手写sql
            .in("id", ids); // WHERE id in (1, 2, 4)
        // 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
    // 而是基于UpdateWrapper中的setSQL来更新
    userMapper.update(null, wrapper);
}

(3)LambdaQueryWrapper

为了避免硬编码。
其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MP,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。
因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:

  • LambdaQueryWrapper——也可以写作QueryWrapper.lambda()
  • LambdaUpdateWrapper
    分别对应QueryWrapper和UpdateWrapper
@Test
void testLambdaQueryWrapper() {
    // 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.lambda()
            .select(User::getId, User::getUsername, User::getInfo, User::getBalance)//传字段对应的get函数
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000);
    // 2.查询
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

(4)自定义SQL

MybatisPlus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml编写SQL。

自定义SQL使用:
1、先用Wrapper构建where条件
2、再调用Mapper层自定义的方法,将构建好的where条件和sql需要的参数传进去

就是把wrapper传到自定义的方法中,wrapper中的条件使用特殊占位符标在SQL注解中,并且方法参数要有@Param(“ew”)或者 @Param(Constants.WAPPER)

@Test
void testCustomWrapper() {
    // 1.准备自定义查询条件
    List<Long> ids = List.of(1L, 2L, 4L);
    QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);

    // 2.调用mapper的自定义方法,直接传递Wrapper
    userMapper.deductBalanceByIds(200, wrapper);
}

Mapper里

package com.itheima.mp.mapper;

public interface UserMapper extends BaseMapper<User> {
    @Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
    void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper);//用@Param注解声明wrapper变量名称,必须为ew的
}

Service接口

MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。
通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:

  • save:新增
  • remove:删除
  • update:更新
  • get:查询单个结果
  • list:查询集合结果
  • count:计数
  • page:分页查询

由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口。
UserServiceImpl不能多继承,但通过IUserService接口达到同时集成IServiceServiceImpl的目的。

(1)基本用法:
在这里插入图片描述
(2)
在这里插入图片描述

一般

package com.itheima.mp.service;

public interface IUserService extends IService<User> {
    // 拓展自定义方法
}
package com.itheima.mp.service.impl;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements IUserService {
// <UserMapper, User> 第一个泛型为这个Service要用到的Mapper(因为将来在Service中要调用Mapper层的方法,不可能自己再实现一遍),第二个泛型为操作的实体类。
}

虽然没用上UserServiceImpl的方法,但是需要写。

在实现接口继承ServiceImpl后
按照Restful风格编写Controller接口方法:

package com.itheima.mp.controller;

@Api(tags = "用户管理接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("users")
public class UserController {

    private final IUserService userService;//注入

    @PostMapping
    @ApiOperation("新增用户")
    public void saveUser(@RequestBody UserFormDTO userFormDTO){
        // 1.转换DTO为PO
        User user = BeanUtil.copyProperties(userFormDTO, User.class);
        // 2.新增
        userService.save(user);
    }

    @DeleteMapping("/{id}")
    @ApiOperation("删除用户")
    public void removeUserById(@PathVariable("id") Long userId){
        userService.removeById(userId);
    }

    @GetMapping("/{id}")
    @ApiOperation("根据id查询用户")
    public UserVO queryUserById(@PathVariable("id") Long userId){
        // 1.查询用户
        User user = userService.getById(userId);
        // 2.处理vo
        return BeanUtil.copyProperties(user, UserVO.class);
    }

    @GetMapping
    @ApiOperation("根据id集合查询用户")
    public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids){
        // 1.查询用户
        List<User> users = userService.listByIds(ids);
        // 2.处理vo
        return BeanUtil.copyToList(users, UserVO.class);
    }
}

上述接口都直接在controller即可实现,无需编写任何service代码,非常方便。

添加业务逻辑

带有业务逻辑的接口则需要在service中自定义实现了

e.g.:根据id扣减用户余额
这看起来是个简单修改功能,只要修改用户余额即可。但这个业务包含一些业务逻辑处理:

  • 判断用户状态是否正常
  • 判断用户余额是否充足

业务逻辑都要在service层来做,另外更新余额需要自定义SQL,要在mapper中来实现。因此,我们除了要编写controller以外,具体的业务还要在service和mapper中编写。

Controller:

private final IUserService userService;//注入
@PutMapping("{id}/deduction/{money}")
@ApiOperation("扣减用户余额")
public void deductBalance(@PathVariable("id") Long id, @PathVariable("money")Integer money){
    userService.deductBalance(id, money);
}
package com.itheima.mp.service;

public interface IUserService extends IService<User> {
    void deductBalance(Long id, Integer money);
}
package com.itheima.mp.service.impl;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Override
    public void deductBalance(Long id, Integer money) {
        // 1.查询用户——调userservice的getById来查,因为已经继承了,直接用getById就行
        User user = getById(id);
        // 2.判断用户状态
        if (user == null || user.getStatus() == 2) {
            throw new RuntimeException("用户状态异常");
        }
        // 3.判断用户余额
        if (user.getBalance() < money) {
            throw new RuntimeException("用户余额不足");
        }
        // 4.扣减余额—— update tb_user  set money = money - ?   ———— 用自定义的sql写 需要usermapper
        baseMapper.deductMoneyById(id, money);//ServiceImpl已经注入了badeMapper(泛型指定为userMapper),所以可以直接调用mapper无需注入。Iservice调用的crud都是通过BaseMapper的crud方法实现的
    }
}

Mapper:

@Update("UPDATE user SET balance = balance - #{money} WHERE id = #{id}")
void deductMoneyById(@Param("id") Long id, @Param("money") Integer money);

总结

简单的方法直接在自己的Mapper里继承BaseMapper,就无需自己实现单表CRUD
业务逻辑相对复杂,需要自己写业务的时候用自定义Service,利用Wrapper生成查询条件,再结合Mapper.xml编写SQL

简单的方法在Controller里直接调Service方法(e.g. userService.save(user)
业务逻辑相对复杂,需要自己写业务的时候用自定义Service并传参进去 userService.deductBalance(id, money)并在UserServiceImpl实现业务逻辑。

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值