16、通用枚举类型处理(一)

本文介绍了一种通过定义通用枚举转换处理器来简化大量枚举类型的处理方式。该方法使用泛型实现,避免了为每个枚举单独编写TypeHandle的需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

如果一个项目中,有几十个,几百个,甚至更多的枚举类型怎么办?

如果遇到一个枚举类,写一个TypeHandle,这样的话,枚举类的处理器会剧增。
不仅如此,你会发现,枚举类的处理器几乎大同小异。

解决的思路

定义一个万能的枚举转换处理器,使用泛型实现

定义枚举接口

package com.lf;

/**
 * Created by LF on 2017/6/12.
 */
public interface BaseEnum<E extends Enum<?>, T> {

    T getCode();

    String getName();

    E of(String code);
}

实现枚举接口,定义枚举

package com.lf.dict;

import com.lf.BaseEnum;
import lombok.ToString;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by LF on 2017/6/12.
 */
@ToString
public enum Gender implements BaseEnum<Gender, Integer> {
    MAN(1, "男"),
    WOMAN(2, "女");

    static Map<Integer, Gender> enumMap = new HashMap<>();

    static {
        Gender[] values = Gender.values();
        for (Gender value : values) {
            enumMap.put(value.getCode(), value);
        }
    }

    private Integer code;
    private String name;

    Gender(Integer code, String name) {
        this.code = code;
        this.name = name;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    @Override
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Gender of(String code) {
        return enumMap.get(code);
    }
}

定义万能枚举转换处理器

package com.lf.typehandle;

import com.lf.BaseEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Created by LF on 2017/6/12.
 */
@MappedTypes(basePackage = {"com.lf.dict"})
@MappedJdbcTypes(value = JdbcType.INTEGER, includeNullJdbcType = true)
public class BaseEnumTypeHandle<E extends BaseEnum> extends BaseTypeHandler<E> {

    private Class<E> type;
    private E[] enums;

    /**
     * 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
     *
     * @param type 配置文件中设置的转换类
     */
    public BaseEnumTypeHandle(Class<E> type) {
        if (type == null)
            throw new IllegalArgumentException("Type argument cannot be null");
        this.type = type;

        this.enums = type.getEnumConstants();
        Class<?>[] interfaces = type.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            Object[] enumConstants = anInterface.getEnumConstants();

        }
//        this.enums =  type.getGenericInterfaces();
        if (this.enums == null)
            throw new IllegalArgumentException(type.getSimpleName()
                    + " does not represent an enum type.");
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter,
                                    JdbcType jdbcType) throws SQLException {
        //BaseTypeHandler已经帮我们做了parameter的null判断
        ps.setObject(i, (String) parameter.getCode(), jdbcType.TYPE_CODE);
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName)
            throws SQLException {
        // 根据数据库存储类型决定获取类型,本例子中数据库中存放String类型
        String i = rs.getString(columnName);
        if (rs.wasNull()) {
            return null;
        } else {
            // 根据数据库中的value值,定位PersonType子类
            return locateEnumStatus(i);
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex)
            throws SQLException {
        // 根据数据库存储类型决定获取类型,本例子中数据库中存放String类型
        String i = rs.getString(columnIndex);
        if (rs.wasNull()) {
            return null;
        } else {
            // 根据数据库中的value值,定位PersonType子类
            return locateEnumStatus(i);
        }
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        // 根据数据库存储类型决定获取类型,本例子中数据库中存放String类型
        String i = cs.getString(columnIndex);
        if (cs.wasNull()) {
            return null;
        } else {
            // 根据数据库中的value值,定位PersonType子类
            return locateEnumStatus(i);
        }
    }

    /**
     * 枚举类型转换,由于构造函数获取了枚举的子类enums,让遍历更加高效快捷
     *
     * @param value 数据库中存储的自定义value属性
     * @return value对应的枚举类
     */
    private E locateEnumStatus(String value) {
        for (E e : enums) {
            if (e.getCode().equals(value)) {
                return e;
            }
        }
        throw new IllegalArgumentException("未知的枚举类型:" + value + ",请核对" + type.getSimpleName());
    }
}

配置

 <typeHandlers>
        <typeHandler  handler="com.lf.typehandle.BaseEnumTypeHandle" javaType="com.lf.dict.Gender"/>
    </typeHandlers>

配置需要转换的字段

