Mybatis-plus的学习总结

目录

一、实体类的注释理解

二、配置文件、初步使用的基本流程

三、条件构造器、自定义构造器

四、IService接口用法

五、扩展功能

六、插件功能


一、实体类的注释理解

1、@TableName  用于指定表名:

数据库的名字是那个就写那个,@TableName("xxx")

@Data
@TableName("user")//指定数据库表名
public class User {

}

2、@Tableld 用来指定表中的普通字段信息

@Tableld  用来指定表中的主键字段信息这里的 type 属性可以接受不同的值,这些值定义了主键生成的策略。

 //用户id
@TableId(value = "id" ,type = IdType.AUTO)//指定主键字段名 AUTO表示自增长
private Long id;
//在id有类型的时候一定要指定自增长@TableId(value = "id" ,type = IdType.AUTO)

以下是 IdType 可能的值:
AUTO:数据库自增ID,适用于支持自增的数据库,如MySQL。
NONE:未设置主键类型,通常用于没有主键的情况。
INPUT:用户输入ID,适用于用户自定义ID。通过set方法自行输入
ASSIGN_ID:当没有指定ID时,由MyBatis Plus自动生成ID(例如,使用雪花算法)。
ASSIGN_UUID:当没有指定ID时,由MyBatis Plus自动生成UUID作为主键。
写法:

@TableId(value = "id", type = IdType.AUTO)
    private Long id;

@TableId(value = "id", type = IdType.NONE)
    private Long id;

3、@Tablefield

@Tablefield("'xxx")可以指定你所需要的数据库字段名,还可以用、单引号、双引号等...改变字体类型

    @TableField("username")//指定普通字段名
    private String username;
  // @TableField(exist = false)//指定该字段在数据库中不存在
    //用户密码
    private String password;
    //用户手机号

发生在以下情况下:
成员变量名与数据库字段名不一致
成员变量名以is开头,且是布尔值
成员变量名与数据库关键字冲突
成员变量不是数据库字段
功能:
标记普通字段:在实体类的字段上使用 @TableField 注解来标记该字段对应数据库表中的普通列。
字段映射:如果实体类字段名与数据库列名不一致,可以通过 value 属性来指定映射关系。
字段存在性:通过 exist 属性可以标记该字段是否在数据库表中存在,这在处理非数据库字段时很有用。
属性:
value:指定数据库表的列名。
exist:标记该字段是否存在于数据库表中,默认为 true。//可以自行选择true跟,false是指定该字             段在数据库中不存在
insert:标记该字段是否参与插入操作。
update:标记该字段是否参与更新操作。
el:用于支持字段值的SpEL表达式。
condition:指定字段在哪些操作下有效,如 @TableField(condition = SqlCondition.LIKE)

总结:

待定

二、配置文件、初步使用的基本流程

1、yaml文件:

#配置项
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
mapper-locations:"classpath *: /mapper/ ** / *. xml"# Mapper.xml文件地址,默认值
configuration:
map-underscore-to-camel-case: true #是否开启下划线和驼峰的映射
cache-enabled: false # 是否开启二级缓存
global-config:
db-config:
id-type: assign_id # id为雪花算法生成
update-strategy: not_null # 更新策略:只更新非空字段

2、MyBatisPlus使用的基本流程是什么?
1 引入起步依赖
2 自定义Mapper基础BaseMapper  //mapper文件继承BaseMapper不用写CRUD(增删改查)
3 在实体类上添加注解声明 表信息
4 在application.yml中根据需要添加配置

三、条件构造器、自定义构造器

1、条件构造器的基本写法,跟进阶用法

问题:

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

SELECT id, username, info,balance
FROM user
WHERE username LIKE ? AND balance >= ?

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

UPDATE user
SET balance = 2080
WHERE (username = "jack")

SQL对应代码如下:

QueryWrapper,硬代码:

@Test
void testQueryWrapper() {
// 1.构建查询条件
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like( column: "username", val: "o")
.ge( column: "balance", val: 1000);
// 2.查询
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out :: println);
}

@Test
void testUpdateByQueryWrapper() {
// 1,要更新的数据
User user = new User();
user.setBalance(2000);
//2.更新的条件
QueryWrapper<User> wrapper = new QueryWrapper<User>().eq( column: "username", val: "jack") ;
// 3.执行更新
userMapper.update(user, wrapper);

}

Lambda语法:LambdaQueryWrapper

Lambda 不能直接用硬代码,需要直接将get函数传入

one(查单个),list(查多个),page(分页查询),count(计数),exists(是否存在)

eq(等于)、ne(不等于)、gt(大于)、lt(小于)、ge(大于等于)、le(小于等于)

// 1.构建查询条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.select(User :: getId, User :: getUsername, User: :getInfo, User :: getBalance)
.like(User :: getUsername, val: "o")
.ge(User :: getBalance, val: 1000); I
// 2.查询
List<User> users = userMapper.selectList(wrapper);
users. forEach(System.out :: println);

