Mybatisplus基础入门笔记
一、Mybatisplus
1、定义
- mybatisplus用于解决对单表的增删改查,这些不需要业务处理的简单代码的代码集成。
2、使用
-
我们只需要在mapper接口去继承BaseMappper,在给一个要操作的实体类型就可以自动生成Mapper相应的增删改查,insert、delete、update、select。
-
相应的service层也提供了两个供我们继承的的接口以及实现类,Iservice和ServiceImpl<userMapper,User>。可以自动生成Service相应的增删改查,save、remove、update。获取有分为list、get。还有lambdaQuery和lambdaUpdate。
-
在继承的Service中自动注入了一个baseMapper,他就是我们在指定范型时传入的userMapper,所以我门可以在xxxSerciceImpl中使用baseMapper就可以调用对应的Mapper,当然想要调用其他xxxMapper就必须手动注入了。
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
public interface IUserService extends IService<User> {
}
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
/**
* 删除一个用户
* @param id
*/
public void deleteById(Long id) {
baseMapper.deleteById(id);
}
}
3、条件构造器
-
使用类型分为四个LambdaUpdateWrapper、LambdaQueryWrapper、UpdateWrapper、QueryWrapper
-
用法:
QueryWrapper
和LambdaQueryWrapper
主要主要是用来构建select、delete和update的where条件部分UpdateWrapper
和LambdaUpdateWrapper
通常只有在set语句比较特殊的时候才使用。
-
推荐使用LambdaUpdateWrapper、LambdaQueryWrapper,避免硬编码,原理通过反射获取字段名。
List<Long> ids = List.of(1L,2L,3L); LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>() .select(User::getUsername, User::getBalance) .in(User::getId,ids); List<User> userList = baseMapper.selectList(wrapper);
4、自定义SQL
-
查询语句应该写到mapper层,写到service违反了三层架构的思想,所以我们采用wrapper加自定义Sql。
-
使用方式,用wrapper在Service层写好where条件,将剩余操作值和wrapper一同传到mapper层。给wrapper用@Param标注,别名只能是“ew”,也可以使用MybatisPlus提供的
Constants.WRAPPER
。 -
在xxxmaper.xml文件上where部分使用
${ew.customSqlSegment}
,来提取wrapper生成的where条件sql字段。List<Long> ids = List.of(1L,2L,4L); int amount = 200; LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>() .in(User::getId,ids); userMapper.updateBalanceByIds(wrapper,amount);
void updateBalanceByIds(@Param(Constants.WRAPPER) LambdaQueryWrapper<User> wrapper,@Param("amount") int amount);
<update id="updateBalanceByIds"> update tb_user set balance = balance - #{amount} ${ew.customSqlSegment} </update>
5、IService批量新增注意事项(提高效率)
-
在MybatisPlus中默认的批量新增,底层是将每一个对象都转化成一个sql执行语句,然后一次通信后全部挨个执行。如下所示
insert into user(name,age,email) values('张三',18,zhangsan@qq.com); insert into user(name,age,email) values('李四',18,lisi@qq.com); insert into user(name,age,email) values('王五',18,wangwu@qq.com);
-
优化方案:转化成一次执行语句添加多条数据,它是由mysql驱动控制的,只需要在yml文件的url连接上加一个参数&rewriteBatchedStatements=true。就可以转化成一次执行语句添加多条数据。如下所示
url: jdbc:mysql://localhost:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
insert into user(name,age,email) values('张三',18,zhangsan@qq.com), ('李四',18,lisi@qq.com), ('王五',18,wangwu@qq.com);
二、常用扩展功能
1、代码生成器
- 定义:可以根据数据库的表,自动生成带有MybatisPlus功能的三层架构。
- 推荐使用idea插件,插件市场搜索:MybatisPlus
2、DB静态工具
-
定义:解决service互相调用而产生的循环依赖问题
-
使用方法:和调用serviceImpl<userMapper,User>的方法是一样的,只不过在调用传一个
对应数据类型
就可以了。Db.lambdaQuery(Address.class) -
使用实例:
@Service @Slf4j public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { /** * 根据id查询用户和相关地址 * @param id * @return */ public UserVO queryUSerAndAddressById(Long id) { //根据id获取到用户 User user = getById(id); //用户状态异常 if(user == null || user.getStatus() == UserStatus.FROZEN){ throw new RuntimeException("用户状态异常!"); } //查询地址 List<Address> addressList = Db.lambdaQuery(Address.class) .eq(Address::getUserId, user.getId()) .list(); UserVO userVO = BeanUtil.copyProperties(user, UserVO.class); //如果查询到的地址信息不为空,则添加地址信息 if (CollUtil.isNotEmpty(addressList)){ userVO.setAddressVOList(BeanUtil.copyToList(addressList, AddressVO.class)); } return userVO; } }
3、逻辑删除
-
定义:在数据库中可以设置一个逻辑删除的字段,设置开启值(默认0)和删除值(默认1)
-
我们需要在yml文件开启mybatisplus的默认逻辑删除并指定
逻辑删除字段
,在删除信息时会将逻辑删除字段
设置成删除值mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 #逻辑删除值(默认为1) logic-not-delete-value: 0 #逻辑开启值(默认为0)
4、枚举处理器
-
将对象的枚举值换成自定义枚举对象。
-
mybatis默认的Enum枚举处理器不能解决与数据库之间的转化,所以我们需要在yml文件中将默认的枚举转换器换成mybatisplus提供的枚举转换器MybatisEnumTypeHandler
mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
-
我们只需要告诉MybatisPlus将哪个字段作为值传入数据库,在对应字段加上注解@EnumValue。
-
@JsonValue可以在给前端传值时,指定要哪个为传的变量,将变量值作为内容传递。
@Getter public enum UserStatus { NORMAL(1,"正常"), FROZEN(2,"冻结"), ; @EnumValue private final int value; @JsonValue private final String desc; UserStatus(int value, String desc) { this.value = value; this.desc = desc; } }
5、JSON处理器
- 将对象转化成JSON存储到数据库。
- mybatis默认使用的是JacksonTypeHandler,我们转换也用这个JSON处理器。
- 使用两步操作:
- 在要转换成json储存到数据库的成员变量上加注接@TableField(typeHandler = JacksonTypeHandler.class),指定JSON处理器为JacksonTypeHandler
- 最后在需要转化的对象类上加上注解@TableName(value = “tb_user”,autoResultMap = true),主要是autoResultMap开启自动结果集映射
@Data
@TableName(value = "tb_user",autoResultMap = true)
public class User {
//...字段省略
/**
* 详细信息
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
//...字段省略
}
三、常用插件功能
1、分页查询插件
a、基本用法
-
首先添加一个给容器添加MybatisPlusInterceptor,就可以开启分页查询插件了。
@Configuration public class MybatisConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //创建分页查询 PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); paginationInnerInterceptor.setMaxLimit(1000L); //添加分页查询 interceptor.addInnerInterceptor(paginationInnerInterceptor); return interceptor; } }
-
调用page方法,即可获得分页之后的数据。
@Test void testPage(){ int pageNo = 1; int pageSize = 3; //1、准备分页条件 //1.1、排序规则 Page<User> page = Page.of(pageNo, pageSize); //1.2、排序规则 page.addOrder(OrderItem.asc("balance")); page.addOrder(OrderItem.asc("id")); //2、分页查询 Page<User> p = userService.page(page); //3、解析 //3.1、总条数 long total = p.getTotal(); System.out.println("total:"+total); //3.2、总页数 long pages = p.getPages(); System.out.println("pages:"+pages); //3.3、分页数据 List<User> userList = p.getRecords(); userList.forEach(System.out::println); }
b、通用分页实体
-
我们可以事先准备一个通用的分页实体,当其他实体需要分页查询时继承通用的分页实体即可
@Data @ApiModel("分页结果") public class PageDto<T> { @ApiModelProperty("总条数") private Long total; @ApiModelProperty("总页数") private Long pages; @ApiModelProperty("查询的数据集合") private List<T> list; }
c、分页查询公共代码封装成工具
-
PageQuery
@Data @ApiModel("分页查询实体") public class PageQuery { @ApiModelProperty("页码") private Integer pageNo; @ApiModelProperty("分页条数") private Integer pageSize; @ApiModelProperty("排序字段") private String sortBy; @ApiModelProperty("是否升序") private Boolean isAsc = true; public <T> Page<T> toMpPage(OrderItem ...items){ //1.1、分页条件 Page<T> page = Page.of(pageNo, pageSize); //1.2、排序条件 if (StrUtil.isNotBlank(sortBy)){ //排序字段不为空,则安照传入字段排序 page.addOrder(new OrderItem().setColumn(sortBy).setAsc(isAsc)); } else if(items != null){ //排序字段为空,则安照更新时间排序 page.addOrder(items); } return page; } public <T> Page<T> toMpPage(String defaultSortBy,Boolean defaultAsc){ return toMpPage(new OrderItem().setColumn(defaultSortBy).setAsc(defaultAsc)); } public <T> Page<T> toMpPageDefaultSortByCreateTime(){ return toMpPage(new OrderItem().setColumn("create_time").setAsc(false)); } public <T> Page<T> toMpPageDefaultSortByUpdateTime(){ return toMpPage(new OrderItem().setColumn("update_time").setAsc(false)); } }
-
PageDto
@Data @ApiModel("分页结果") public class PageDto<T> { @ApiModelProperty("总条数") private Long total; @ApiModelProperty("总页数") private Long pages; @ApiModelProperty("查询的数据集合") private List<T> list; public static <PO,VO> PageDto<VO> of(Page<PO> p,Class<VO> clazz){ PageDto<VO> pageDto = new PageDto<>(); //1、总条数 pageDto.setTotal(p.getTotal()); //2、总页数 pageDto.setPages(p.getPages()); //3、当前页数据 List<PO> userPageList = p.getRecords(); if (CollUtil.isEmpty(userPageList)){ pageDto.setList(Collections.emptyList()); return pageDto; } //4、拷贝user的VO pageDto.setList(BeanUtil.copyToList(userPageList, clazz)); return pageDto; } public static <PO,VO> PageDto<VO> of(Page<PO> p, Function<PO,VO> convertor){ PageDto<VO> pageDto = new PageDto<>(); //1、总条数 pageDto.setTotal(p.getTotal()); //2、总页数 pageDto.setPages(p.getPages()); //3、当前页数据 List<PO> userPageList = p.getRecords(); if (CollUtil.isEmpty(userPageList)){ pageDto.setList(Collections.emptyList()); return pageDto; } //4、拷贝user的VO pageDto.setList(userPageList.stream().map(convertor).collect(Collectors.toList())); return pageDto; } }
-
最后在sercice层将工具调用
/** * 条件分页查询用户 * @param query 将查询条件封装成一个对象 * @return PageDto<User> */ public PageDto<UserVO> queryUsersPage(UserQuery query) { String name = query.getName(); UserStatus status = query.getStatus(); //1、构建查询条件 Page<User> page = query.toMpPageDefaultSortByUpdateTime(); //2、分页查询 Page<User> p = lambdaQuery() .like(name != null, User::getUsername, name) .eq(status != null, User::getStatus, status) .page(page); //3、封装VO结果 //4、返回 return PageDto.of(p,user -> { //基础拷贝 UserVO userVO = BeanUtil.copyProperties(user, UserVO.class); //其他特殊操作 userVO.setUsername(userVO.getUsername().substring(0,userVO.getUsername().length() - 2) + "**"); return userVO; }); }