  <resultMap id="BaseResultMap" type="com.lf.entity.Blog">
    <id column="id" jdbcType="VARCHAR" property="id" />
    <result column="title" jdbcType="VARCHAR" property="title" />
    <result column="url" jdbcType="VARCHAR" property="url" typeHandler="com.lf.typehandle.BaseEnumTypeHandle"/>
    <result column="userid" jdbcType="VARCHAR" property="userid"  />
  </resultMap>

这样,我们就不需要为每一个枚举创建一个Handler去自动转换数据库中的枚举了。
仅是这样的话和org.apache.ibatis.type.EnumOrdinalTypeHandler就没有什么区别了,我们写这个类就完全没有必要了

/** 1. 功能:枚举操作类,枚举类型创建,转换 * 2. 作者:杨磊 * 3. 创建日期:2008-1-30 * 4. 最后修改日期:2008-11-10 **/ using System; using System.Collections.Generic; using System.Text; using System.Collections; /// /// 枚举操作类,枚举类型创建,转换 /// public class EnumHelper { #region 通过字符串获取枚举成员实例 /// /// 通过字符串获取枚举成员实例 /// /// 枚举名,比如Enum1 /// 枚举成员的常量名或常量值, /// 范例:Enum1枚举有两个成员A=0,B=1,则传入"A"或"0"获取 Enum1.A 枚举类型 public static T GetInstance(string member) { return CommFun.ConvertTo(Enum.Parse(typeof(T), member, true)); } #endregion #region 获取枚举成员名称和成员值的键值对集合 /// /// 获取枚举成员名称和成员值的键值对集合 /// /// 枚举名,比如Enum1 public static Hashtable GetMemberKeyValue() { //创建哈希表 Hashtable ht = new Hashtable(); //获取枚举所有成员名称 string[] memberNames = GetMemberNames(); //遍历枚举成员 foreach (string memberName in memberNames) { ht.Add(memberName, GetMemberValue(memberName)); } //返回哈希表 return ht; } #endregion #region 获取枚举所有成员名称 /// /// 获取枚举所有成员名称 /// /// 枚举名,比如Enum1 public static string[] GetMemberNames() { return Enum.GetNames(typeof(T)); } #endregion #region 获取枚举成员的名称 /// /// 获取枚举成员的名称 /// /// 枚举名,比如Enum1 /// 枚举成员实例或成员值, /// 范例:Enum1枚举有两个成员A=0,B=1,则传入Enum1.A或0,获取成员名称"A" public static string GetMemberName(object member) { //转成基础类型的成员值 Type underlyingType = GetUnderlyingType(typeof(T)); object memberValue = CommFun.ConvertTo(member,underlyingType); //获取枚举成员的名称 return Enum.GetName(typeof(T), memberValue); } #endregion #re
<think>我们参考引用[1]和引用[2]中提到的内容,用户想要在EasyExcel导出数据时处理枚举类型转换问题。引用[1]提供了枚举类型转换器的示例(ProductionTypeEnumConverter),它实现了EasyExcel的Converter接口,用于在导出时将枚举类型转换为可读的字符串(通过getDesc()方法),在导入时将字符串转换回枚举类型。 因此,处理枚举类型转换的关键是自定义个转换器(Converter),并在导出时使用它。 步骤: 1. 创建个转换器类,实现EasyExcel的Converter<T>接口(T为枚举类型)。 2. 实现接口中的四个方法,其中最重要的是: - convertToJavaData: 用于导入时,将Excel中的字符串转换为枚举对象(本例中导出不需要,但为了完整实现,可以保留)。 - convertToExcelData: 用于导出时,将枚举对象转换为要在Excel中显示的字符串(通常是枚举的描述)。 3. 在数据模型的字段上使用@ExcelProperty注解,并通过converter属性指定自定义的转换器。 下面详细说明: 步骤1:定义枚举类型转换器 以引用[1]中的示例为基础,我们假设有枚举类型StatusEnum,它有个code和个desc,我们希望在导出时显示desc(描述)而不是枚举的名称或ordinal。 枚举定义示例: ```java public enum StatusEnum { ENABLED(1, "启用"), DISABLED(0, "禁用"); private int code; private String desc; StatusEnum(int code, String desc) { this.code = code; this.desc = desc; } public int getCode() { return code; } public String getDesc() { return desc; } // 根据code获取枚举,用于导入转换(可选) public static StatusEnum getByCode(int code) { for (StatusEnum status : StatusEnum.values()) { if (status.code == code) { return status; } } return null; } } ``` 步骤2:实现转换器 注意:转换器需要实现Converter<StatusEnum>接口,并实现四个方法。但实际导出时我们主要关注convertToExcelData方法。 ```java import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.GlobalConfiguration; import com.alibaba.excel.metadata.data.CellData; import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.property.ExcelContentProperty; public class StatusEnumConverter implements Converter<StatusEnum> { // 支持Java类型 @Override public Class<?> supportJavaTypeKey() { return StatusEnum.class; } // 支持Excel类型(这里我们返回字符串类型) @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } // 导入时使用:将Excel单元格数据(字符串)转换为Java对象(枚举) @Override public StatusEnum convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { // 这里假设Excel中存储的是描述(desc),我们需要通过描述找到对应的枚举 String desc = cellData.getStringValue(); for (StatusEnum status : StatusEnum.values()) { if (status.getDesc().equals(desc)) { return status; } } // 或者,如果Excel中存储的是code,则根据code转换 // 这里根据实际情况选择,本例按照desc转换 throw new IllegalArgumentException("未知的状态描述: " + desc); } // 导出时使用:将枚举转换为Excel单元格数据(字符串) @Override public WriteCellData<?> convertToExcelData(StatusEnum value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { // 将枚举转换为描述字符串 return new WriteCellData<>(value.getDesc()); } } ``` 步骤3:在数据模型中使用转换器 在导出数据模型的字段上,通过@ExcelProperty的converter属性指定自定义转换器。 ```java import com.alibaba.excel.annotation.ExcelProperty; public class UserExportModel { // 其他字段... @ExcelProperty(value = "状态", converter = StatusEnumConverter.class) private StatusEnum status; // getter和setter... } ``` 这样,在导出时,status字段就会使用StatusEnumConverter进行转换,将枚举值转换为描述字符串(如“启用”、“禁用”)写入Excel。 注意事项: 1. 如果同枚举类型在多个地方使用,可以复用同个转换器。 2. 如果枚举类型较多,可以为每个枚举都编写个转换器,或者编写通用的转换器(通过反射等方式),但通常每个枚举单独写个转换器更清晰。 3. 在转换器中,导入和导出的逻辑要对应。如果导出时写的是描述,导入时也要根据描述转换回来。如果导出的是code,则导入时也要根据code转换。 引用[2]提到,有时候需要将数据库中的原始值(如1,0)转换为字典项(如“启用”,“禁用”),这也可以通过自定义转换器实现,方法类似,只是转换逻辑不同。 因此,处理枚举类型转换的核心就是自定义转换器并绑定到字段。 相关问题: 1. EasyExcel中如何自定义日期格式的转换? 2. 如何实现EasyExcel导出时的动态列? 3. 在Guns框架中如何统处理EasyExcel的导出异常?</think>在 EasyExcel 中处理枚举类型转换,需要通过自定义转换器实现 `Converter` 接口。以下是详细步骤和完整实现方案: ### 解决方案步骤 1. **创建枚举类型转换器** 实现 `Converter<T>` 接口,重写关键方法: ```java import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.GlobalConfiguration; import com.alibaba.excel.metadata.data.CellData; import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.property.ExcelContentProperty; public class EnumConverter implements Converter<YourEnumType> { // 指定支持的Java类型 @Override public Class<YourEnumType> supportJavaTypeKey() { return YourEnumType.class; } // 指定Excel数据类型为字符串 @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } // 导入时:Excel单元格 → Java枚举 (非必需) @Override public YourEnumType convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { String value = cellData.getStringValue(); return YourEnumType.fromDesc(value); // 自定义转换逻辑 } // ★ 导出时:Java枚举 → Excel单元格 (核心) @Override public WriteCellData<String> convertToExcelData(YourEnumType value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return new WriteCellData<>(value.getDesc()); // 输出枚举描述 } } ``` 2. **在数据模型中使用转换器** 通过 `@ExcelProperty` 的 `converter` 属性绑定转换器: ```java public class ExportDataModel { @ExcelProperty("状态") private Integer statusCode; // 原始值 // 枚举字段绑定转换器 @ExcelProperty(value = "状态描述", converter = EnumConverter.class) private StatusEnum status; // 枚举类型 // 示例枚举 public enum StatusEnum { ENABLED(1, "启用"), DISABLED(0, "禁用"); private final int code; private final String desc; StatusEnum(int code, String desc) { this.code = code; this.desc = desc; } public static StatusEnum fromCode(int code) { for (StatusEnum e : values()) { if (e.code == code) return e; } return null; } public String getDesc() { return desc; } } } ``` 3. **在导出逻辑中自动应用转换** 控制器层无需修改,转换器会自动生效: ```java // 导出逻辑保持不变 List<ExportDataModel> data = getDataFromService(); EasyExcel.write(response.getOutputStream(), ExportDataModel.class) .sheet("数据") .doWrite(data); ``` ### 关键说明 1. **转换原理** - 导出时:当 EasyExcel 遇到 `StatusEnum` 类型字段,自动调用 `convertToExcelData()` 将枚举转为描述文本 - 导入时:反向调用 `convertToJavaData()` 实现文本到枚举的转换(如需要)[^1] 2. **枚举设计建议** ```java // 推荐在枚举中添加描述字段和转换方法 public enum Gender { MALE(1, "男"), FEMALE(2, "女"); private final int code; private final String desc; // 构造方法、getter // 静态方法根据code/text获取枚举 } ``` 3. **动态字典项处理** 若需关联数据字典(如从数据库读取),可在转换器中注入服务: ```java @Component // 声明为Spring组件 public class DictConverter implements Converter<Integer> { @Autowired private DictService dictService; @Override public WriteCellData<String> convertToExcelData(Integer code, ...) { String dictValue = dictService.getNameByCode("GENDER", code); return new WriteCellData<>(dictValue); } } ``` ### 常见问题解决 - **枚举描述不显示**:检查枚举是否实现 `getDesc()` 方法 - **转换器未生效**:确认字段类型与 `supportJavaTypeKey()` 返回值致 - **性能优化**:对于高频枚举,使用 `EnumMap` 缓存映射关系 > 通过此方案,枚举值在 Excel 中将自动显示为易读的描述文本(如"启用"而非数字1),同时保持代码解耦[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值