结合上一篇我的单表增删改查,这一篇主要分析一下一对多的新增和修改以及查询的逻辑。
需求:
- 给用户分配角色
- 修改用户下的角色
- 查询和用户关联的菜单信息
用户,角色,菜单之间的关系是:用户 > 角色 > 菜单
根据需求可知还需要角色表以及用户角色关联表,如下关系图所示
准备工作上篇都介绍过了,直接开始实现需求
1.给用户分配角色
分别新建用户角色实体类,用户角色新增类
package com.example.management.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("user_role")
public class UserRole {
/**
* 主键
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 用户id
*/
@TableField(value = "user_id")
private Long userId;
/**
* 角色id
*/
@TableField(value = "role_id")
private Long roleId;
}
package com.example.management.domain.form;
import com.example.management.groups.Add;
import com.example.management.groups.Update;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserRoleSaveForm implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@NotNull(message = "id不能为空",groups = {Update.class})
private Long id;
/**
* 用户id
*/
@NotNull(message = "用户id不能为空", groups = {Update.class, Add.class})
private Long userId;
/**
* 角色id
*/
@NotEmpty(message = "角色id不能为空", groups = {Update.class, Add.class})
private List<Long> roleIds;
}
Controller层:
package com.example.management.controller;
import com.example.management.domain.form.UserRoleSaveForm;
import com.example.management.groups.Add;
import com.example.management.groups.Update;
import com.example.management.service.UserRoleService;
import com.example.management.util.Result;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/userRole")
@RequiredArgsConstructor
public class UserRoleController {
private final UserRoleService userRoleService;
/**
* 给用户分配角色
*/
@PostMapping("/save")
public Result save(@RequestBody @Validated(Add.class) UserRoleSaveForm form){
log.info("给用户添加角色:{}",form);
return userRoleService.addRole(form);
}
/**
* 修改用户下的角色
*/
@PutMapping("/update")
public Result update(@RequestBody @Validated(Update.class) UserRoleSaveForm form){
log.info("修改用户下的角色:{}",form);
return userRoleService.updateRole(form);
}
}
Service层:
package com.example.management.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.management.domain.entity.UserRole;
import com.example.management.domain.form.UserRoleSaveForm;
import com.example.management.util.Result;
public interface UserRoleService extends IService<UserRole> {
/**
* 给用户分配角色
* @param form
* @return
*/
Result addRole(UserRoleSaveForm form);
/**
* 修改用户下的角色
* @param form
* @return
*/
Result updateRole(UserRoleSaveForm form);
}
Service实现类逻辑:
因为用户和角色是一对多的关系
1.首先根据前端传的id去判断数据库中用户信息是否存在
2.不存在,抛出异常;
存在,根据前端传的获取角色id集合,遍历该集合得到每一个角色信息
3.调用角色接口根据每一个id查询对应的角色信息
4.判断角色信息在数据库中是否存在
5.不存在,抛出异常;
存在,说明有数据,此时可以给中间表中插入数据
6.new一个中间表 并赋值;
7.赋值完后将数据保存!
8.返回结果
package com.example.management.service.Impl;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.management.domain.entity.Role;
import com.example.management.domain.entity.User;
import com.example.management.domain.entity.UserRole;
import com.example.management.domain.form.UserRoleSaveForm;
import com.example.management.domain.form.UserSaveForm;
import com.example.management.mapper.RoleMapper;
import com.example.management.mapper.UserMapper;
import com.example.management.mapper.UserRoleMapper;
import com.example.management.service.UserRoleService;
import com.example.management.util.Result;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Slf4j
@Service
@RequiredArgsConstructor
public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> implements UserRoleService {
private final UserRoleMapper userRoleMapper;
private final UserMapper userMapper;
private final RoleMapper roleMapper;
/**
* 给用户分配角色,
* @param form
* @return
*/
@Override
public Result addRole(UserRoleSaveForm form) {
//判断用户信息是否存在
User user = userMapper.selectById(form.getUserId());
if (Objects.isNull(user)){
throw new RuntimeException("用户信息不存在");
}
//遍历前端传的角色id
for (Long roleId : form.getRoleIds()) {
//查询角色信息
Role role = roleMapper.selectById(roleId);
//判断角色信息是否存在
if (Objects.nonNull(role)){
//给用户分配角色
//new出中间表实体类
UserRole userRole = new UserRole();
//给实体类赋值
userRole.setUserId(form.getUserId());
userRole.setRoleId(roleId);
//插入数据
userRoleMapper.insert(userRole);
}else {
throw new RuntimeException("角色信息不存在");
}
}
return Result.success("角色分配成功");
}
2.修改用户下的角色
一对多的修改遵循先删后增
所以修改和新增的区别就在于修改用户下的角色的时候必须先将旧数据删除,在进行刚才的给用户分配角色步骤;
/**
* 修改用户下的角色
* @param form
* @return
*/
@Override
public Result updateRole(UserRoleSaveForm form) {
//删除旧数据
new LambdaUpdateChainWrapper<>(userRoleMapper)
.eq(UserRole::getUserId,form.getUserId())
.remove();
//判断用户
User user = userMapper.selectById(form.getUserId());
if (Objects.isNull(user)){
throw new RuntimeException("用户信息不存在");
}
//遍历前端传的角色id
for (Long roleId : form.getRoleIds()) {
//查询角色信息
Role role = roleMapper.selectById(roleId);
//判断角色信息是否存在
if (Objects.nonNull(role)){
//给用户分配角色
//new出中间表实体类
UserRole userRole = new UserRole();
//给实体类赋值
userRole.setUserId(form.getUserId());
userRole.setRoleId(roleId);
//插入数据
userRoleMapper.insert(userRole);
}else {
throw new RuntimeException("角色信息不存在");
}
}
return Result.success("角色分配成功");
}
3.查询和用户关联的菜单信息
根据需求可知还需要菜单表以及角色菜单关系表,如下所示
Controller层:
/**
*查询用户关联的菜单信息
*/
@GetMapping("/{id}")
public Result getById(@PathVariable("id") Long id){
log.info("查询用户关联的菜单信息");
return userRoleService.getMenuById(id);
}
Service层:
/**
* 查询用户关联的菜单信息
* @param id
* @return
*/
Result getMenuById(Long id);
Service实现类逻辑:
实现思想是:先根据id查询出用户信息,再根据用户角色关系表查询出角色信息,然后根据角色菜单关系表查出菜单信息。最后将查询出来的菜单信息返回给前端
1.根据id查询用户信息
2.判断用户信息是否存在
不存在,抛出异常;存在:
3.根据用户角色关系表查出和用户相关的角色信息
4.用Stream流转成角色ids
5.根据角色菜单关系表查出和角色相关的菜单信息
6.用Stream流转成菜单ids
7.遍历菜单ids
8.调用菜单接口根据遍历的每一个菜单id去数据库中查询数据是否存在
9.new出返回给前端的菜单vo
10.将菜单信息返回给前端
方式一:直接代码实现
/**
* 查询用户关联的菜单信息
* @param id
* @return
*/
@Override
public Result getMenuById(Long id) {
//根据id查询用户信息
User user = userMapper.selectById(id);
//判断用户信息是否存在
if (Objects.isNull(user)){
throw new RuntimeException("用户信息不存在");
}
//根据用户角色关系表查出和用户想关的角色信息
List<UserRole> list = new LambdaQueryChainWrapper<>(userRoleMapper)
.in(UserRole::getUserId, id)
.list();
//用Stream流转成角色ids
List<Long> roleIds = list.stream().map(UserRole::getRoleId).collect(Collectors.toList());
//根据角色菜单关系表查出每个角色相关的菜单
List<RoleMenu> roleList = new LambdaQueryChainWrapper<>(roleMenuMapper)
.in(RoleMenu::getRoleId, roleIds)
.list();
//用Stream流转成菜单ids
List<Long> menuIds = roleList.stream().map(RoleMenu::getMenuId).collect(Collectors.toList());
//遍历菜单ids
ArrayList<MenuVo> menuList = new ArrayList<>();
for (Long menuId : menuIds) {
//调用菜单接口根据遍历的每一个菜单id去数据库中查询数据是否存在
Menu menu = menuMapper.selectById(menuId);
//判断
if (Objects.nonNull(menu)){
//new出返回给前端的菜单vo
MenuVo menuVo = new MenuVo();
BeanUtils.copyProperties(menu,menuVo);
menuList.add(menuVo);
}else {
throw new RuntimeException("菜单信息不存在");
}
}
return Result.success(menuList);
}
方式二:用sql实现
/**
* 查询用户关联的菜单信息
*
* @param id
* @return
*/
@Override
public MenuVo getMenuById(Long id) {
//根据id查询用户信息
User user = userMapper.selectById(id);
//判断用户信息是否存在
if (Objects.isNull(user)) {
throw new RuntimeException("用户信息不存在");
}
//调用userRoleMapper用sql语句实现查询
return userRoleMapper.selectMenuById(id);
mapper接口:
package com.example.management.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.management.domain.entity.UserRole;
import com.example.management.domain.vo.MenuVo;
public interface UserRoleMapper extends BaseMapper<UserRole> {
MenuVo selectMenuById(Long id);
}
mapper.xml文件中两种sql实现查找:
1.
<?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.example.management.mapper.UserRoleMapper"> <select id="selectMenuById" resultType="com.example.management.domain.vo.MenuVo"> select m.* from user_role ur, role_menu rm, menu m where ur.user_id = ur.id and rm.role_id = ur.role_id and m.id = rm.menu_id; </select> </mapper>
2.
select m.* from role_menu rm right join user_role ur on ur.role_id = rm.role_id and ur.user_id = ur.id join menu m on m.id = rm.menu_id
⭐sql实现步骤:
第一种(隐式内连接): #第一步:写整体的结构:select * from 涉及到的表 where 条件 #第二步:把 * 替换成具体的字段 #第三步:查询条件 分为 1.表和表之间的关联字段相等 # 2.具体的筛选条件(比如查询名字为张三的用户) #第四步:排序用 order by...
第二种(外连接): #第一步:首先找出这几张表中都关联的表,把他写在最前面,然后用它和另外的几张表做join操作, #需要注意的是 使用什么join(left right inner之间的区别) #第二步:然后根据表和表的相同的字段做条件,如果有其他条件可以写在相对于的关系后面