QueryWrapper的LambdaQueryWrapper写法:

可以在QueryWrapper后面加上.lambda()来使用LambdaQueryWrapper功能

@Test
void testQueryWrapper() {
// 1.构建查询条件
QueryWrapper<User> wrapper = new QueryWrapper<User>().lambda()
.select("id", "username", "info", "balance")
.like( column: "username", val: "o")
.ge ( column: "balance", val: 1000);
// 2.查询
List<User> users = userMapper.selectList(wrapper);
users. forEach(System.out :: println);

}

条件构造器的用法总结:

QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分

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

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

2、自定义构造器

多余的操作交给Mp(MyBatisPlus)构建

演示代码:

我们可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分。

1 基于Wrapper构建where条件

List<Long> ids = List.of(1L, 2L, 4L);
int amount = 200;
// 1.构建条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(User :: getId, ids);
//2.自定义SQL方法调用
userMapper.updateBalanceByIds(wrapper, amount);

2 在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew

没有updateBalanceByIds方法需要在userMapper类里创建一个新的

void updateBalanceByIds(@Param("ew") LambdaQueryWrapper<User> wrapper, @Param("amount") int amount);

3自定义SQL,并使用Wrapper条件

xml文件里可以写SQL语句

<update id="updateBalanceByIds">
UPDATE tb_user SET balance = balance - #{amount} ${ew.customSqlSegment
</update>

小总结

ew是QueryWrapper<User>的实例,当缩写来看,用于构建查询条件,

LambdaQueryWrapper可以叫Lambdaew
QueryWrapper例子:

1.eq("name","张三"):添加一个等于条件,查询名字为“张三”的用户。

2.gt("age",18)添加一个大于条件,查询年龄大于18岁的用户。

// 创建一个QueryWrapper对象,这里用ew作为变量名
QueryWrapper<User> ew = new QueryWrapper<>();
// 添加条件,比如查询名字等于“张三”并且年龄大于18岁的用户
ew.eq("name", "张三").gt("age", 18);
// 执行查询,这里假设有一个BaseMapper<User>接口的实例userMapper
List<User> users = userMapper.selectList(ew);

上面代码,等于以下sql

SELECT * FROM user WHERE name = '张三' AND age > 18;

条件构造器、自定义构造器总结:

基于Wrapper构建where条件

在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew

自定义SQL,并使用Wrapper条件

四、IService接口用法

1、接口基本用法

使用流程如下图:

MP的Service接口使用流程是怎样的?

● 自定义Service接口,继承IService接口

public interface IUserService extends IService<User> {

}

● 自定义Service实现类,实现自定义接口,并继承Servicelmpl类

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

}

继承IService接口,继承Servicelmpl实现类后,这2类个包含简单的增删改查(CRUD)接口跟实现方法,就不需要写一些简单的增删改查(CRUD),只有碰到一些复杂的业务时才需要自定义接口

2、开发基础业务接口

导入相关依赖

<!-- swogger -- >
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
<!-- web -- >
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

创建一个controller类,注入接口

开始写CRUD(增删改查)的方法,具体见下面代码跟注释

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

private final IUserService userService;
//注入接口

//有参构造器
public UserController(IUserService userService){
this.userService = userService;
}

//第一个接口完成
@Apioperation("新增用户接口")
@PostMapping
public void saveUser(@RequestBody UserFormDTO userDTO) {
//1、把DTO导入到po
//copyProperties(userDTO,User.class)作用把userDTO复制到User.class里
User user = BeanUyil.copyProperties(userDTO,User.class);
//2、新增
userService.save(user);
}

//第二个接口
@ApiOperation("删除用户接口")
@DeleteMapping("{id}")//将PostMapping的post换成delete 改成删除接口
//接口名也要写成相对的名字 deleteUserById
public void deleteUserById(@ApiParam("用户id") @PathVariable("id") Long id){
//@ApiParam("用户id")  APL注解
//@PathVariable("id") 用于将方法参数绑定到URI模板变量的值,
//URL是/users/123,那么123将被作为id参数的值
//在userService找到删除方法,需要根据id来查询并删除它,removeById可以根据id删除
userService.removeById(id);
}

//第三个接口
@Apioperation("根据id查询用户接口")
@GetMapping("{id}")
public UserVo queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){
//查询结果不能是void要用vo,将void改成UserVo
// 1.查询用户PO
User user = userService.getById(id);//get查单个id,list查多个id
//2.把PO拷贝到VO
// 从user这个po开始拷,拷到目标类UserVo.class里
return BeanUtil.copyProperties(user, UserVo.class);
//我们查询的是po,所有我们还要做一件事情把po拷贝copy到vo类里面再return返回vo值。
}

