(一)MybatisPlus入门案例
数据库表的建立sql,可以复制到txt文本导入即可,也可以直接复制到navicat直接执行。
-- --------------------------------------------------------
-- 主机: 127.0.0.1
-- 服务器版本: 8.0.28 - MySQL Community Server - GPL
-- 服务器操作系统: Win64
-- HeidiSQL 版本: 12.2.0.6576
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-- 导出 mp 的数据库结构
CREATE DATABASE IF NOT EXISTS `mp` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `mp`;
-- 导出 表 mp.address 结构
CREATE TABLE IF NOT EXISTS `address` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint DEFAULT NULL COMMENT '用户ID',
`province` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '省',
`city` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '市',
`town` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '县/区',
`mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '手机',
`street` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '详细地址',
`contact` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '联系人',
`is_default` bit(1) DEFAULT b'0' COMMENT '是否是默认 1默认 0否',
`notes` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
`deleted` bit(1) DEFAULT b'0' COMMENT '逻辑删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=71 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=COMPACT;
-- 正在导出表 mp.address 的数据:~11 rows (大约)
INSERT INTO `address` (`id`, `user_id`, `province`, `city`, `town`, `mobile`, `street`, `contact`, `is_default`, `notes`, `deleted`) VALUES
(59, 2, '北京', '北京', '朝阳区', '13900112222', '金燕龙办公楼', 'Rose', b'1', NULL, b'0'),
(60, 1, '北京', '北京', '朝阳区', '13700221122', '修正大厦', 'Jack', b'0', NULL, b'0'),
(61, 1, '上海', '上海', '浦东新区', '13301212233', '航头镇航头路', 'Jack', b'1', NULL, b'0'),
(63, 2, '广东', '佛山', '永春', '13301212233', '永春武馆', 'Rose', b'0', NULL, b'0'),
(64, 3, '浙江', '杭州', '拱墅区', '13567809102', '浙江大学', 'Hope', b'1', NULL, b'0'),
(65, 3, '浙江', '杭州', '拱墅区', '13967589201', '左岸花园', 'Hope', b'0', NULL, b'0'),
(66, 4, '湖北', '武汉', '汉口', '13967519202', '天天花园', 'Thomas', b'1', NULL, b'0'),
(67, 3, '浙江', '杭州', '拱墅区', '13967589201', '左岸花园', 'Hopey', b'0', NULL, b'0'),
(68, 4, '湖北', '武汉', '汉口', '13967519202', '天天花园', 'Thomas', b'1', NULL, b'0'),
(69, 3, '浙江', '杭州', '拱墅区', '13967589201', '左岸花园', 'Hopey', b'0', NULL, b'0'),
(70, 4, '湖北', '武汉', '汉口', '13967519202', '天天花园', 'Thomas', b'1', NULL, b'0');
-- 导出 表 mp.user 结构
CREATE TABLE `user` (
`id` BIGINT(19) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`username` VARCHAR(50) NOT NULL COMMENT '用户名' COLLATE 'utf8_general_ci',
`password` VARCHAR(128) NOT NULL COMMENT '密码' COLLATE 'utf8_general_ci',
`phone` VARCHAR(20) NULL DEFAULT NULL COMMENT '注册手机号' COLLATE 'utf8_general_ci',
`info` JSON NOT NULL COMMENT '详细信息',
`status` INT(10) NULL DEFAULT '1' COMMENT '使用状态(1正常 2冻结)',
`balance` INT(10) NULL DEFAULT NULL COMMENT '账户余额',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `username` (`username`) USING BTREE
)
COMMENT='用户表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
ROW_FORMAT=COMPACT
AUTO_INCREMENT=5
;
-- 正在导出表 mp.user 的数据:~4 rows (大约)
INSERT INTO `user` (`id`, `username`, `password`, `phone`, `info`, `status`, `balance`, `create_time`, `update_time`) VALUES
(1, 'Jack', '123', '13900112224', '{"age": 20, "intro": "佛系青年", "gender": "male"}', 1, 1600, '2023-05-19 20:50:21', '2023-06-19 20:50:21'),
(2, 'Rose', '123', '13900112223', '{"age": 19, "intro": "青涩少女", "gender": "female"}', 1, 600, '2023-05-19 21:00:23', '2023-06-19 21:00:23'),
(3, 'Hope', '123', '13900112222', '{"age": 25, "intro": "上进青年", "gender": "male"}', 1, 100000, '2023-06-19 22:37:44', '2023-06-19 22:37:44'),
(4, 'Thomas', '123', '17701265258', '{"age": 29, "intro": "伏地魔", "gender": "male"}', 1, 800, '2023-06-19 23:44:45', '2023-06-19 23:44:45');
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 你的密码
logging:
level:
com.itheima: debug
pattern:
dateformat: HH:mm:ss
mybatis:
mapper-locations: classpath*:mapper/*.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itheima.mp</groupId>
<artifactId>mp-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mp-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
注释mybatis的依赖,加速mp的依赖
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>2.3.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
继承Basemapper,mp对mybaits的代码时是无侵入的,因此可以继续保留原mybaits的代码
public interface UserMapper extends BaseMapper<User> {
void saveUser(User user);
void deleteUser(Long id);
void updateUser(User user);
User queryUserById(@Param("id") Long id);
List<User> queryUserByIds(@Param("ids") List<Long> ids);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mp.mapper.UserMapper">
<insert id="saveUser" parameterType="com.itheima.mp.domain.po.User">
INSERT INTO `user` (`id`, `username`, `password`, `phone`, `info`, `balance`)
VALUES
(#{id}, #{username}, #{password}, #{phone}, #{info}, #{balance});
</insert>
<update id="updateUser" parameterType="com.itheima.mp.domain.po.User">
UPDATE `user`
<set>
<if test="username != null">
`username`=#{username}
</if>
<if test="password != null">
`password`=#{password}
</if>
<if test="phone != null">
`phone`=#{phone}
</if>
<if test="info != null">
`info`=#{info}
</if>
<if test="status != null">
`status`=#{status}
</if>
<if test="balance != null">
`balance`=#{balance}
</if>
</set>
WHERE `id`=#{id};
</update>
<delete id="deleteUser" parameterType="com.itheima.mp.domain.po.User">
DELETE FROM user WHERE id = #{id}
</delete>
<select id="queryUserById" resultType="com.itheima.mp.domain.po.User">
SELECT *
FROM user
WHERE id = #{id}
</select>
<select id="queryUserByIds" resultType="com.itheima.mp.domain.po.User">
SELECT *
FROM user
<if test="ids != null">
WHERE id IN
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
LIMIT 10
</select>
</mapper>
单元测试
@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testInsert() {
User user = new User();
user.setId(5L);
user.setUsername("Lucy");
user.setPassword("123");
user.setPhone("18688990011");
user.setBalance(200);
user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
userMapper.saveUser(user);
}
@Test
void testSelectById() {
User user = userMapper.queryUserById(5L);
System.out.println("user = " + user);
}
@Test
void testQueryByIds() {
List<User> users = userMapper.queryUserByIds(List.of(1L, 2L, 3L, 4L));
users.forEach(System.out::println);
}
@Test
void testUpdateById() {
User user = new User();
user.setId(5L);
user.setBalance(20000);
userMapper.updateUser(user);
}
@Test
void testDeleteUser() {
userMapper.deleteUser(5L);
}
}
测试原mybatis的代码依旧可用,但是我们要学习的是mp
注释掉的是原mybaits代码,新的是mq,测试都阔以通过
@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testInsert() {
User user = new User();
user.setId(5L);
user.setUsername("Lucy");
user.setPassword("123");
user.setPhone("18688990011");
user.setBalance(200);
user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
// userMapper.saveUser(user);
// mp实现
userMapper.insert(user);
}
@Test
void testSelectById() {
// User user = userMapper.queryUserById(5L);
User user = userMapper.selectById(5L);
System.out.println("user = " + user);
}
@Test
void testQueryByIds() {
// List<User> users = userMapper.queryUserByIds(List.of(1L, 2L, 3L, 4L));
List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L));
users.forEach(System.out::println);
}
@Test
void testUpdateById() {
User user = new User();
user.setId(5L);
user.setBalance(20000);
// userMapper.updateUser(user);
userMapper.updateById(user);
}
@Test
void testDeleteUser() {
// userMapper.deleteUser(5L);
userMapper.deleteById(5L);
}
}
(二)MybatisPlus常用注解
为了成为mp的个人练习生,需要修改一下表面不一致,因为我们一开始的实体和数据库太一致了。。。这里就只测试表名了,想测试的小伙伴可以自己根据条件去修改。。。
@Data
@TableName("tb_user")
public class User {
/**
* 用户id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 注册手机号
*/
private String phone;
/**
* 详细信息
*/
private String info;
/**
* 使用状态(1正常 2冻结)
*/
private Integer status;
/**
* 账户余额
*/
private Integer balance;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
(三)MybatisPlus常用配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 你的密码
logging:
level:
com.itheima: debug
pattern:
dateformat: HH:mm:ss
#mybatis:
# mapper-locations: classpath*:mapper/*.xml
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po
mapper-locations: classpath*:mapper/**/*.xml
global-config:
db-config:
id-type: auto
(四)MybatisPlus条件构造器
@Test
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);
}
@Test
void testUpdateQueryWrapper(){
User user = new User();
user.setBalance(2000);
QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username","jack");
userMapper.update(user,wrapper);
}
@Test
void testUpdateWrapper(){
List<Long> ids = List.of(1L,2L,4L);
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200")
.in("id", ids);
userMapper.update(null,wrapper);
}
避免硬编码机制:
@Test
void testLambdaQueryWrapper(){
LambdaQueryWrapper<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);
}
(五)MybatisPlus自定义sql
@Test
void testUpdateWrapper2(){
// 1.更新条件
List<Long> ids = List.of(1L,2L,4L);
int amount = 200;
QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);
userMapper.updateBalanceByIds(wrapper, amount);
}
void updateBalanceByIds(@Param("ew") QueryWrapper<User> wrapper,@Param("amount") int amount);
<update id="updateBalanceByIds">
UPDATE tb_user SET balance = balance - ${amount} ${ew.customSqlSegment}
</update>
(六) Iservice基本接口用法
public interface IUserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}
// alt + enter 自动生成单元测试
@SpringBootTest
class UserServiceImplTest {
@Autowired
private IUserService userService;
@Test
void testInsert() {
User user = new User();
// user.setId(5L);
user.setUsername("LiLei");
user.setPassword("123");
user.setPhone("18688990011");
user.setBalance(200);
user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
// userMapper.saveUser(user);
// mp实现
userService.save(user);
}
@Test
void testQuery(){
List<User> users = userService.listByIds(List.of(1L, 2L, 4L));
users.forEach(System.out::println);
}
}
(七) Iservice开发基础业务接口
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
视频导入的包
knife4j:
enable: true
openapi:
title: 用户管理接口文档
description: 用户管理接口文档
email: 1111111111@qq.com
concat: llll
url: https://www.itcast.cn
version: v1.0.0
group:
default:
group-name: default
api-rule: package
api-rule-resources:
- com.itheima.mp.controller
@Data
@ApiModel(description = "用户表单实体")
public class UserFormDTO {
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
@ApiModelProperty("注册手机号")
private String phone;
@ApiModelProperty("详细信息,JSON风格")
private String info;
@ApiModelProperty("账户余额")
private Integer balance;
}
@Data
@ApiModel(description = "用户VO实体")
public class UserVO {
@ApiModelProperty("用户id")
private Long id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("详细信息")
private String info;
@ApiModelProperty("使用状态(1正常 2冻结)")
private Integer status;
@ApiModelProperty("账户余额")
private Integer balance;
}
@Data
@TableName("tb_user")
public class User {
/**
* 用户id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 注册手机号
*/
private String phone;
/**
* 详细信息
*/
private String info;
/**
* 使用状态(1正常 2冻结)
*/
private Integer status;
/**
* 账户余额
*/
private Integer balance;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
@Api(tags = "用户管理接口")
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final IUserService userService;
@ApiOperation("新增用户接口")
@PostMapping
public void saveUser(@RequestBody UserFormDTO userDto){
// 1.把Dto拷贝到po
User user = BeanUtil.copyProperties(userDto, User.class);
userService.save(user);
}
@ApiOperation("删除用户接口")
@DeleteMapping("/{id}")
public void deleteUserById(@ApiParam("用户id") @PathVariable("id") Long id){
userService.removeById(id);
}
@ApiOperation("根据id查询用户接口")
@GetMapping("/{id}")
public UserVO queryUserById(@ApiParam("用户id") @PathVariable Long id){
User user = userService.getById(id);
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
return userVO;
}
@ApiOperation("根据id批量查询用户接口")
@GetMapping
public List<UserVO> queryUserById(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
List<User> users = userService.listByIds(ids);
return BeanUtil.copyToList(users, UserVO.class);
}
@ApiOperation("扣减用户余额接口")
@DeleteMapping("/{id}/deduction/{money}")
public void deductMoneyById(@ApiParam("用户id") @PathVariable("id") Long id,
@ApiParam("扣减的金额") @PathVariable("money") Integer money){
userService.deductBalance(id,money);
}
}
public interface IUserService extends IService<User> {
void deductBalance(Long id, Integer money);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService{
@Override
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.减扣余额
baseMapper.deductBalance(id, money);
}
}
public interface UserMapper extends BaseMapper<User> {
void saveUser(User user);
void deleteUser(Long id);
void updateUser(User user);
User queryUserById(@Param("id") Long id);
List<User> queryUserByIds(@Param("ids") List<Long> ids);
void updateBalanceByIds(@Param("ew") QueryWrapper<User> wrapper,@Param("amount") int amount);
@Update("update tb_user set balance = balance - #{money} where id = #{id}")
void deductBalance(@Param("id") Long id,@Param("money") Integer money);
}
http://localhost:8080/doc.html 启动springboot进入这个网址测试接口,测试下来都没问题。
(八) Iservice的Lambda查询
@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery {
@ApiModelProperty("用户名关键字")
private String name;
@ApiModelProperty("用户状态:1-正常,2-冻结")
private Integer status;
@ApiModelProperty("余额最小值")
private Integer minBalance;
@ApiModelProperty("余额最大值")
private Integer maxBalance;
}
@ApiOperation("根据复杂条件批量查询用户接口")
@GetMapping("/list")
public List<UserVO> queryUsers(UserQuery userQuery){
List<User> users = userService.queryUsers(userQuery.getName(),userQuery.getStatus(),
userQuery.getMinBalance(), userQuery.getMaxBalance());
return BeanUtil.copyToList(users, UserVO.class);
}
public interface IUserService extends IService<User> {
List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
}
@Override
public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
List<User> list = 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();
return list;
}
@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.减扣余额
// baseMapper.deductBalance(id, money);
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();
}
(九) Iservice的批量新增
更改配置
(十) 代码生成器
(十一) 静态工具
更改一下mp的版本,新版本支持
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
service相互调用可能会出现循环依赖时,利用db
@Data
@ApiModel(description = "收货地址VO")
public class AddressVO{
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("用户ID")
private Long userId;
@ApiModelProperty("省")
private String province;
@ApiModelProperty("市")
private String city;
@ApiModelProperty("县/区")
private String town;
@ApiModelProperty("手机")
private String mobile;
@ApiModelProperty("详细地址")
private String street;
@ApiModelProperty("联系人")
private String contact;
@ApiModelProperty("是否是默认 1默认 0否")
private Boolean isDefault;
@ApiModelProperty("备注")
private String notes;
}
@Data
@ApiModel(description = "用户VO实体")
public class UserVO {
@ApiModelProperty("用户id")
private Long id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("详细信息")
private String info;
@ApiModelProperty("使用状态(1正常 2冻结)")
private Integer status;
@ApiModelProperty("账户余额")
private Integer balance;
@ApiModelProperty("用户的收获地址")
private List<AddressVO> addresses;
}
@Override
public UserVO queryUserInfoById(Long id) {
// 1.查询用户
User user = getById(id);
if(user == null || user.getStatus() == 2){
throw new RuntimeException("用户状态异常!");
}
// 2.查询地址
List<Address> addresses = Db.lambdaQuery(Address.class)
.eq(Address::getUserId, id).list();
// 3.封装VO
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
if(CollUtil.isNotEmpty(addresses)){
List<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class);
userVO.setAddresses(addressVOS);
}
return userVO;
}
@Override
public List<UserVO> queryUserInfosById(List<Long> ids) {
List<User> users = listByIds(ids);
if(CollUtil.isEmpty(users)){
return Collections.emptyList();
}
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
List<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class);
Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);
if(CollUtil.isNotEmpty(addresses)){
addressMap = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
}
List<UserVO> list = new ArrayList<>(users.size());
for (User user : users) {
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
userVO.setAddresses(addressMap.get(userVO.getId()));
list.add(userVO);
}
return list;
}
(十二) 逻辑删除
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po
mapper-locations: classpath*:mapper/**/*.xml
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
@SpringBootTest
class IAddressServiceTest {
@Autowired
private IAddressService addressService;
@Test
void testLogicDelete(){
addressService.removeById(60L);
Address address = addressService.getById(60L);
System.out.println("address:" + address);
}
}
(十三) 枚举处理器
@Getter
public enum UserStatus {
NORMAL(1,"正常"),
FROZEN(2,"冻结"),;
@EnumValue
@JsonValue
private final int value;
private final String desc;
UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
}
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po
mapper-locations: classpath*:mapper/**/*.xml
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
configuration:
# 配置枚举处理器(3.5.0+ 版本推荐)
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
/**
* 使用状态(1正常 2冻结)
*/
private UserStatus status;
// 2.校验用户状态
if(user == null || user.getStatus() == UserStatus.FROZEN){
throw new RuntimeException("用户状态异常");
}
int remainBalance = user.getBalance() - money;
lambdaUpdate().set(User::getBalance, remainBalance)
.set(remainBalance==0,User::getStatus, UserStatus.FROZEN)
.eq(User::getId, id)
.eq(User::getBalance, user.getBalance()) // 乐观锁
.update();
(十四) JSON处理器
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}
@Data
@TableName(value = "tb_user",autoResultMap = true)
public class User {
/**
* 用户id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 注册手机号
*/
private String phone;
/**
* 详细信息
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
/**
* 使用状态(1正常 2冻结)
*/
private UserStatus status;
/**
* 账户余额
*/
private Integer balance;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
需要将xml以前使用mybatis创建的部分删干净,不然会报错,大概时info字段转换错误
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mp.mapper.UserMapper">
<update id="updateBalanceByIds">
UPDATE tb_user SET balance = balance - ${amount} ${ew.customSqlSegment}
</update>
</mapper>
(十四) 分页插件基本用法
@Configuration
public class MybatisPlusConfig {
/**
* 添加 MyBatis-Plus 插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
paginationInterceptor.setDbType(DbType.MYSQL); // 根据数据库类型设置
paginationInterceptor.setOverflow(true); // 溢出总页数后处理(默认false)
paginationInterceptor.setMaxLimit(500L); // 单页分页条数限制
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
}
@Test
void testPageQuery(){
int pageNo = 1, pageSize = 20;
Page<User> page = Page.of(pageNo, pageSize);
page.addOrder(new OrderItem("balance", true));
page.addOrder(new OrderItem("id",true));
Page<User> p = userService.page(page);
long total = p.getTotal();
System.out.println(total);
long pages = p.getPages();
System.out.println(pages);
List<User> records = p.getRecords();
records.forEach(System.out::println);
}
(十五) 通用分页实体、与MP转换
@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;
}
@Data
public class PageQuery {
@ApiModelProperty("页码")
private Integer pageNo = 1;
private Integer pageSize = 5;
private String sortBy;
private Boolean isAsc = true;
public <T> Page<T> toMpPage(OrderItem ... orders){
// 1.分页条件
Page<T> p = Page.of(pageNo, pageSize);
// 2.排序条件
// 2.1.先看前端有没有传排序字段
if (sortBy != null) {
p.addOrder(new OrderItem(sortBy, isAsc));
return p;
}
// 2.2.再看有没有手动指定排序字段
if(orders != null){
p.addOrder(orders);
}
return p;
}
public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc){
return this.toMpPage(new OrderItem(defaultSortBy, isAsc));
}
public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
return toMpPage("create_time", false);
}
public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {
return toMpPage("update_time", false);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageDTO<V> {
private Long total;
private Long pages;
private List<V> list;
/**
* 返回空分页结果
* @param p MybatisPlus的分页结果
* @param <V> 目标VO类型
* @param <P> 原始PO类型
* @return VO的分页对象
*/
public static <V, P> PageDTO<V> empty(Page<P> p){
return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList());
}
/**
* 将MybatisPlus分页结果转为 VO分页结果
* @param p MybatisPlus的分页结果
* @param voClass 目标VO类型的字节码
* @param <V> 目标VO类型
* @param <P> 原始PO类型
* @return VO的分页对象
*/
public static <V, P> PageDTO<V> of(Page<P> p, Class<V> voClass) {
// 1.非空校验
List<P> records = p.getRecords();
if (records == null || records.size() <= 0) {
// 无数据,返回空结果
return empty(p);
}
// 2.数据转换
List<V> vos = BeanUtil.copyToList(records, voClass);
// 3.封装返回
return new PageDTO<>(p.getTotal(), p.getPages(), vos);
}
/**
* 将MybatisPlus分页结果转为 VO分页结果,允许用户自定义PO到VO的转换方式
* @param p MybatisPlus的分页结果
* @param convertor PO到VO的转换函数
* @param <V> 目标VO类型
* @param <P> 原始PO类型
* @return VO的分页对象
*/
public static <V, P> PageDTO<V> of(Page<P> p, Function<P, V> convertor) {
// 1.非空校验
List<P> records = p.getRecords();
if (records == null || records.size() <= 0) {
// 无数据,返回空结果
return empty(p);
}
// 2.数据转换
List<V> vos = records.stream().map(convertor).collect(Collectors.toList());
// 3.封装返回
return new PageDTO<>(p.getTotal(), p.getPages(), vos);
}
}
@ApiOperation("根据条件分页查询用户接口")
@GetMapping("/page")
public PageDTO<UserVO> queryUsersByPage(UserQuery userQuery){
return userService.queryUsersByPage(userQuery);
}
@Override
public PageDTO<UserVO> queryUsersByPage(UserQuery userQuery) {
String name = userQuery.getName();
Integer status = userQuery.getStatus();
Page<User> page = Page.of(userQuery.getPageNo(), userQuery.getPageSize());
if(StrUtil.isNotBlank(userQuery.getSortBy())){
page.addOrder(new OrderItem(userQuery.getSortBy(), userQuery.getIsAsc()));
}else{
page.addOrder(new OrderItem("update_time", false));
}
Page<User> p = lambdaQuery()
.like(name != null, User::getUsername, name)
.eq(status != null, User::getStatus, status)
.page(page);
PageDTO<UserVO> pageDTO = new PageDTO<>();
pageDTO.setPages(p.getPages());
pageDTO.setTotal(p.getTotal());
List<User> records = p.getRecords();
if(CollUtil.isEmpty(records)){
pageDTO.setList(Collections.emptyList());
return pageDTO;
}
List<UserVO> userVOS = BeanUtil.copyToList(records, UserVO.class);
pageDTO.setList(userVOS);
return pageDTO;
}
封装改写。。。