业务介绍
数据库存入的是value值(或者是id),需要将查询出来的数据的字典值与内容进行转换,内容进行展示。
实现方式
1.在sql使用子查询
2.通过springAop切面+注解的方式
优缺点
1.优点:简单,直接 缺点:sql太多,难以维护
2.优点:不需要在每个业务sql中子查询sql,反射与业务交织在一起,代码不好维护(用了大量的递归---为了实现属性中存在对象或者集合对象的情况)
实现思路
通过对返回结果进行处理,查看对应的属性上有没有需要转换注解修饰,有的话则获取,拿到对应的值,通过sql查询出来,在对转换注解修饰的属性进行赋值
代码
转换注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface DataDict {
/**
* 赋值的字典字段
* @return
*/
String dictField() default "";
/**
* 字典code
* @return
*/
String dictCode() default "";
}
字典处理类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DictDto {
/**
* 字典编码
*/
private String code;
/**
* 当前字段名称
*/
private String name;
/**
* 需要赋值的字段名称
*/
private String dictField;
}
字典工具类
@Component
public class DictUtil {
@Resource
private PlmBaseMapper plmBaseMapper;
@SneakyThrows
public Boolean dealDict(List<DictDto> dictParams, Map<String, String> dictInfoMap) {
if (dictParams.size() == 0) {
return Boolean.FALSE;
}
List<String> codes = getCodes(dictParams);
//根据code 获取对应的字典内容---可以放在redis中
List<DictVO> dictList = plmBaseMapper.findDictList(codes);
if (dictList == null || dictList.isEmpty()) {
return Boolean.FALSE;
}
//先把字典值转成map
for (DictVO dictVO : dictList) {
dictInfoMap.put(dictVO.getCode() + "_" + dictVO.getValue(), dictVO.getName());
}
return Boolean.TRUE;
}
/**
* 获取实体中所有需要翻译的code
*
* @param dictDtos
* @return
*/
private List<String> getCodes(List<DictDto> dictDtos) {
if (CollectionUtil.isEmpty(dictDtos)) {
return new ArrayList<>();
}
List<String> collect = dictDtos.stream()
.map(dictDto -> dictDto.getCode()).collect(Collectors.toList());
collect.stream().distinct();
return collect;
}
}
参数获取代码
@Slf4j
public class DataParamUtil {
/**
* 用户key
*/
public static final String USER_KEY = "USER_KEY";
/**
* 字典key
*/
public static final String DICT_KEY = "DICT_KEY";
/**
* 现在实现了用户和字典,后期有其他的继续往后追加
*
* @param obj
* @return
* @throws IllegalAccessException
*/
public static Map<String, List> getMapping(Object obj) throws IllegalAccessException {
Map<String, List> resultMap = new HashMap<>(2);
//字典信息
List<DictDto> dictDtoList = new ArrayList<>();
//需要判断是否为集合,集合包含List<List<User>>的情况
loopMapping(obj, dictDtoList);
resultMap.put(DICT_KEY, dictDtoList);
return resultMap;
}
private static void loopMapping(Object obj, List<DictDto> dictDtoList) throws IllegalAccessException {
Field[] fields;
if (obj instanceof Collection) {
Collection olist = ((Collection) obj);
if (olist == null || olist.size() == 0) {
return;
}
for (Object temp : olist) {
loopMapping(temp,dictDtoList);
}
} else {
//单对象
fields = obj.getClass().getDeclaredFields();
loopFields(fields, obj, null, dictDtoList);
}
}
/**
* 增加一个前缀,表示属性值中包含集合其他属性的情况
* 反射性能消耗资源大,多个注解的获取尽量在一次循环中获取(设置值也是一样)
* 除了获取对应注解的值,也需要判断当前属性类型是否为对象或者是集合,分别进行处理
*
* @param fields
* @param obj
* @param prefix
* @param userDtoList
* @param dictDtoList
* @throws IllegalAccessException
*/
private static void loopFields(Field[] fields, Object obj, String prefix,
List<DictDto> dictDtoList) throws IllegalAccessException {
for (Field field : fields) {
//去掉安全检查
field.setAccessible(true);
if (field.isAnnotationPresent(DataDict.class)) {
//获取字典信息
getDictMapping(field, prefix, dictDtoList);
continue;
}
//判断该字段是否为list
if (Collection.class.isAssignableFrom(field.getType())) {
//是集合,又需要循环获取每个字段
Collection olist = (Collection) field.get(obj);
if (olist == null || olist.size() == 0) {
return;
}
for (Object temp : olist) {
fields = temp.getClass().getDeclaredFields();
//循环获取字段值
loopFields(fields, temp, field.getName(), dictDtoList);
}
continue;
}
//属于对象--包含当前包下的数据--写死类型
boolean isObject = field.getType().toString().contains("com.*.model");
if (isObject) {
Class clazz = field.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
loopFields(declaredFields, obj, field.getName(),dictDtoList);
}
}
}
/**
* 获取字典映射信息
*
* @param field
* @param dictDtoList
* @throws IllegalAccessException
*/
private static void getDictMapping(Field field, String prefix, List<DictDto> dictDtoList) {
DataDict dataDict = field.getAnnotation(DataDict.class);
String curName = StringUtils.isNotBlank(prefix) ? prefix + "." + field.getName() : field.getName();
dictDtoList.add(new DictDto(dataDict.dictCode(), curName, dataDict.dictField()));
}
}
设置结果代码
@Component
@Slf4j
public class DataResultUtil {
@Autowired
private DictUtil dIctUtil;
/**
* 存放对应字典信息
*/
private Map<String, String> dictInfoMap = new HashMap<>();
/**
* 字典参数存放
*/
private List<DictDto> dictParams = new ArrayList<>();
/**
* 思路:获取结果中的数据,然后反射获取对应的注解,获取注解的值数据,通过注解值,查询对应的数据,然后在设置到对应的字段上
* 结果:分为分页和单结果,主要是为了获取数据的方式不一样
* 需要考虑:如果结果对象中包含其他对象,包含集合对象的情况,需要将子属性也要处理
*
* @param resultR
* @param annotation
* @return
* @throws Throwable
*/
public Object translation(Object resultR, DataDeal annotation) throws Throwable {
// 拿到要返回的数据--这里可以打印下看看内容
if (ObjectUtil.isNull(resultR)) {
return resultR;
}
Object result = ((RestResult) resultR).getData();
if (result instanceof PageVO) {
// 分页的情况
((RestResult) resultR).setData(translationPage(result, annotation));
return resultR;
}
if (setParamValue(result)) {
return result;
}
result = translate(result, annotation);
((RestResult) resultR).setData(result);
return resultR;
}
/**
* 给字典赋值
*
* @param result
* @return
* @throws IllegalAccessException
*/
private boolean setParamValue(Object result) throws IllegalAccessException {
// 获取所有配置注解的code 和实体字段
Map<String, List> mapping = DataParamUtil.getMapping(result);
//字典参数
dictParams = mapping.get(DataParamUtil.DICT_KEY);
//两者都没有字段需要转换,直接返回
if (dictParams.isEmpty() ) {
return true;
}
//字典参数
Boolean dictPass = dIctUtil.dealDict(dictParams, dictInfoMap);
if (!dictPass &&) {
//不通过,直接返回
return true;
}
return false;
}
/**
* 转换单个数据
*
* @param result
* @param annotation
* @return
*/
private Object translate(Object result, DataDeal annotation) throws IllegalAccessException {
//处理数据信息
if (result instanceof List || result instanceof ArrayList) {
for (Object entity : (List) result) {
assign(entity, annotation);
}
} else {
assign(result, annotation);
}
return result;
}
/**
* 给字段赋值,这里处理字典和用户参数:后面如果还有处理逻辑,继续往后追加集合
*
* @param entity
* @param annotation
*/
private void assign(Object entity, DataDeal annotation) {
int dictSize = dictParams.size();
int userSize = userParams.size();
int loopIndex = dictSize > userSize ? dictSize : userSize;
for (int i = 0; i < loopIndex; i++) {
//字典处理
if (dictSize > i && annotation.dict()) {
setDictParam(entity, dictParams, i);
}
//用户处理
if (userSize > i && annotation.user()) {
setUserParam(entity, userParams, i);
}
}
}
/**
* 设置用户参数
*
* @param entity
* @param userParams
*/
private void setUserParam(Object entity, List<UserDto> userParams, int i) {
UserDto userDto = userParams.get(i);
String userId = userDto.getUserId();
String userField = userDto.getDictField();
String name = userDto.getName();
try {
String[] names = name.split("\\.");
loopFields(0, entity, names, userField, userId, userInfoMap);
} catch (Exception e) {
log.error("设置值出错", e.getMessage());
}
}
/**
* 循环递归设置list中的数据
*
* @param curIndex
* @param entity
* @param names
* @param field
* @param key
* @param map
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private void loopFields(int curIndex, Object entity, String[] names, String field, String key, Map<String, String> map)
throws NoSuchFieldException, IllegalAccessException {
Class c = entity.getClass();
if (null == c) {
return;
}
if (curIndex == names.length - 1) {
setFieldValue(curIndex, entity, names, field, key, map);
return;
}
//下面还有数据
Field f = c.getDeclaredField(names[curIndex]);
f.setAccessible(true);
Object objList = f.get(entity);
if (Objects.isNull(objList)) {
return;
}
objList = getObj(objList);
if (Objects.isNull(objList)) {
return;
}
loopFields(++curIndex, objList, names, field, key, map);
}
/**
* 设置字段值
*
* @param curIndex
* @param entity
* @param names
* @param field
* @param key
* @param map
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private void setFieldValue(int curIndex, Object entity, String[] names, String field, String key, Map<String, String> map)
throws NoSuchFieldException, IllegalAccessException {
Class c = entity.getClass();
//需要赋值了
if (c != null) {
Field f = c.getDeclaredField(names[curIndex]);
f.setAccessible(true);
Object preValue = f.get(entity);
if (ObjectUtil.isNotNull(preValue)) {
// 需要赋值的字段
Field fvalue = c.getDeclaredField(field);
fvalue.setAccessible(true);
//如果字段值不为空得话,就直接返回
if (null != fvalue.get(entity)) {
return;
}
//注意key拼接的格式
String fieldValue = map.getOrDefault(key + "_" + preValue.toString(), null);
fvalue.set(entity, fieldValue);
}
}
}
/**
* 设置字典参数
*
* @param entity
* @param dictParams
* @param i
*/
private void setDictParam(Object entity, List<DictDto> dictParams, int i) {
DictDto dictDto = dictParams.get(i);
String code = dictDto.getCode();
String name = dictDto.getName();
String dictField = dictDto.getDictField();
try {
String[] names = name.split("\\.");
loopFields(0, entity, names, dictField, code, dictInfoMap);
} catch (Exception e) {
log.error("设置值出错", e.getMessage());
}
}
/**
* 转换多个数据
*
* @param resultR
* @param annotation
* @return
*/
private Object translationPage(Object resultR, DataDeal annotation) throws Throwable {
long s = System.currentTimeMillis();
log.info("开始处理字典....");
if (resultR == null) {
return resultR;
}
PageVO page = (PageVO) resultR;
Object result = page.getData();
if (ObjectUtil.isNull(result)) {
return resultR;
}
List dealList = (List) result;
List resultList = new ArrayList();
if (setParamValue(result)) {
return result;
}
for (Object temp : dealList) {
resultList.add(translate(temp, annotation));
}
page.setData(resultList);
// 返回处理后的数据
log.info("字典处理完毕....结束时间为:{}毫秒", System.currentTimeMillis() - s);
return page;
}
/**
* 判断单个数据是集合还是一个对象,只获取一个对象
*
* @param result
* @return
*/
public static Object getObj(Object result) {
Object obj = null;
if (result instanceof Collection) {
Collection olist = ((Collection) result);
if (olist == null || olist.size() == 0) {
return null;
}
Iterator iterator = olist.iterator();
while (iterator.hasNext()) {
obj = iterator.next();
break;
}
} else {
obj = result;
}
return obj;
}
}
切面注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DataDeal {
/**
* 字典处理
* @return
*/
boolean dict() default false;
/**
* 用户处理
* @return
*/
boolean user() default false;
}
切面处理类
@Component
@Aspect
public class DataAspect {
@Autowired
private DataResultUtil dataResultUtil;
@AfterReturning(pointcut = "@annotation(com.*.common.annotation.DataDeal)", returning = "result")
public Object dealData(JoinPoint pjp, Object result) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
DataDeal annotation = method.getAnnotation(DataDeal.class);
if (annotation == null || (!annotation.dict() && !annotation.user())) {
return result;
}
//存在注解,判断注解的值然后调用对应的方法
return dataResultUtil.translation(result, annotation);
}
}
具体使用情况
备注
我是实现了用户转换的,代码中有关于用户信息的,请自行去掉