//第四个接口
@ApiOperation("根据id批量查询用户接口")
@GetMapping//@GetMapping("id")请求,要去掉("id")因为不是查询一个id了
public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids) {
//查询多个id,因为要查询多个id要用list,所以要写成List<UserVo>,List<Long> ids
//@PathVariable注释也要换成,@RequestParam
// 1.查询用户PO,这里不用get用list查找多个用户id
List<User> users = userService. listByIds(ids);
//2.把PO拷贝到VO,用copyToList来批量拷贝
return BeanUtil.copyToList(users, Uservo.class);

}

}

3、复杂业务接口

扣减用户余额接口

//第五个接口
@Apioperation("扣减用户余额接口”)
@PutMapping("/{id}/deduction/{money}")
public void deductBalance(
@ApiParam("用户id") @PathVariable("id") Long id,
@ApiParam("扣减的金额") @PathVariable("money") Integer money) { 
userService.deductBalance(id,money);
}

没有这个方法,在接口创建这个方法

public void deductBalance(Long id, Integer money) ;

 实现接口

@override
public void deductBalance(Long id, Integer money) {
//1、查询用户
// 要调用Service,不过因为这个类本来就是Service不用调用,直接用就行
//get查单个 list查多个
  User user = getById(id);
//2、校验用户状态 ,状态:1、是正常2、是冻结
if (user == null | | user.getStatus() == 2) {
//抛出异常
throw new RuntimeException("用户状态异常!");
}
//3、校验余额是否充足,怎么看是否充足,看balance是否大于money,小于就不正常
if (user.getBalance() < money) {
//抛出异常
throw new RuntimeException("用户余额不足!");
 }
//4、扣减余额,就是修改balance字段 update(修改)
//update tb_user set balance=balance-?  这语句不建议用Mp实现
//要在业务写sql语句,建议在自定义SQL实现,自定义SQL要用UserMapper
//继承了ServiceImpl,ServiceImpl有注入 UserMapper baseMapper
baseMapper.deductBalance(id, money);
//在UserMapper中创建deductBalance这个方法

}
   

在mapper中创建方法,用注释写sql语句

public interface UserMapper extends BaseMapper<User> {

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

}

也可以在xml里写SQL语句

<update id="updateBalanceByIds">
UPDATE tb_user SET balance = balance - #{money} WHERE id = #{id}
</update>

网址:http://127.0.0.1:8080/doc.html#/home、localhost:8080/doc.html#/home  这2个都可以

通过【有道云笔记】关闭加密模式和去除验证码方便宜开发调式
https://note.youdao.com/s/MtASuhes  去测试它的CRUD(增删改查)功能

小总结:

1、对于一些简单增删改查可以直接在controller调用Mp(Mybatis-plus)提供的service方法,无需写任何的自定义service,mapper

2、Mp(Mybatis-plus)只提供基础的增删改查

3、当业务逻辑相对复杂的才需要自定义service比在里面编写业务逻辑

4、当service提供的方法不足以满足增删改查需求的时候,才需要自定义mapper,比如上面的update,大多数情况下用不上自定义mapper的。

5、第四个代码,4、扣减余额,后面有用其他方法来优化

4、IService的Lambda方法

IService的Lambda查询

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

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

status:用户状态,可以为空

minBalance:最小余额,可以为空

maxBalance:最大余额,可以为空

根据上面图和需求创建一个新的controller接口

@Apioperation("根据复杂条件查询用户接口")
@GetMapping("/list")//这个路径跟上面的不一样
public List<UserVo> queryUsers(UserQuery query) {
//图中的参数很多,需要自定义一个对象UserQuery ,去接收图里的参数,拿到对象后开始查询
// 1.查询用户PO
//
List<User> users = userService.queryUsers(query.getName(), query.getStatus(), query. getMinBalance(), query. getMaxBalance()
// 2.把PO拷贝到VO
return BeanUtil.copyToList(users, Uservo.class);

创建自定义对象UserQuer

@Data
@ApiModel(description="用户查询条件实体")
public class UserQuery {
@ApiModelProperty("用户名关键字")
private String name;
@ApiModelProperty("用户状态:1-正常,2-冻结")
private Integer status; I
@ApiModelProperty("余额最小值")
private Integer minBalance;
@ApiModelProperty("余额最大值")
private Integer maxBalance;

}

创建自定义接口

1 related problem
public interface IUserService extends IService<User> {

List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) ;|

}

实现接口

用lambdaQuery可以不用new、wrapper,包含各种各样的方法,它可以直接构造条件,比如:

one(查单个),list(查多个),page(分页查询),count(计数),exists(是否存在)

eq(等于)、ne(不等于)、gt(大于)、lt(小于)、ge(大于等于)、le(小于等于)

也可以用list,list复杂条件查、listByIds根据id查,要new、wrapper,相对而言比较麻烦



@override
public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
//List<User> users = lambdaQuery() return users  直接返回return lambdaQuery()
return lambdaQuery()
. like( condition: name != null, User :: getUsername, name)
.eq( condition: status != null, User :: getStatus, status)
.ge( condition: minBalance != null, User :: getBalance, minBalance)
. le( condition: maxBalance != null, User :: getBalance, maxBalance)
.list();//one是查单个,list查多个,page分页查询,count计数,exists是否存在

}

5、IService的Lambda更新

需求:改造根据id修改用户余额的接口,要求如下
1.完成对用户状态校验
2.完成对用户余额校验

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

更改,四、IService接口用法、3、复杂业务接口,这个目录下的代码:4. 扣减余额

把BaseMapper改成lambda,两者相比之下lambda处理起来比较简单

@Override
@Transactional//事务
public void deductBalance(Long id, Integer money) {
// 1.查询用户
User user = getById(id);
// 2.校验用户状态
if (user == null | | user.getStatus() == 2) {
throw new RuntimeException("用户状态异常!");
}
// 3.校验余额是否充足
if (user.getBalance() < money) {
throw new RuntimeException("用户余额不足!");
}
//4、扣减余额,就是修改balance字段 update(修改)
int remainBalance = user.getBalance() - money;
lambdaUpdate()
.set(User :: getBalance, remainBalance)
.set ( condition: remainBalance == e, User :: getStatus, val: 2)
.eq(User :: getId, id)
.eq(User :: getBalance, user.getBalance()) // 乐观锁
.update();

}

    第4个,4、(更改部分)扣减余额,就是修改balance字段 update(修改)

//update tb_user set balance=balance-?  这语句不建议用Mp实现
//要在业务写sql语句,建议在自定义SQL实现,自定义SQL要用UserMapper
//继承了ServiceImpl,ServiceImpl有注入 UserMapper baseMapper
// baseMapper.deductBalance(id, money);
//余额以经知道了,剩余余额=余额-money
int remainBalance = user.getBalance() - money;
lambdaUpdate()
///相当于这个这句sql ,update tb_user set balance=balance-money,更新余额
.set(User :: getBalance, remainBalance)
//余额扣成0了才改成2
//添加一个if条件语句 若为0就把(2.校验用户状态)getStatus的值改成2,否则就不执行
.set ( condition: remainBalance == e, User :: getStatus, val: 2)
//where根据id查,条件是id,将id传入,
.eq(User :: getId, id)
//上面三句是在构造sql语句

//用户的余额必须等于更新时查的那个getBalance,条件
//再去做set,否则就是被别人改过了,等于
//更新时不等于user.getBalance时,条件不成立,就会执行失败
.eq(User :: getBalance, user.getBalance()) // 乐观锁
//执行语句update,不加update这条代码不会执行,需要注意一下
.update();

代码小总结:

待定

6、IService批量新增

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

普通for循环插入:

private User buildUser(int i) {
User user = new User();
user.setUsername("user_" + i);
user.setPassword("123");
user.setPhone(""+(18688190000L +i));
user.setBalance(2000);
user.setInfo("{\"age\": 24, \"intro\": \"英语老师\",\"gender\":\"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(user.getCreateTime());
return user;
}

@Test
void testSaveOneByOne() {
long b = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
userService.save(buildUse>(i));
}
long e = System.currentTimeMillis();
System.out.println("耗时: "+ (e -b));

}

IService的批量插入:

在普通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));

}
@Test
void testSaveBatch() {
// 我们每次批量插入1000条件,插入100次即10万条数据

// 1.准备一个容量为1000的集合
List<User> list = new ArrayList<>( initialCapacity: 1000);
long b = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
// 2.添加一个user
//把一千条sql语句打包,插入mysql
list.add(buildUser(i));
// 3.每1000条批量插入一次
if (1 % 1000 == 0) {
//saveBatch的时候,依次提交给mysql
userService.saveBatch(list);
// 4.清空集合,准备下一批数据
list.clear();
}

把sql语句变成这样子,mabatis动态sql

五、扩展功能

1、代码生成器

需要下载2个插件:

MyBatisX :便于XML映射跳转、生成Java 实体类、Mapper 接口及 XML 映射文件

MyBatisPlus:实现数据库的物理分页,记录分析和优化 SQL 性能,能根据具体需求定制和扩展功能

如图


2、DB静态工具

1增删改查(CRUD)

增加

常用: save新增 、saveBatch批量新增、saveBatchOrUpdate增或改

删除

常用:removeById根据id删除、removeByIds根据id批量删除

修改

查询

get查单个

list查多个

count查数量

page分页查询

便捷查询,便捷修改

2 DB静态工具代码展示

创建一个Address、AddressVo实体类,来调用里面的属性

 在原来的UserVo里添加属性addresses,拿到AddressVO列表

@ApiModelProperty("用户的收获地址")
private List<AddressVO> addresses;

controller

@ApiOperation("根据id查询用户接口”)
@GetMapping("{id}")
public UserVo queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){
return userService.queryUserAndAddressById(id);
}

 接口

public UserVo queryUserAndAddressById(Long id);

实现类 

Db. lambdaQuery便捷查询

@Override
public UserVo queryUserAndAddressById(Long id) {
//1.查询用户
User user = getById(id);
if (user == null | | user.getStatus() == 2) {
throw new RuntimeException(“用户状态异常!”);
}
// 2.查询地址
//Db. lambdaQuery便捷查询
List<Address> addresses = Db. lambdaQuery(Address.class).eq(Address :: getUserId, id).list();
//3.封装VO
// 3.1.转User的PO为VO
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
// 3.2.转地址VO
//isEmpty()检查一个字符串或集合是否为空,没有值的话就返回
//isNotEmpty()检查一个字符串或集合是否不为空,有值的话就返回
if (CollUtil.isNotEmpty(addresses)) {
userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
}
return userVO;
}

DB 代码小总结:

依赖注入(DI):依赖注入是一种设计模式,它允许创建可重用、可测试和松耦合的代码

静态DB访问:静态DB访问通常指的是通过静态方法或属性直接访问数据库

依赖注入通常是更好的选择,因为它提供了更好的代码管理和扩展性,静态DB访问可能在特定情况下更快或更简单,但它会带来长期的维护和测试问题。在大多数情况下,应该优先考虑使用依赖注入

代码优化

@override
public List<UserVo> queryUserAndAddressByIds(List<Long> ids) {
// 1.查询用户
List<User> users = listByIds(ids);
//isEmpty(users)当users为空的话返回空集合
if (CollUtil.isEmpty(users)) {
return Collections.emptyList();//空集合
//不为空有值就往下面走,否则就返回空集合不往下走
}
// 2.查询地址
// 2.1.获取用户id集合,用list集合、stream流来实现
List<Long> userIds = users.stream().map(User :: getId).collect(Collectors.toList());
//users.stream()把users转化成stream流
//User :: getId是一个方法引用,它等同于user -> user.getId()
//每个User对象映射到它的getId()方法返回的值上是个方法引用,把users转成id
//collect(Collectors.toList())这是一个终端操作,它将流中的元素收集到一个新的列表中
//Collectors.toList(),它会返回一个包含流中所有元素的List

// 2.2.根据用户id查询地址
List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds) .list();
//Db.lambdaQuery便捷查询Address.class,因为id不止一个用in
//通过Address::getUserId,Address找到Userid,userIds传给它,结果不止一个用list返回
// 2.3.转换地址VO
List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
//addresses转化AddressVO

//2.4.用户地址集合分组处理,相同用户的放入一个集合(组)中,分类整理,手动分组
Map<Long, List<AddressVO>> addressMap = new HashMap<>( initialCapacity: 0);//空集合
if(CollUtil.isNotEmpty(addressVOList)) {
addressMap = addressVOList.stream()
  .collect(Collectors.groupingBy(AddressVO :: getUserId));
}
//addressVOList.stream()先转化成stream流,然后Collectors.groupingBy 分组
//(AddressVO :: getUserId))表示我用用户id做分组
//Map<Long, List<AddressVO>>,表示Map<id, List<地址>>
// 3.转换VO返回
List<UserVO> list = new ArrayList<>(users.size());
//users.size()提前定义为users集合的大小
for (User user : users) {
// 3.1.转换User的PO为vo
UserVO vo = BeanUtil.copyProperties(user, UserVo.class);
list.add(vo);
// 3.2.转换地址VO
vo.setAddresses(addressMap.get(user.getId()));
//addressMap.get(user.getId())先根据 ID 来获取 addressMap 中的地址信息。
//然后再将这个获取到的地址信息设置给 ID 对应的 vo 对象
return list;

}

优化代码小总结: 

users.stream()把users转化成stream流

(user::getId) 每个User对象映射到它的getId()方法返回的值上是个方法引用,把users转成id

collect(Collectors.toList())这是一个终端操作,它将流中的元素收集到一个新的列表中
Collectors.toList(),它会返回一个包含流中所有元素的List

 3 逻辑删除

逻辑删除

MybatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:

mybatis-plus:
 global-config:
  db-config:
   logic-delete-field: flag # 全局逻辑删除的实体字段名,字段类型可以是boolean、integer
   logic-delete-value: 1 # 逻辑已删除值(默认为 1)
   logic-not-delete-value:0 # 逻辑未删除值(默认为 0)

 运行下面代码

@SpringBootTest
class IAddressServiceTest {

@Autowired
private IAddressService addressService;

@Test
void testLogicDelete() {
// 1.删除
addressService.removeById(59L);

// 2.查询
Address address = addressService.getById(59L);
System.out.println("address = " + address);
}

}

如果运行后没有sql语句,是日志的级别不对,要把info改成debug级别


logging:
  level:
   com. itheima: debug //info
pattern:
  dateformat: HH:mm:ss

 注意:

逻辑删除本身也有自己的问题,比如:
会导致数据库表垃圾数据越来越多,影响查询效率
SQL中全都需要对逻辑删除字段做判断,影响查询效率
因此,我不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。

4 枚举处理器

以前正常user类

详细信息
private String info;
使用状态(1正常2冻结)
private Integer status;

 枚举类,

@Getter
public enum UserStatus {
NORMAL(1,"正常"),
FREEZE(2,"冻结")
;

private final int value;//数字
private final String desc;//内容

UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
    }
}

