MyBatisPlus属于是MyBatis的拓展,不影响原MyBatis框架下的代码运行,并对MyBatis框架进行拓展及优化。
使用步骤:
注意:继承BaseMapper时要填写泛型为要操作的实体类。
基本原理:
MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。
并且满足下面三点:
·类名驼峰转下划线作为表名
·名为id的字段作为主键
·变量名驼峰转下划线作为表的字段
(createTime -> create_time)
常见注解:
常见配置:
MyBatisPlus的配置项继承了MyBatis原生配置和一些自己特有的配置。例如:
注意:除去第一个别名扫描包,其他的配置在默认情况下就是这样配置的。
对MyBatis-Plus中的wrapper进行测试:
@Testvoid testLambdaQueryWrapper1() {//查询用户名中有“o”且余额为1000的用户的id、userName、info、balanceLambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().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);}@Testvoid testLambdaQueryWrapper2() {//更新jack的余额为2000User user = new User();user.setBalance(2000);LambdaQueryWrapper wrapper = new LambdaQueryWrapper<User>().eq(User::getUsername, "jack");userMapper.update(user, wrapper);}@Testvoid testLambdaUpdateWrapper() {//更新id为1,2,4的用户的余额,扣200/*update set balance = balance - 200 where id in (1,2,4)*/List<Long> ids = List.of(1L, 2L, 4L);LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>().setSql("balance = balance - 200").in(User::getId, ids);userMapper.update(null, wrapper);}
自定义SQL:
操作步骤:
Service接口:
使用流程:
实现下面接口:
代码:
Controller:
@RestController
@RequestMapping("/users")
@Api(tags = "用户管理接口")
@RequiredArgsConstructor
public class UserController {private final IUserService userService;@ApiOperation("新增用户接口")@PostMappingpublic void save(@RequestBody UserFormDTO userDTO) {User user = BeanUtil.copyProperties(userDTO, User.class);userService.save(user);}@ApiOperation("删除用户接口")@DeleteMapping("/{id}")public void delete(@ApiParam("用户id") @PathVariable Long id) {userService.removeById(id);}@ApiOperation("根据id查询用户接口")@GetMapping("/{id}")public UserVO get(@ApiParam("用户id") @PathVariable Long id) {User user = userService.getById(id);UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);return userVO;}@ApiOperation("根据id批量查询接口")@GetMappingpublic List<UserVO> list(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids) {List<User> users = userService.listByIds(ids);List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);return userVOS;}@ApiOperation("根据id扣减余额接口")@PutMapping("/{id}/deduction/{money}")public void deductBalance(@ApiParam("用户id") @PathVariable Long id, @ApiParam("扣减金额") @PathVariable Integer money) {userService.deductBalance(id, money);}}
Service:
public interface IUserService extends IService<User> {void deductBalance(Long id, Integer money);
}
ServiceImpl:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic void deductBalance(@Param("id") Long id, @Param("money") 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.扣除余额baseMapper.deductBalance(id, money);}
}
Mapper:
public interface UserMapper extends BaseMapper<User> {void updateBalanceByIds(@Param("ew") LambdaUpdateWrapper<User> wrapper, @Param("amount") int amount);@Update("update user set balance = balance - #{money} where id = #{id}")void deductBalance(Long id, Integer money);
}
Lambda查询:
lambda查询功能很强大,除去下面例题使用的list,还有查询一个时使用的one,分页查询使用的page,统计使用的count。
代码演示:
Controller:
@ApiOperation("根据复杂条件查询用户接口")@GetMapping("/list")public List<UserVO> queryUsers(UserQuery query) {//1.查询用户POList<User> users = userService.queryUsers(query.getName(), query.getStatus(), query.getMinBalance(), query.getMaxBalance());//2.将PO拷贝到VOreturn BeanUtil.copyToList(users, UserVO.class);}
Service:
List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
ServiceImpl:
@Overridepublic List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {return lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).ge(minBalance != null, User::getBalance, minBalance).le(maxBalance != null, User::getBalance, maxBalance).list();}
Lambda更新
代码演示:
Controller:
@ApiOperation("根据id扣减余额接口")@PutMapping("/{id}/deduction/{money}")public void deductBalance(@ApiParam("用户id") @PathVariable Long id, @ApiParam("扣减金额") @PathVariable Integer money) {userService.deductBalance(id, money);}
Service:
void deductBalance(Long id, Integer money);
ServiceImpl:
@Overridepublic void deductBalance(@Param("id") Long id, @Param("money") 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.扣除余额int remainBalance = user.getBalance() - money;lambdaUpdate().set(User::getBalance, remainBalance).set(remainBalance == 0, User::getStatus, 2).eq(User::getId, id).eq(User::getBalance, user.getBalance())//乐观锁 先比较再更新.update();}
在业务层,如果两个线程同时查询同一个用户的信息,并且都扣除100余额,如果不进行加锁那么结果余额只会减少100,所以这里采用乐观锁,先比较数据库中的余额信息与查询到的用户余额信息是否一致,一致再更新数据。
批量新增:
批处理方案:
·普通for循环逐条插入,速度极差,不推荐
·MP的批量新增,基于预编译的批处理,性能不错
·配置jdbc的参数,设置rewriteBatchedStatements为true,性能最好
配置:
DB静态工具:
逻辑删除:
逻辑删除即删除数据时,通过修改一个特殊的字段值,来进行逻辑上的删除,而不是真正的删除数据,比如添加字段deleted,它为false即这条数据没被删除,它为true即这条数据被删除
问题:
枚举处理器:
实现PO类中枚举类型变量与数据库字段的转换:
User和UserVO:
@Data
public class User {/*** 用户id*/@TableId(value = "id", type = IdType.AUTO)private Long id;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 注册手机号*/private String phone;/*** 详细信息*/private String info;/*** 使用状态(1正常 2冻结)*/private UserStatus status;/*** 账户余额*/private Integer balance;/*** 创建时间*/private LocalDateTime createTime;/*** 更新时间*/private LocalDateTime updateTime;
}
@Data
@ApiModel(description = "用户VO实体")
public class UserVO {@ApiModelProperty("用户id")private Long id;@ApiModelProperty("用户名")private String username;@ApiModelProperty("详细信息")private String info;@ApiModelProperty("使用状态(1正常 2冻结)")private UserStatus status;@ApiModelProperty("账户余额")private Integer balance;@ApiModelProperty("用户的收货地址")private List<AddressVO> addresses;
}
①枚举类:
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;}
}
②在配置文件中进行配置:
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
JSON处理器:
分页查询:
①先配置插件
@Configuration
public class MyBatisConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//创建分页插件PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);paginationInnerInterceptor.setMaxLimit(1000L);//添加分页插件interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}}
②使用插件:
PageQuery:
@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {@ApiModelProperty("页码")private Long pageNo = 1L;@ApiModelProperty("页大小")private Long pageSize = 5L;@ApiModelProperty("排序字段")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc = true;public <T> Page<T> toMpPage(OrderItem ... items) {//1.分页条件Page<T> page = Page.of(pageNo, pageSize);//2.排序条件if (StrUtil.isNotBlank(sortBy)) {//不为空page.addOrder(new OrderItem(sortBy, isAsc));} else if (items != null) {//为空,默认排序page.addOrder(items);}return page;}public <T> Page<T> toMpPage(String defaultSortBy, Boolean defaultAsc) {return toMpPage(new OrderItem(defaultSortBy, defaultAsc));}public <T> Page<T> toMpPageDefaultSortByCreateTime() {return toMpPage(new OrderItem("create_time", false));}public <T> Page<T> toMpPageDefaultSortByUpdateTime() {return toMpPage(new OrderItem("update_time", false));}}
pageDTO:
@Data
@ApiModel(description = "分页结果")
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> 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的VOdto.setList(BeanUtil.copyToList(records, clazz));//5.返回return dto;}public static <PO, VO> PageDTO<VO> of(Page<PO> p, Function<PO, VO> convertor) {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的VOdto.setList(records.stream().map(convertor).collect(Collectors.toList()));//5.返回return dto;}}
Controller:
@ApiOperation("根据条件分页查询用户接口")@GetMapping("/page")public PageDTO<UserVO> queryUsersPage(UserQuery query) {return userService.pageUsersPage(query);}
Service:
PageDTO<UserVO> pageUsersPage(UserQuery query);
ServiceImpl:
@Overridepublic PageDTO<UserVO> pageUsersPage(UserQuery query) {String name = query.getName();Integer 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结果return PageDTO.of(p, UserVO.class);}