1.说明
2.model与数据库
- 以基本角色表为例
- 实体类使用了lombok,需要进行相关配置
/**
* 基本角色表
*/
@Data
@TableName(value = "base_role")
public class BaseRole extends BaseModel {
//角色别名
@NotEmpty(message = "角色别名不能为空")
private String code;
//角色名称
@NotEmpty(message = "角色名称不能为空")
private String name;
//父角色id
private Integer parent_id;
//该角色所有的父角色id集合,数据格式:[0],[2],[152],
private String parent_ids;
//该角色的层级,一般与父角色的数量一致
private Integer level;
//角色说明
private String remark;
}
3. 数据的新增与修改
- 数据新增思路
1.获取要保存的model
2.设置 parent_ids属性值和 level属性值
3.保存model
- 数据修改思路
1.获取修改后的model
2.修改 parent_ids属性值和 level属性值
3.获取角色修改前的所有子角色,对子角色的 parent_ids属性值和 level属性值进行修改并保存.
4.对修改后的model进行完整更新,同步到数据库
- 在数据的修改上,需要首先实现获取角色的所有子角色功能
List<BaseRole> getLikeParentIds(Integer id);
<select id="getLikeParentIds" resultType="com.****.model.base.BaseRole">
SELECT a.* FROM base_role a where 1=1
<if test="id != null and id != ''">
and parent_ids LIKE CONCAT('%$[',#{id},'$]%') escape '$'
</if>
</select>
- 在角色的parent_ids字段里,保存了该角色的所有上级角色的id,以 [0],[2],[152], 这样的格式记录.因此只需要加上一个模糊查询,就能方便的获取到所有子角色
- 接下来需要实现 设置parent_ids属性值和 level属性值 的方法
/**
* 设置一个角色的parent_ids属性和level属性
* @param model
*/
public void setParentIds(BaseRole model){
Assert.isTrue(model!=null,BaseExceptionEnum.PARA_ERROR);
Integer parent_id=model.getParent_id();
if(model.getParent_id()!=null){
//如果修改了父角色,验证设置的父对象是否合法,不能将自身的子对象设置为自己的父对象
if(!ModelUtil.isNew(model)){
BaseRole oldModel=super.getById(model.getId());
Assert.isTrue(oldModel!=null,BaseExceptionEnum.PARA_ERROR);
String old_parent_ids=oldModel.getParent_ids();
List<BaseRole> roles=roleDao.getLikeParentIds(oldModel.getId());
for(BaseRole role:roles){
Assert.isTrue(!role.getId().equals(model.getParent_id()),"不能将自身的子角色设置为自己的父角色");
}
}
//获取父角色的parent_ids
//父角色的parent_ids + 父角色的id = 当前角色的parent_ids
BaseRole parentModel=super.getById(parent_id);
Assert.isTrue(parentModel!=null,BaseExceptionEnum.PARA_ERROR);
model.setParent_ids(parentModel.getParent_ids()+"["+model.getParent_id()+"],");
//设置层级
//父角色的层级 + 1 = 当前角色的层级
int level=0;
if( parentModel.getLevel()!=null){
level = parentModel.getLevel();
}
model.setLevel(level+1);
}else{
model.setParent_ids("");
model.setLevel(1);
}
}
- 在角色存在父级信息的时候才进行相关处理,在处理前先验证一下是否设置成了闭合的父子关系.最后在对角色的parent_ids和level字段进行设置值.
- 之后,实现编辑所需要的 更新所有子角色的 parent_ids属性值和 level属性值 的方法
/**
* 在修改后,更新所有子角色的结构
* @param oldRole
* @param newRole
*/
public void updataAllChildParentIdsForEdit(BaseRole oldRole, BaseRole newRole){
/*
例子:
oldRole是 A , A的父级是P ,newRole是 N
A 有 两个子级 ,是 A1 和 A2
A2 有个子级是 A21
* */
/*
获取所有的子角色
相当于获取了 A1,A2,A21 三个对象
* */
List<BaseRole> roles=roleDao.getLikeParentIds(oldRole.getId());
/*
* 获取所有子级的公共父级元素信息
* A1,A2,A21三个子级元素都有父级元素 [A],[P],
* 将A的所有父级加上A本身,就是所有子级的公共父级元素
* */
String oldPidsPrefix = oldRole.getParent_ids() + "[" + oldRole.getId() + "],";
//遍历所有子级对象
for(BaseRole role:roles){
//更新当前子角色的所有父级元素信息
/*
获取当前角色的非公共得到父级元素信息
A21除了有公共的[A],[P],父级外,还有一个父级[A2]
角色的所有父级信息是按层级顺序排的,截去前面的公共父级,后面的就是非公共父级元素信息
* */
String oldPidsSuffix = role.getParent_ids().substring(oldPidsPrefix.length());
/*
创建当前子角色新的父级元素信息
N的所有父级元素 + N本身 +当前角色非公共父级元素信息 = 新的所有父级元素信息
* */
String role_parent_ids = newRole.getParent_ids() + "[" + newRole.getId() + "]," + oldPidsSuffix;
role.setParent_ids(role_parent_ids);
//更新层级数,角色的父级元素数量+1
int level = StrUtil.count(role_parent_ids, "[");
role.setLevel(level);
super.updateById(role);
}
}
- 代码很短,但逻辑上有有点绕.我在注释上用了一个例子进行解释说明.
/**
* 添加或编辑
* @param model
*/
@Transactional(rollbackFor=Exception.class)
@Override
public void addOrEdit(BaseRole model) {
Assert.isTrue(super.isUnique(model,"code"),"角色别名不能重复");
Assert.isTrue(super.isUnique(model,"name"),"角色名称不能重复");
//设置 parent_ids属性值和 level属性值
setParentIds(model);
//新增的数据直接保存
if(ModelUtil.isNew(model)){
super.saveOrUpdate(model);
}else{
//在进行修改时,更新子角色
BaseRole oldModel=super.getById(model.getId());
updataAllChildParentIdsForEdit(oldModel,model);
//完整更新
super.updateIntact(model);
}
}
- 由于暂时没有其他的操作,我把新增和编辑合并到了一个方法里.思路还是一样的
- 编辑时需要通过完整更新进行保存.完整更新是指 对于为null的字段也更新到数据库.
因为会存在一些特殊情况,将一个有父级角色的数据修改成没有父级角色,那么修改后的parent_id值就会是null,在调用mybatis plus 提供的保存方法时,为null的值将视为不修改,数据库的值不会发生修改.
完整更新的实现可以参考https://www.jianshu.com/p/8a83f29e79d4
4.数据的删除
- 数据删除的逻辑可根据实际的业务情况进行调整,这里实现了两种逻辑
单一删除:删除一个角色数据,修改所有子角色的结构,用被删除角色的父角色代替被删除的角色.
整体删除:删除一个角色数据,该角色下的所有子角色也将被删除.
/**
* 删除(不包括子角色)
* @param ids
*/
@Transactional(rollbackFor=Exception.class)
@Override
public void delete(Integer[] ids){
Assert.isTrue(ids!=null, BaseExceptionEnum.PARA_ERROR);
for(int i=0;i<ids.length;i++){
Integer id=ids[i];
BaseRole baseRole=super.getById(id);
Assert.isTrue(baseRole!=null,BaseExceptionEnum.PARA_ERROR);
updataAllChildParentIdsForDelete(baseRole);
super.removeById(id);
}
}
/**
* 删除,包括子角色
* @param ids
*/
@Transactional(rollbackFor=Exception.class)
@Override
public void deleteChildren(Integer[] ids){
Assert.isTrue(ids!=null, BaseExceptionEnum.PARA_ERROR);
for(int i=0;i<ids.length;i++){
Integer id=ids[i];
BaseRole baseRole=super.getById(id);
Assert.isTrue(baseRole!=null,BaseExceptionEnum.PARA_ERROR);
deleteAllChild(baseRole);
super.removeById(id);
}
}
/**
*在删除后,修改子角色的继承关系和层级
* @param byDelete 被删除的角色
*/
public void updataAllChildParentIdsForDelete(BaseRole byDelete){
List<BaseRole> roles=roleDao.getLikeParentIds(byDelete.getId());
for(BaseRole role:roles){
String new_parent_ids=StrUtil.replace(role.getParent_ids(),"["+byDelete.getId()+"]","");
role.setParent_ids(new_parent_ids);
role.setParent_id(byDelete.getParent_id());
role.setLevel(role.getLevel()-1);
super.updateById(role);
}
}
4.数据的查询
- 没有采取在后端进行数据树状结构的封装.数据直接提供给前端,由 Layui 第三方组件treetable-lay来完成数据树状展示.
网友评论