因为有枚举UserStatus来表达状态,所有直接拿来用就好了,可以用status是否等于NORMAL、FREEZE,来比较

//直接用
private UserStatus status;

数据库的status是int类型的

认识一些要用到处理器,看看就好不用可以去记

 在对应数字字段,前加一个注解@EnumValue,再配置枚举处理器:

@EnumValue
private final int value;
private final String desc;

通用枚举,在application.yml中配置全局枚举处理器:

mybatis-plus:
  configuration:
   default-enum-type-handler: com.baogidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

原来的业务,进行了上面的操作后,原来的判断状况会报错,需要改成UserStatus.FROZEN(代表2,冻结)、UserStatus.NORMAL(代表1,正常)

// 2,校验用户状态
if (user == null | | user.getStatus() = 2) {
throw new RuntimeException("用户状态异常!");
}

int remainBalance = user.getBalance() - money;
lambdaUpdate()
.set(User :: getBalance, remainBalance)
//状态赋值
.set ( condition: remainBalance == 0, User :: getStatus, val: 2) 
.eq(User :: getId, id)
.eq(User :: getBalance, user.getBalance()) //乐观锁
.update();

 使用枚举后的业务

// 2,校验用户状态
if (user == null | | user.getStatus() = UserStatus.FROZEN) {
throw new RuntimeException("用户状态异常!");
}


