🌟 后端 | MybatisPlus + SpringBoot实践



整个关于 MyBatisPlus 配套的例子以系统中的 User 功能为例,涉及如何操作用户数据。

定义基础实体 BaseEntity

抽象出对于 MyBatisPlus 的基础配置,也就是适用于项目的每个数据库表。编写具体表实体的时候可以扩展该基础实体 BaseEntity 即可。

抽取出每个表的共同字段,例如 id createBy createTime updateBy updateTime 。

该类被定义在系统公共部分的 MyBatis 下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// money-pos/qk-money-common/money-common-mybatis/src/main/java/com/money/mb/base/BaseEntity.java
package com.money.mb.base;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;

@Getter
@Setter
public class BaseEntity {

/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;

/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;

/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;

/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}

定义系统公共数据实体 SysUser

根据数据库表 定义(转换)对象+字段。

系统公共数据,例如:User 表、Role 表、Permission 表,此类作为系统框架的公共数据维护,通常也会被抽出放在一个模块下,比如此处的 entity/SysUser.java 就放在收银系统的 money-app-system 模块下。

该文件主要定义 sys_user 表的实体类 SysUser,并从上边定义的 MyBatisPlus 基础类 BaseEntity 扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// money-pos/qk-money-app/money-app-system/src/main/java/com/money/entity/SysUser.java
package com.money.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.money.mb.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;

@Getter
@Setter
@TableName("sys_user") // 该实体类 SysUser 对应的数据表
public class SysUser extends BaseEntity {

@Schema(description = "用户名")
private String username;

@Schema(description = "密码")
private String password;

@Schema(description = "昵称")
private String nickname;

@Schema(description = "头像")
private String avatar;

@Schema(description = "手机号码")
private String phone;

@Schema(description = "邮箱")
private String email;

@Schema(description = "备注")
private String remark;

@Schema(description = "可用状态:0-禁用;1-启用")
private Boolean enabled;

@Schema(description = "初次登录:0-不是;1-是")
private Boolean initLogin;

@Schema(description = "最后登录时间")
private LocalDateTime lastTime;

@Schema(description = "租户id")
private Long tenantId;

}

系统相关的实体被放在 money-app-system 模块下, 但其他模块也会定义实体类需要操作数据库,比如业务相关的表,文件自然会放在 money-app-api 业务模块下,但是包依旧需定义为:package com.money.entity; 表示同一功能。之后打包后也会在一个目录下,我们按模块编写代码只是方便逻辑上的思考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// money-pos/qk-money-app/money-app-api/src/main/java/com/money/entity/GmsBrand.java
package com.money.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.money.mb.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@TableName("gms_brand")
@Schema(description = "商品品牌表")
public class GmsBrand extends BaseEntity {

@Schema(description="品牌logo")
private String logo;

@Schema(description="品牌名称")
private String name;

// 。。。
}

配置 Mapper —— SysUserMapper

同样是放在整个目录下的 mapper 目录下:package com.money.mapper; ,之后会打包在一起,同时也需要清楚,会有其他模块的 mapper 也会定义在该 package 下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// money-pos/qk-money-app/money-app-system/src/main/java/com/money/mapper/SysUserMapper.java
package com.money.mapper;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.money.entity.SysUser;

/**
* 管理员表(SysAdmin)表数据库访问层
*
* @author money
* @since 2021-09-08 22:34:42
*/
public interface SysUserMapper extends BaseMapper<SysUser> {
}

定义 SysUserService

定义 SysUserService 接口,直接扩展 MyBatisPlus 自带的 IService,直接就获得了基础的增删改查方法,在此基础上定义我们所需的个别方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// money-pos/qk-money-app/money-app-system/src/main/java/com/money/service/SysUserService.java
package com.money.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.money.dto.query.SysUserPageQueryDTO;
import com.money.web.vo.PageVO;
import com.money.dto.SysUserDTO; // 新增时的用户表单
import com.money.entity.SysUser; // 此作为 user 表的 PO,和数据表一一对应
import com.money.vo.SysUserVO; // 查询的返回结果表单

import java.util.Set;

/**
* 用户表(SysUser)表服务接口
*
* @author money
* @since 2021-09-08 22:35:10
*/
public interface SysUserService extends IService<SysUser> {

/**
* 通过用户名获取用户
*
* @param username 用户名
* @return {@link SysUser}
*/
SysUser getByUsername(String username);

// ============================================================

/**
* 查询用户列表
*
* @param queryDTO 查询dto
* @return {@link PageVO}<{@link SysUserVO}>
*/
PageVO<SysUserVO> list(SysUserPageQueryDTO queryDTO);

/**
* 添加用户
*
* @param sysUserDTO 系统用户dto
*/
void add(SysUserDTO sysUserDTO);

/**
* 修改用户
*
* @param sysUserDTO 系统用户dto
*/
void updateById(SysUserDTO sysUserDTO);

/**
* 删除用户
*
* @param ids id
*/
void deleteById(Set<Long> ids);

}

定义 SysUserServiceImpl

