一、快速入门
1、实现crud
想实现crud,只需要下面几步
1、引入mybatis-plus的依赖
2、定义mapper
引入依赖:
MybatisPlus提供了starter,实现了自动Mybatis以及MybatisPlus的自动装配功能,坐标如下:
<dependency>
<groupId>***.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
我们自定义的Mapper只要实现了这个BaseMapper,就无需自己实现单表CRUD了。
public interface UserMapper extends BaseMapper<User>{
}
2、常见注解
@TableName @TableId @TableField
@TableName :是用于表名注解,表示实体类对应的表
@TableId:主键注解,表示实体类的注解
它支持两个属性:
@TableId(value="id",type="IdType.AUTO")
private Long id
IdType支持的类型有:AUTO:数据库主键自增长 INPUT:通过set方法自行输入
ASSIGN_ID:分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
@TableField:
成员变量名与数据库字段名不一致
成员变量名以isXXX命名,按照JavaBean的规范,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。
成员变量名与数据库名字段名一致,但与数据库关键字冲突
@TableName("user")
public class User {
@TableId
private Long id;
private String name;
@TableField(exis=false)
private Integer age;
@TableField(is_married")
private Boolean isMarried;
@TableField("`concat`")
private String concat;
}
3、总结
MybatisPlus是如何获取实现CRUD的数据库表信息
1、默认以类名驼峰转下划线为表名
2、默认把名为id字段当作主键
3、默认把变量名驼峰转下划线为表字段
MybatisPlus常见注解有
1、@TableName:指定表名称及全局配置
2、@TableId:指定id字段及相关配置
3、@TableField:指定普通字段名称及相关配置
IdType常见类型有:
AUTO、ASSIGN_ID、INPUT
使用@TableField的场景有哪些:
1、成员变量名与数据库字段名不一致
2、成员变量名以is开头,且是布尔值
3、成员变量名与数据库关键字冲突
4、成员变量名不是数据库字段
二、常见配置
MybatisPlus也支持基于yaml文件的自定义配置,详见官方文档:
使用配置 | MyBatis-Plushttps://baomidou.***/reference/
大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如:
-
实体类的别名扫描包
-
全局id类型
三、条件构造器
除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。
参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:
Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:
而QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:
UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:
1、QueryWrapper
查询:查询出名字中带o的,存款大于等于1000元的人。代码如下:
void testQueryWrapper(){
//构造条件构造器
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id","username","info","balance")
.like("username","o")
.ge("balance",1000);
//查询数据
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
更新:更新用户名为jack的用户的余额为2000。代码如下:
@Test
void testUpdateByQueryWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.eq("username","Jack");
User user = new User();
user.setBalance(2000);
userMapper.update(user,wrapper);
}
2、UpdateWrapper
基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。
例如:更新id为1,2,4的用户的余额,扣200,对应的SQL应该是:
UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)
@Test
void testUpdateWrapper(){
List<Long> ids = List.of(1L, 2L, 4L);
//生成sql
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance=balance-200")
.in("id",ids);
//更新 第一个字段为null,就是不填更新字段与数据
userMapper.update(null, wrapper);
}
3、LambdaQueryWrapper
为了解决QueryWrapper、UpdateWrapper的硬编码问题
中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。 因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:
-
LambdaQueryWrapper
-
LambdaUpdateWrapper
分别对应QueryWrapper和UpdateWrapper
@Test
void testQueryWrapper(){
//构造条件构造器
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.lambda()
.select(User::getId,User::getUsername,User::getInfo,User::getBalance)
.like(User::getUsername,"o")
.ge(User::getBalance,1000);
//查询数据
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
四、自定义SQL
@Test
void testUpdateWrapper(){
List<Long> ids = List.of(1L, 2L, 4L);
//生成sql
UpdateWrapper<User> wrapper = new UpdateWrapper<User>();
wrapper.lambda()
.setSql("balance=balance-200")
.in(User::getId,ids);
//更新 第一个字段为null,就是不填更新字段与数据
userMapper.update(null, wrapper);
}
这种写法在某些公司是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。
五、Service接口
MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。
通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类
-
save:新增 -
remove:删除 -
update:更新 -
get:查询单个结果 -
list:查询集合结果 -
count:计数 -
page:分页查询
1、CRUD
新增:
1、save 是新增单个元素
2、saveBatch 是批量新增
3、savaOrUpdate 是根据id判断,如果数据存在就是更新,否则就是新增
4、saveOrUpdateBatch 是批量的新增或修改
删除:
1、removeById:根据id删除
2、removeByIds:根据ids批量删除
3、removeByMap:根据Map中的键值对为条件删除
4、remove(Wrapper<T>):根据Wrapper条件删除
修改:
1、updateById:根据id修改
2、update(Wrapper<T>):根据UpdateWrapper修改,Wrapper中包含set和where部分
3、update(T,Wrapper<T>):按照T内的数据修改与Wrapper匹配到的数据
4、updateBatchById:根据id批量修改
List:
1、listByIds:根据id批量查询
2、list(Wrapper<T>):根据Wrapper条件查询多条数据
3、list():查询所有
Count:
1、count():统计所有数量
2、count(Wrapper<T>):统计符合Wrapper条件的数据数量
总结:
2、用法
对于简单的增删改查,我们可以之间在controller里调mp里面提供的service方法,无需写任何自定义service或mapper。
只有在我们这个业务逻辑相对复杂,需要自己写一些业务,而mp只能提供简单的增删改查,我们才需要自己写service和mapper
3、Lambda
IService中还提供了Lambda功能来简化我们的复杂查询及更新功能。我们用两个案例来学一下
@GetMapping("/list")
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUsers(UserQuery query){
// 1.组织条件
String username = query.getName();
Integer status = query.getStatus();
Integer minBalance = query.getMinBalance();
Integer maxBalance = query.getMaxBalance();
LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda()
.like(username != null, User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance);
// 2.查询用户
List<User> users = userService.list(wrapper);
// 3.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
在组织查询条件的时候,我们加入了 username != null 这样的参数,意思就是当条件成立时才会添加这个查询条件,类似Mybatis的mapper.xml文件中的<if>标签。这样就实现了动态查询条件效果了。
不过,上述条件构建的代码太麻烦了。 因此Service中对LambdaQueryWrapper和LambdaUpdateWrapper的用法进一步做了简化。我们无需自己通过new的方式来创建Wrapper,而是直接调用lambdaQuery和lambdaUpdate方法:
@GetMapping("/list")
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUsers(UserQuery query){
// 1.组织条件
String username = query.getName();
Integer status = query.getStatus();
Integer minBalance = query.getMinBalance();
Integer maxBalance = query.getMaxBalance();
// 2.查询用户
List<User> users = userService.lambdaQuery()
.like(username != null, User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.list();
// 3.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
可以发现lambdaQuery方法中除了可以构建条件,还需要在链式编程的最后添加一个list(),这是在告诉MP我们的调用结果需要是一个list集合。这里不仅可以用list(),可选的方法有:
-
.one():最多1个结果 -
.list():返回集合结果 -
.count():返回计数结果
@GetMapping("/list")
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUsers(UserQuery query){
// 1.组织条件
String username = query.getName();
Integer status = query.getStatus();
Integer minBalance = query.getMinBalance();
Integer maxBalance = query.getMaxBalance();
LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda()
.like(username != null, User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance);
// 2.查询用户
List<User> users = userService.list(wrapper);
// 3.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
进一步简化
@GetMapping("/list")
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUsers(UserQuery query){
// 1.组织条件
String username = query.getName();
Integer status = query.getStatus();
Integer minBalance = query.getMinBalance();
Integer maxBalance = query.getMaxBalance();
// 2.查询用户
List<User> users = userService.lambdaQuery()
.like(username != null, User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.list();
// 3.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}