int remainBalance = user.getBalance() - money;
lambdaUpdate()
.set(User :: getBalance, remainBalance)
//set的状态赋值也要改
.set ( condition: remainBalance == 0, User :: getStatus, UserStatus.FROZEN) 
.eq(User :: getId, id)
.eq(User :: getBalance, user.getBalance()) //乐观锁
.update();

前端返回,@JsonValue标记枚举的值并把它返回,

@EnumValue
@JsonValue
private final int value;
@JsonValue
private final String desc;

如何实现PO类中的枚举类型变量与数据库字段的转换?

1 给枚举中的与数据库对应value值添加@EnumValue注解

@EnumValue
private final int value;
@JsonValue
private final String desc;

2 在配置文件中配置统一的枚举处理器,实现类型转换

mybatis-plus:
 configuration:
   default-enum-type-handler: com.baogidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

5 JSON处理器

认识JSON处理器

 user表中有一个JSON类型的字段

 要怎么让String 类转换成JSON类

private String info;

创建一个新的实体类UserInfo

@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class UserInfo {
private Integer age;
private String intro;
private String gender;

}

 将String改成UserInfo 

运用注解@TableField去定义它的类型,选择类型处理器JacksonTypeHandler,就是认识图的最后三个就是,但是推荐用JacksonTypeHandler,autoResultMap = true会告诉 MyBatis-Plus 自动创建结果映射

