四 拓展功能
1 代码生成器
① 安装MyBatisPlus插件(可根据表信息生成代码)
② 配置数据库信息 – Config Database
// mp_test为数据库
jdbc:mysql://localhost:3306/mp_test?useSSL=false&serverTimezone=Asia/Shanghai
③ 生成数据库表
create table tb_student
(
id int not null
primary key,
name varchar(12) not null,
phone int null
)
comment '学生';
④ 生成代码 – Code Generator
2 DB静态工具
DB功能与IService接口相近,但IService接口中的方法是非静态的,而DB中的方法是静态的,相对于IService,DB需要多传递一个实体类的字节码参数,才能利用反射进行crud(除了save和update因为传了实体类对象),由此可避免循环依赖的问题(ServiceA,ServiceB相互注入),当ServiceA需要调用ServiceB时,直接使用静态工具即可,无需注入,避免循环依赖。
① 生成地址VO实体
@Data
@ApiModel(description = "地址VO")
public class AddressVO {
@ApiModelProperty("id")
private Integer id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("地址")
private String address;
@ApiModelProperty("电话")
private Integer phone;
}
② 创建地址表 – 需自行编写测试数据user_id相等即可
create table tb_address
(
id int auto_increment
primary key,
user_id int null,
username varchar(11) null,
address varchar(20) null,
phone varchar(11) null
)
comment '地址';
③ 更新用户VO实体
@Data
@ApiModel(description = "用户VO实体")
@TableName("tb_user")
public class UserVo {
@ApiModelProperty("id")
private Integer id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("是否玩家")
private Boolean isPlayer;
@ApiModelProperty("测关键字")
private Integer order;
@ApiModelProperty("地址")
private List<AddressVO> addresses;
}
④ 更新 UserController 方法 – 根据单个ID多表查询
@ApiOperation("通过id查询用户接口")
@GetMapping("{id}")
public UserVo queryUserById(@ApiParam("用户id")@PathVariable("id")Long id){
return userService.queryUserAndAddressById(id);
}
⑤ 更新IUserService,IUserServiceImpl
public interface IUserService extends IService<User> {
UserVo queryUserAndAddressById(Long id);
}
@Override
public UserVo queryUserAndAddressById(Long id) {
// 1.查询用户
User user = getById(id);
if(user==null){
throw new RuntimeException("用户不存在");
}
// 2.查询地址
List<Address> addressList = Db.lambdaQuery(Address.class)
.eq(Address::getUserId, id).list();
// 3.封装VO
UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
if(CollUtil.isNotEmpty(addressList)){
userVo.setAddresses(BeanUtil.copyToList(addressList, AddressVO.class));
}
return userVo;
}
⑥ 多表查询测试 – 通过单个userId查询多个地址
⑦ 更新 UserController 方法 – 根据多个ID多表查询
@ApiOperation("通过id批量查询用户接口")
@GetMapping
public List<UserVo> queryUserByIds(@ApiParam("用户id集合")@RequestParam("ids")List<Long> ids){
return userService.queryUserAndAddressByIds(ids);
}
⑧ 更新IUserService,IUserServiceImpl
List<UserVo> queryUserAndAddressByIds(List<Long> ids);
@Override
public List<UserVo> queryUserAndAddressByIds(List<Long> ids) {
// 1.查询用户
List<User> users = listByIds(ids);
if(CollUtil.isEmpty(users)){
// 使用 Collections.emptyList() 返回一个空列表 避免返回 null
return Collections.emptyList();
}
// 2.获取用户id集合,通过user集合获取id集合
List<Integer> userIds = users.stream().map(User::getId).collect(Collectors.toList());
// 3.通过用户id查询地址集合(所有用户的所有地址)
List<Address> addressList = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
// 4.转换地址VO
List<AddressVO> addressVOList = BeanUtil.copyToList(addressList, AddressVO.class);
// 5. 若查询结果非空,将地址集合根据用户 ID 进行分组,key为用户ID,Value为用户对应的地址集合
Map<Integer, List<AddressVO>> addressMap = CollUtil.isEmpty(addressVOList)
? Collections.emptyMap()
: addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
// 6.转VO返回 提前定义集合大小防止临时扩容
List<UserVo> list = new ArrayList<>(users.size());
for (User user:users){
// 转UserPO为VO
UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
list.add(userVo);
// 转地址VO
userVo.setAddresses(addressMap.get(user.getId()));
}
return list;
}
⑨ 多表查询测试 – 通过userId集合查询多个地址
3 逻辑删除
基于代码逻辑模拟删除效果,但不删除数据,通常为添加标记字段,删除时将数据标为1,只查询标记为0的数据,若数据本身为0,则不作更改。
MybatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。在application.yaml文件中配置逻辑删除的字段名称和值即可
需注意由于逻辑删除实际上并未删除数据,且会增加sql语句的逻辑判断,导致影响效率,因此可以考虑将要删除的数据迁移到其他表中,完成删除操作。
① 填写yaml文件
mybatis-plus:
# 因mp不善于多表查询,故多表查询仍需手写
type-aliases-package: com.tj.mp.demo.domain
mapper-locations: classpath*:mapper/**/*.xml
global-config:
db-config:
#设置全局逻辑删除的实体字段名,字段类型可以是boolean、integer
logic-delete-field: deleted
#值设为1表示删除(默认为1)
logic-delete-value: 1
#值设为0表示未删除(默认为0)
logic-not-delete-value: 0
② 数据库增添逻辑删除字段 – deleted
alter table tb_address
add `deleted` bit default 0 not null;
③ 实体类增添逻辑删除成员变量 – deleted
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_address")
@ApiModel(value="Address对象", description="地址")
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Integer userId;
private String username;
private String address;
private String phone;
private Boolean deleted;
}
④ 测试类
@SpringBootTest
class IAddressServiceTest {
@Autowired
private IAddressService addressService;
@Test
void testLogicDelete(){
// 1.根据用户id进行删除
// UPDATE tb_address SET deleted=1 WHERE id=? AND deleted=0
addressService.removeById(1);
// 2.根据用户id进行查询
// SELECT id,user_id,username,address,phone,deleted FROM tb_address WHERE id=? AND deleted=0
System.out.println(addressService.getById(1));
}
}
4 枚举处理器
根据业务用户需要有多种状态,可供判断与赋值,应使用枚举类型(可使用==进行比较)维护可读性,但数据库中仍为整数类型,此时应做java枚举类型与mysql整数类型的转换(mybatis-plus提供)
① 配置yaml文件
mybatis-plus:
type-aliases-package: com.tj.mp.demo.domain
mapper-locations: classpath*:mapper/**/*.xml
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
# 配置全局枚举处理器
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
② 更新数据库字段
alter table tb_user
add status int default 1 null;
③ 定义枚举类
@Getter
public enum UserStatus {
NORMAL(1, "正常"),
FROZEN(2, "冻结"),
;
// 对应数据库字段与成员变量进行绑定
@EnumValue
private final int value;
// 枚举默认以枚举项名(FROZEN)返回前端,需通过注解告诉springMVC,想返给前端的数据
@JsonValue
private final String desc;// 描述
UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
}
④ 更新PO实体类 – User
@Data
@AllArgsConstructor
@NoArgsConstructor
// PO 是与数据库表结构直接映射的对象,用于表示数据库中的记录。
@TableName("tb_user")
public class User {
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
@TableField(value = "username")
private String username;
@TableField("is_player")
private Boolean isPlayer;
@TableField("`order`")
private Integer order;
@TableField(exist = false)
private String address;
// 枚举类型状态
private UserStatus status;
}
⑤ 更新PO实体类 – User
@Data
@ApiModel(description = "用户VO实体")
// VO 是为前端视图(如网页、移动端界面)设计的对象,用于展示数据。
@TableName("tb_user")
public class UserVo {
@ApiModelProperty("id")
private Integer id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("是否玩家")
private Boolean isPlayer;
@ApiModelProperty("测关键字")
private Integer order;
@ApiModelProperty("地址")
private List<AddressVO> addresses;
// 枚举类型状态
@ApiModelProperty("状态")
private UserStatus status;
}
⑥ 更新所涉及的方法 – User
@Override
public UserVo queryUserAndAddressById(Long id) {
// 1.查询用户 判断用户状态
User user = getById(id);
if(user==null||user.getStatus()== UserStatus.FROZEN){
throw new RuntimeException("用户状态异常");
}
// 2.查询地址
List<Address> addressList = Db.lambdaQuery(Address.class)
.eq(Address::getUserId, id).list();
// 3.封装VO
UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
if(CollUtil.isNotEmpty(addressList)){
userVo.setAddresses(BeanUtil.copyToList(addressList, AddressVO.class));
}
return userVo;
}
⑦ 测试
5 JSON处理器
解决数据库中JSON与java类型的转换
① 更新数据库JSON字段
ALTER TABLE tb_user
ADD info JSON DEFAULT '{}';
② 定义信息实体对象
@Data
@NoArgsConstructor
// 为这个构造方法创建一个静态工厂方法,方法名为 of
@AllArgsConstructor(staticName = "of")
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}
③ 更新PO实体类JSON成员变量
@Data
@AllArgsConstructor
@NoArgsConstructor
// 设置自动结果映射为true
@TableName(value = "tb_user",autoResultMap = true)
public class User {
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
@TableField(value = "username")
private String username;
@TableField("is_player")
private Boolean isPlayer;
@TableField("`order`")
private Integer order;
@TableField(exist = false)
private String address;
private UserStatus status;
// 通过注解绑定JSON类型处理器(只针对当前字段有效)
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
}
④ 更新VO实体类JSON成员变量
@Data
@ApiModel(description = "用户VO实体")
@TableName("tb_user")
public class UserVo {
@ApiModelProperty("id")
private Integer id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("是否玩家")
private Boolean isPlayer;
@ApiModelProperty("测关键字")
private Integer order;
@ApiModelProperty("地址")
private List<AddressVO> addresses;
@ApiModelProperty("状态")
private UserStatus status;
@ApiModelProperty("信息")
// 通过注解绑定JSON类处理器(只针对当前字段有效)
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
}
⑤ 插入测试
@SpringBootTest
class IUserServiceTest {
@Autowired
private IUserService userService;
@Test
void testSaveUser(){
User user = new User();
user.setId(9);
user.setUsername("black");
user.setIsPlayer(true);
user.setOrder(3);
user.setInfo(UserInfo.of(22,"项目经理","男"));
userService.save(user);
}
}
⑥ 查询