思路1:手写工具类处理(基于反射,比较麻烦,不推荐):
字典表结果如下:
CREATE TABLE `sys_dict` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`dict_code` varchar(128) NOT NULL COMMENT '字典编码',
`dict_name` varchar(128) NOT NULL COMMENT '字典名称',
`dict_value` int(3) NOT NULL COMMENT '字典值',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=utf8mb4 COMMENT='字典表';
INSERT INTO `sys_dict` VALUES (NULL, 'contract_type', '预研合同', '1');
INSERT INTO `sys_dict` VALUES (NULL, 'contract_type', '研制合同', '2');
INSERT INTO `sys_dict` VALUES (NULL, 'contract_type', '订购合同', '3');
1、创建java注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: 自定义注解,实现字典name 、value的转换
* @Author: kang
* @Date: 2024/1/18
*/
@Target(ElementType.FIELD) // 这个注解应用于字段
@Retention(RetentionPolicy.RUNTIME) // 这个注解在运行时仍然可用
public @interface DictField {
String value() default ""; // 默认值为空字符串
}
2、在需要解析的实体上加注解,注意这些@DictField里面的值就是对应字典表的code
public class ContractInfo {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private String code;
/**
* 合同类型
*/
@DictField("contract_type")
private String type;
/**
* 产品层级
*/
@DictField("product_level")
private String productLevel;
/**
* 合同类别
*/
@DictField("contract_category")
private String category;
}
3、定义工具类,将这些注解的值转化为字典的name用于前端展示
import cn.hutool.core.collection.CollUtil;
import com.mars.admin.aspect.DictField;
import com.mars.admin.entity.SysDict;
import com.mars.admin.service.SysDictService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
/**
* @Description: 自定义注解,实现字典name 、value的转换
* @Author: kang
* @Date: 2024/1/18
*/
@Slf4j
@Service
public class DictUtil {
@Resource
private SysDictService sysDictService;
/**
* @Description: 字典值转为名称,比如页面展示,excel导出展示
* @Author: kang
* @Date: 2024/1/18
*/
public void convertObjValue2Name(Object obj) {
if (obj == null) {
return;
}
List<SysDict> dictList = sysDictService.getAll();
if (CollUtil.isEmpty(dictList)) {
return;
}
try {
for (Field field : obj.getClass().getDeclaredFields()) {
//1、判断对象有没有注解
DictField dictField = field.getAnnotation(DictField.class);
if (dictField != null) {
//2、如果有,则注解的value就是字典类型,字段的值就是字典的值
field.setAccessible(true);
String dictCode = dictField.value();
String dictValue = (String) field.get(obj);
//3、筛选出数据库对应的字典,完成字典value -> 字典name的转换
SysDict sysDict = dictList.stream().filter(i -> dictCode.equals(i.getDictCode())
&& dictValue.equals(i.getDictValue())).findFirst().orElse(null);
if (sysDict != null) {
field.set(obj, sysDict.getDictName());
}
}
}
} catch (Exception e) {
log.info("##### 字典注解映射出错了:{}", e.getMessage());
}
}
/**
* @Description: 字典名称转为值,比如excel导入
* @Author: kang
* @Date: 2024/1/18
*/
public void convertObjName2Value(Object obj) {
if (obj == null) {
return;
}
List<SysDict> dictList = sysDictService.getAll();
if (CollUtil.isEmpty(dictList)) {
return;
}
try {
for (Field field : obj.getClass().getDeclaredFields()) {
//1、判断对象有没有注解
DictField dictField = field.getAnnotation(DictField.class);
if (dictField != null) {
//2、如果有,则注解的value就是字典类型,字段的值就是字典的值
field.setAccessible(true);
String dictCode = dictField.value();
String dictValue = (String) field.get(obj);
//3、筛选出数据库对应的字典,完成字典字典name -> value的转换
SysDict sysDict = dictList.stream().filter(i -> dictCode.equals(i.getDictCode())
&& dictValue.equals(i.getDictName())).findFirst().orElse(null);
if (sysDict != null) {
field.set(obj, sysDict.getDictValue());
}
}
}
} catch (Exception e) {
log.info("##### 字典注解映射出错了:{}", e.getMessage());
}
}
/**
* @Description: 字典值转为名称,比如页面展示,excel导出展示
* @Author: kang
* @Date: 2024/1/18
*/
public <Q> void convertListValue2Name(List<Q> list) {
if (CollUtil.isEmpty(list)) {
return;
}
List<SysDict> dictList = sysDictService.getAll();
if (CollUtil.isEmpty(dictList)) {
return;
}
try {
for (Q obj : list) {
convertObjValue2Name(obj);
}
} catch (Exception e) {
log.info("##### 字典注解映射出错了:{}", e.getMessage());
}
}
/**
* @Description: 字典名称转为值,比如excel导入
* @Author: kang
* @Date: 2024/1/18
*/
public <Q> void convertListName2Value(List<Q> list) {
if (CollUtil.isEmpty(list)) {
return;
}
List<SysDict> dictList = sysDictService.getAll();
if (CollUtil.isEmpty(dictList)) {
return;
}
try {
for (Q obj : list) {
convertObjName2Value(obj);
}
} catch (Exception e) {
log.info("##### 字典注解映射出错了:{}", e.getMessage());
}
}
}
4、也可以在controller手动调用,也可以定义切面,我比较懒直接手动调用
@GetMapping("contractinfo/list")
public void export(ContractInfo contractinfo) throws Exception {
List<ContractInfo> list = contractinfoService.list(Wrappers.query(contractinfo));
//手动调用
dictUtil.convertListValue2Name(list);
}
ok , 大功告成!
思路2:基于mybatis-plus解决字典转换(推荐):
1、User类中有一个用户状态字段:
像这种字段我们一般会定义一个枚举,做业务判断的时候就可以直接基于枚举做比较。但是我们数据库采用的是int
类型,对应的PO也是Integer
。因此业务操作时必须手动把枚举
与Integer
转换,非常麻烦。
因此,MybatisPlus提供了一个处理枚举的类型转换器,可以帮我们把枚举类型与数据库类型自动转换。
2.定义枚举
我们定义一个用户状态的枚举:
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;
@Getter
public enum UserStatus {
NORMAL(1, "正常"),
FREEZE(2, "冻结")
;
private final int value;
private final String desc;
UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
}
然后把User
类中的status
字段改为UserStatus
类型:
要让MybatisPlus
处理枚举与数据库类型自动转换,我们必须告诉MybatisPlus
,枚举中的哪个字段的值作为数据库值。
3、MybatisPlus
提供了@EnumValue
注解来标记枚举属性:
4、配置枚举处理器
在application.yaml文件中添加配置:
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
可以看到使用mybatisPlus的配置非常简洁、方便!!
ok , 大功告成!
基于mybatis-plus解决表中JSON转换:
数据库的user表中有一个info
字段,是JSON类型:
格式像这样:
{"age": 20, "intro": "佛系青年", "gender": "male"}
而目前User
实体类中却是String
类型:
这样一来,我们要读取info中的属性时就非常不方便。如果要方便获取,info的类型最好是一个Map
或者实体类。
而一旦我们把info
改为对象
类型,就需要在写入数据库时手动转为String
,再读取数据库时,手动转换为对象
,这会非常麻烦。
因此MybatisPlus提供了很多特殊类型字段的类型处理器,解决特殊字段类型与数据库类型转换的问题。例如处理JSON就可以使用JacksonTypeHandler
处理器。
接下来,我们就来看看这个处理器该如何使用。
3.4.1.定义实体
首先,我们定义一个单独实体类来与info字段的属性匹配:
import lombok.Data;
@Data
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}
3.4.2.使用类型处理器
接下来,将User类的info字段修改为UserInfo类型,并声明类型处理器:
测试可以发现,所有数据都正确封装到UserInfo当中了:
同时,为了让页面返回的结果也以对象格式返回,我们要修改UserVO中的info字段:
此时,在页面查询结果如下:
ok , 大功告成!