@Data
@TableName(value = "user", autoResultMap = true)//映射
public class User {
//详细信息
//运用注解@TableField去定义它的类型,选择类型处理器JacksonTypeHandler
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
}

 将原来的代码修正一下UserInfo.of

User user = new User();
// user.setId(5L);
user.setUsername("ErGouZi") ;
user.setPassword("123");
user.setPhone("18688990013");
user.setBalance(200);
///user.setInfo("{\"age\": 24, \"intro\"英语老师",\"gender\": \"female\"]");
user.setInfo(UserInfo.of( 24, "英语老师", "female"));
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
userMapper. insert(user);

六、插件功能

1分页插件基本用法

首先,要在配置类中注册MyBatisPlus的核心插件,同时添加分页插件:

@Configuration
public class MybatisConfig {

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 1.初始化核心插件
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor ();
// 2.添加分页插件
PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor (DbType.MYSQL) ;
pageInterceptor.setMaxLimit(1000L);//设置分页上限
interceptor. addInner Interceptor (pageInterceptor);
return interceptor;
   }
}

 接着,就可以是分页API了,Wrapper 条件,

 page(page, Wrapper<T> queryWrapper),page包含参数跟结果,外面的page参数传入到里面的Page进行分页查询,再从里返回结果出来

翻页查询
Params:page-翻页对象
    queryWrapper-实体对象封装操作类com.baomidou.
    mybatisplus.core.conditions.query.
    QueryWrapper
