数据字典
业务字典表的作用即,我们需要为用户展示一些重复性的数据时,一般要在数据库里建一张字典表(通过软连接的方式关联其他表),为了方便前端使用(如渲染下拉框),通常需要将数据封装成列表,每个元素包含 Code 和 Name。
后端一般是通过前端传回的类别名,去数据库中,查询这一类别所有的字典,封装好之后传回给前端。
核心目标
根据前端传递的字典类别名(pcodes),从数据库查询所有相关的字典项,并将其封装成结构化的 Vo (Value Object) 列表返回给前端。每个字典项包含一个实际值 (Code) 和一个展示名 (Name)。
数据库结构
字典数据通过以下三个核心字段在数据库中记录:
P_CODE:字典的类别(如CAR_LEVEL)。DICT_CODE:字典项的编号/实际值(如1)。DICT_NAME:字典项的展示名(如轿车)。
💻 后端实现逻辑
整个流程通过 Controller、Service 和 Mapper 三层实现:
-
Controller 层 (
getDictRedis) :- 接收前端请求,通过
@RequestParam获取需要查询的字典类别列表pcodes。 - 调用
Service层方法进行处理。
- 接收前端请求,通过
-
Service 层 (
queryDictsByPcodes) : 核心封装逻辑-
查询数据库: 使用
QueryWrapper(如 MyBatis Plus)根据传入的pcodes查询所有有效的 (IS_EFFECTIVE="1") 字典数据 (PDict实体),并按sort字段排序。 -
数据分组 (Map 转换) : 利用 Java Stream API 的
Collectors.groupingBy(PDict::getPCode)将查询结果List<PDict>转换为Map<String, List<PDict>>,其中String是字典类别名 (P_CODE)。 -
Vo 封装: 遍历该 Map,将每个分组转换成前端所需的
DictGroupVo对象。- 外层
DictGroupVo设置类别名 (pCode)。 - 内层列表
List<DictItemVo>将PDict对象的DICT_CODE和DICT_NAME映射为DictItemVo。
- 外层
-
返回封装好的
List<DictGroupVo>给 Controller。
-
-
Mapper 层 (
PDictMapper) :- 继承
BaseMapper<PDict>,提供了基本的 CURD 操作来支持 Service 层的数据库查询。
- 继承
service层中实现逻辑:通过字典类别名去查询数据库,然后将查询到的数据转换成Map集合,再将Map转换成Vo传输给前端
具体的代码示例
controller层
@GetMapping("/v1/dict-redis")//前端的请求路径
public AjaxResult<List<DictGroupVo>> getDictRedis
(@RequestParam("pcodes") List<String> pcodes){ //前端传回需要字典类别
List<DictGroupVo> list = dictService.queryDictsByPcodes(pcodes);
return AjaxResult.success(list);
}
service层
//接口
List<DictGroupVo> queryDictsByPcodes (List<String> pcodes);
```
//实现
@Override
public List<DictGroupVo> queryDictsByPcodes(List<String> pcodes) {
if (pcodes == null || pcodes.isEmpty()){
return new ArrayList<>();
}
//1.查询数据库 使用QueryWrapper方便快速的查询
QueryWrapper<PDict> query = new QueryWrapper<>();
query.in("P_CODE",pcodes)
.eq("IS_EFFECTIVE","1")
.orderByAsc("sort");
List<PDict> allDicts = this.list(query);
//2.通过Map,存入allDicts的数据
Map<String,List<PDict>> groupMap = allDicts.stream()
.collect(Collectors.groupingBy(PDict::getPCode));```//因为数据库的字段是和PDict对应的
```
//3.将map转换为前端需要的Vo对象
List<DictGroupVo> result = new ArrayList<>();
//循环遍历组装数据
groupMap.forEach((pCode,dictList) ->{
DictGroupVo dictGroupVo = new DictGroupVo();
//设置外层分类类别名pCode
dictGroupVo.setDictCode(pCode);
//转换内层列表
List<DictItemVo> itemList = dictList.stream().map(d -> {
//这里的d就是Map<String,List<PDict>>中PDict的别名
DictItemVo dictItemVo = new DictItemVo();
dictItemVo.setDictCode(d.getDictCode());
dictItemVo.setDictName(d.getDictName());
return dictItemVo;
}).collect(Collectors.toList());
//组装好一条数据
dictGroupVo.setList(itemList);
//存入list中
result.add(dictGroupVo);
});
return result;//返回封装好的Vo给前端
}
```
Mapper层
@Mapper
public interface PDictMapper extends BaseMapper<PDict> {
//直接继承BaseMapper就好了,里面有写好的方法
}
Entity
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("p_dict")
public class PDict extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableField("P_CODE")
private String pCode;
// 对应 dict_code (具体值,如 1)
@TableField("DICT_CODE")
private String dictCode;
// 对应 dict_name (显示名,如 轿车)
@TableField("DICT_NAME")
private String dictName;
// 排序
@TableField("SORT")
private Integer sort;
// 是否有效
@TableField("IS_EFFECTIVE")
private String isEffective;
}
Vo
@Data
public class DictItemVo {
private String dictCode; // 实际的值,如 1
private String dictName; // 实际的名,如 轿车
}
@Data
public class DictGroupVo {
// 这里虽然叫 dictCode,但实际存的是 pCode (如 CAR_LEVEL)
// 这里需要跟前段的命名一致
private String dictCode;
// 具体的字典项列表
private List<DictItemVo> list;
}