在 Service 对应的实例 SysUserServiceImpl 中用注解 @Override 实现具体方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// money-pos/qk-money-app/money-app-system/src/main/java/com/money/service/impl/SysUserServiceImpl.java
package com.money.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.money.web.exception.BaseException;
import com.money.web.vo.PageVO;
import com.money.constant.SysErrorStatus;
import com.money.dto.SysUserDTO;
import com.money.dto.query.SysUserPageQueryDTO;
import com.money.entity.SysUser;
import com.money.entity.SysUserRoleRelation;
import com.money.mapper.SysUserMapper;
import com.money.service.SysRoleService;
import com.money.service.SysUserService;
import com.money.util.PageUtil;
import com.money.vo.SysUserVO;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@CacheConfig(cacheNames = "user")
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

private final PasswordEncoder passwordEncoder;

private final SysRoleService sysRoleService;

// 通过用户名获取用户
@Override
public SysUser getByUsername(String username) {
return this.lambdaQuery().eq(SysUser::getUsername, username).one();
}

// 查询用户列表
@Override
public PageVO<SysUserVO> list(SysUserPageQueryDTO queryDTO) {
IPage<SysUser> page = this.lambdaQuery()
.eq(ObjectUtil.isNotNull(queryDTO.getEnabled()), SysUser::getEnabled, queryDTO.getEnabled())
.like(StrUtil.isNotBlank(queryDTO.getPhone()), SysUser::getPhone, queryDTO.getPhone())
.and(StrUtil.isNotBlank(queryDTO.getName()), wrapper -> wrapper.like(SysUser::getUsername, queryDTO.getName())
.or(orWrapper -> orWrapper.like(SysUser::getNickname, queryDTO.getName())))
.orderByDesc(StrUtil.isBlank(queryDTO.getOrderBy()), SysUser::getLastTime)
.last(StrUtil.isNotBlank(queryDTO.getOrderBy()), queryDTO.getOrderBySql())
.page(PageUtil.toPage(queryDTO));
return PageUtil.toPageVO(page, sysUser -> {
SysUserVO sysUserVO = new SysUserVO();
BeanUtil.copyProperties(sysUser, sysUserVO);
sysUserVO.setRoles(sysRoleService.getByUser(sysUserVO.getId()));
return sysUserVO;
});
}

// 添加用户
@Override
public void add(SysUserDTO sysUserDTO) {
// 唯一性判断
if (this.getByUsername(sysUserDTO.getUsername()) != null) {
throw new BaseException(SysErrorStatus.DATA_ALREADY_EXIST, "用户已存在");
}
SysUser sysUser = new SysUser();
BeanUtil.copyProperties(sysUserDTO, sysUser);
// 加密密码
sysUser.setPassword(passwordEncoder.encode("123456"));
this.save(sysUser);
// 关联角色
List<SysUserRoleRelation> userRoleRelations = sysUserDTO.getRoles().stream().map(roleId -> {
SysUserRoleRelation relation = new SysUserRoleRelation();
relation.setUserId(sysUser.getId());
relation.setRoleId(roleId);
return relation;
}).collect(Collectors.toList());
sysRoleService.relate(userRoleRelations);
}

// 修改用户
@Override
public void updateById(SysUserDTO sysUserDTO) {
SysUser sysUser = new SysUser();
BeanUtil.copyProperties(sysUserDTO, sysUser);
// 用户名不允许修改
sysUser.setUsername(null);
this.updateById(sysUser);
Optional.ofNullable(sysUserDTO.getRoles()).ifPresent(roles -> {
// 重新关联角色
sysRoleService.relevanceByUser(sysUserDTO.getId());
List<SysUserRoleRelation> userRoleRelations = roles.stream().map(roleId -> {
SysUserRoleRelation relation = new SysUserRoleRelation();
relation.setUserId(sysUser.getId());
relation.setRoleId(roleId);
return relation;
}).collect(Collectors.toList());
sysRoleService.relate(userRoleRelations);
});
}

// 删除用户
@Override
public void deleteById(Set<Long> ids) {
this.removeBatchByIds(ids);
// 删除角色关联
ids.forEach(sysRoleService::relevanceByUser);
}

}

调用 sysUserService 方法

在登录校验时使用,通过用户名获取用户的所有信息。

getByUsername 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// money-pos/qk-money-app/money-app-system/src/main/java/com/money/service/impl/SysAuthServiceImpl.java
package com.money.service.impl;

import com.money.service.SysAuthService;
import com.money.service.SysUserService;
import com.money.dto.LoginDTO;
import com.money.entity.SysUser;
import com.money.vo.AuthTokenVO;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class SysAuthServiceImpl implements SysAuthService {

private final SysUserService sysUserService;

@Override
public AuthTokenVO login(LoginDTO loginDto) {
SysUser sysUser = sysUserService.getByUsername(loginDto.getUsername());
// ...
}
}

上边定义了 Service,接着再定义 Controller,login 作为接口接收前端传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.money.controller;

import com.money.dto.LoginDTO;
import com.money.service.SysAuthService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; // 包含 RequestMapping PostMapping RequestBody 等注解

import java.util.List;


@Tag(name = "auth", description = "认证访问")
@RestController
@RequiredArgsConstructor
@RequestMapping("/auth")
public class SysAuthController {

private final SysAuthService sysAuthService;

@Operation(summary = "登录")
@PostMapping("/login")
public AuthTokenVO login(@Validated @RequestBody LoginDTO loginDto) {
return sysAuthService.login(loginDto);
}
}