default <extends IPage<T>> page(page, Wrapper<T> queryWrapper) {
return getBaseMapper ().selectPage(page, queryWrapper);
}
无条件翻页查询
Params: page-翻页对象
See Also: Wrappers. emptyWrapper ()
default < extends IPage<T>> page( page) {
return page(page, Wrappers.emptyWrapper ());

}

IPage子类,用的最多是Page,Page里面包含着分页参数,比如页码,每页大小,等等

 例子:

@Test
void testPageQuery() {
// 1.查询
int pageNo = 1, pageSize = 5;
// 1.1.分页参数
Page<User> page = Page.of(pageNo, pageSize);
// 1.2.排序参数,通过OrderItem来指定
page. addOrder (new OrderItem("balance", false));
// 1.3.分页查询
Page<User> p = userService.page(page);
// 2.总条数
System.out.println("total = " + p.getTotal());
// 3.总页数
System.out.println("pages = " + p.getPages());
// 4.分页数据
List<User> records = p.getRecords();
records. forEach(System.out :: println);
}

 创建config类,自定义一个插件

@Configuration
public class MyBatisConfig {

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor () {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor ();
// 1.创建分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(1000L);
// 2.添加分页插件
interceptor. addInnerInterceptor (paginationInnerInterceptor); 
return interceptor;
   }
}

测试类

@Test
void testPageQuery() {
int pageNo = 1, pageSize = 2;
// 1.准备分页条件
// 1.1.分页条件
Page<User> page = Page.of(pageNo, pageSize);
//1.2.排序条件
page. addorder (new OrderItem( column: "balance", asc: true)) ;
page. addorder(new OrderItem( column: "id", asc: true)) ;

// 2.分页查询
Page<User> p = userService.page(page);

// 3.解析
long total = p.getTotal();
System.out.println("total = " + total);
long pages = p.getPages();
System.out.println("pages = " + pages);
List<User> users = p.getRecords();
users. forEach(System.out :: println);
}

2通用分页实体

写一个统一分页的实体类

@Data
@ApiModel(description="分页查询实体")
public class PageQuery{
@ApiModelProperty("页码")
private Integer pageNo;
@ApiModelProperty("页码")
private Integer pageSize;
@ApiModelProperty("排序字段")
private String sortBy;
@ApiModelProperty("是否升序”)
private Boolean isAsc;
}

用户查询条件实体继承了分页的PageQuery 

@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(description="用户查询条件实体")
public class UserQuery extends PageQuery {
@ApiModelProperty("用户名关健字")
private String name;
@ApiModelProperty("用状态:1-正常,2-冻结")
private Integer status;
@ApiModelProperty("余额最小值")
private Integer minBalance;
@ApiModelProperty("余额最大值")
private Integer maxBalance;
}

写个DTO

@Data
@ApiModel(description="分页结果")
public class PageDTO<T> {
@ApiModelProperty("总页数")
private Integer total;
@ApiModelProperty("总条数")
private Integer pages;
@ApiModelProperty("集合")
private List<T> list;
}

 controller

@ApiOperation(“根据条件分页查询用户接口”)
@GetMapping("/page")
public PageDTO<UserVO> queryUsersPage(UserQuery query){
return userService.queryUsersPage(query) ;
}

接口

PageDTO<UserVO> queryUsersPage(UserQuery query);

实现接口

@override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
String name = query.getName();
Integer status = query.getStatus();
// 1.构建分页条件
// 1.1.分页条件
Page<User> page = Page.of(query.getPageNo(), query.getPageSize());
// 1.2.排序条件
if(StrUtil.isNotBlank(query.getSortBy())){
// 不为空
page.addorder(new OrderItem(query.getSortBy(), query.getIsAsc()));
}else{
// 为空,默认按照更新时间排序
page.addorder (new OrderItem( column: "update_time", asc: false));

// 2.分页查询
Page<User> p = lambdaQuery()
. like( condition: name != null, User :: getUsername, name)
.eq ( condition: status != null, User :: getStatus, status)
.page (page) ;
// 3.封装VO结果
PageDTO<UserVO> dto = new PageDTO<>();
// 3.1.总条数
dto.setTotal(p.getTotal());
//3.2.总页数
dto.setPages(p.getPages());
//3.3,当前页数据
List<User> records = p.getRecords();
if (CollUtil.isEmpty(records)) {
dto.setList(Collections.emptyList());
//没有为空时返回dto,不为空时在下面转化成vo
return dto;
}
// 3.4.拷贝user的vO
//List<UserVOT vos = BeanUtil.copyToList(records, UserVo.class);
dto.setList(BeanUtil.copyToList(records, UserVo.class));
// 4.返回
return dto;
}

3通用分页实体与MP转化

通用分页实体

需求:

在PageQuery中定义方法,将PageQuery对象转为MyBatisPlus中的Page对象
在PageDTO中定义方法,将MyBatisPlus中的Page结果转为PageDTO结果

@Data
@ApiModel(description="分页查询实体")
public class PageQuery {
@ApiModelProperty("页码")
private Integer pageNo = 1;
@ApiModelProperty("页码")
private Integer pageSize = 5;
@ApiModelProperty("排序字段")
private String sortBy;
@ApiModelProperty("是否升序")
private Boolean isAsc = true;

public <T> Page<T> toMpPage(OrderItem ... items) {
// 1.分页条件
Page<User> page = Page.of(pageNo, pageSize);
// 2.排序条件
if(StrUtil.isNotBlank(sortBy)) {
// 不为空
page. addOrder (new OrderItem(sortBy, isAsc));
}else if(items!=null){
// 为空,默认排序,由OrderItem ... items输入的方法决定
page.addOrder (new OrderItem(items);
      }
   }
public <T> Page<T> toMpPageDefaultSortByCreateTime(){
return toMpPage(new OrderItem( column: "create_time", asc: false));
}

public <T> Page<T> toMpPageDefaultSortByUpdateTime(){
return toMpPage(new OrderItem( column: "update_time", asc: false) );
}
}

直接调用里面的方法

//1.构建分页条件
Page<User> page = query.toMpPageDefaultSortByUpdateTime(); 

 将上面的2通用分页实体实现类的封装VO结果业务,写到实体类里,直接调用

@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(description="用户查询条件实体")
public class UserQuery extends PageQuery {
@ApiModelProperty("用户名关健字")
private String name;
@ApiModelProperty("用状态:1-正常,2-冻结")
private Integer status;
@ApiModelProperty("余额最小值")
private Integer minBalance;
@ApiModelProperty("余额最大值")
private Integer maxBalance;
//封装VO结果
public static <PO, VO> PageDTO<VO> of(Page<PO> p, Class<VO> clazz){
PageDTO<VO> dto = new PageDTO<>();
// 1.总条数
dto.setTotal(p.getTotal());
// 2.总页数
dto.setPages(p.getPages());
// 3.当前页数据
List<PO> records = p.getRecords();
if (CollUtil.isEmpty(records)) {
dto.setList(Collections.emptyList());
return dto;
}
// 4.拷贝user的vo
dto.setList(BeanUtil.copyToList(records, clazz));
// 5.返回
return dto;
}
}

直接调用

// 3.封装VO结果
return PageDTO.of(p, UserVO.class);

修改后

@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
String name = query.getName();
Integer status = query.getStatus();
// 1.构建分页条件
Page<User> page = query.toMpPageDefaultSortByUpdateTime();
// 2.分页查询
Page<User> p = lambdaQuery()
. like( condition: name != null, User :: getUsername, name)
.eq( condition: status != null, User :: getStatus, status)
.page (page) ;
// 3.封装VO结果
return PageDTO.of(p, UserVo.class);
}

### MyBatis-Plus 的 Maven 配置方法 为了在 Maven 中配置 MyBatis-Plus,可以按照以下方式操作: #### 1. 添加 MyBatis-Plus 依赖 在项目的 `pom.xml` 文件中添加 MyBatis-Plus 的 Maven 依赖项。以下是具体的依赖代码[^3]: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.1.0</version> </dependency> ``` 此部分代码指定了 MyBatis-Plus 的 groupId、artifactId 和 version。 #### 2. 确保 Maven 已正确配置 在配置 MyBatis-Plus 之前,需确认 Maven 的全局设置已正确完成。具体步骤如下[^1]: - 打开 IDE 设置路径:`File -> Settings -> Build, Execution, Deployment -> Build Tools -> Maven`。 - 配置 Maven Home Path 至本地 Maven 安装目录,例如:`D:\maven\apache-maven-3.6.3`。 - 配置 User Settings File 路径至 `settings.xml` 文件位置,例如:`D:\maven\apache-maven-3.6.3\conf\settings.xml`。 - 如果有自定义的远程仓库地址,则可以在 `settings.xml` 文件中指定镜像源。 #### 3. 整合 Spring Boot 项目中的 MyBatis-Plus 如果是在 Spring Boot 项目中集成 MyBatis-Plus,还需要额外引入 MySQL 数据库驱动和其他必要的依赖项[^5]。完整的 `pom.xml` 可能类似于以下内容: ```xml <dependencies> <!-- MyBatis-Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> <!-- MySQL Driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- Spring Boot Starter Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> ``` 上述代码片段展示了如何通过 Maven 引入 MyBatis-Plus 并与其他组件协同工作。 #### 4. 使用 mvn 命令验证环境 完成以上配置后,在终端输入以下命令来验证 Maven 是否正常运行以及依赖是否成功加载[^2]: ```bash mvn -v ``` 该命令会显示当前使用的 Maven 版本及其配置详情。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值