云迈博客

您现在的位置是:首页 > 后端开发 > Java > 正文

Java

MyBatis使用自定义枚举

雷黄彦2022-05-05Java322
MyBatis使用自定义枚举背景通常我们会使用类似1==男,2==女的数据类型在mysql中进行存储。但是这种数据类型不不易于维护的,在类型过多的情况下经常会忘记int值的含义。所以大多情况下会使用

MyBatis使用自定义枚举

背景

通常我们会使用类似 1==男,2==女的数据类型在mysql中进行存储。但是这种数据类型不不易于维护的,在类型过多的情况下经常会忘记int值的含义。所以大多情况下会使用枚举类,示例如下:


@AllArgsConstructor
@Getter
public enum SexType  {
    /***/
    BOY(1,"男"),
    GIRL(2,"女");

    private final Integer code;

    private final String val;

}

实现

mybatis 默认的类型转换类为 BaseTypeHandler

通过观察BaseTypeHandler 子类,我们发现了EnumTypeHandler

再观察实现,发现他使用的是 parameter.name() 和Enum.valueOf() 看这里到能清楚的知道默认使用的是Enum.name()值,对应我们的枚举SexType也就是BOY/GIRL,显然不符合我们需要存储Enum.code值的需求。 所有我们需要自定义一个枚举转换器

1. 首先定义一个公共接口 BaseBizEnum

public interface BaseBizEnum {

    /**
     * 得到枚举code,对应数据库int值
     * @return int
     */
    Integer getCode();
    /**
     * 得到枚举值 ,对应前端显示值
     * @return str
     */
    String getVal();

}

2. 改造下之前的枚举类

@AllArgsConstructor
@Getter
public enum SexType implements BaseBizEnum {
    /***/
    BOY(1,"男"),
    GIRL(2,"女");


    private final Integer code;


    private final String val;

}

3.自定义枚举处理器基类

@Slf4j
public class BizEnumTypeHandler<E extends BaseBizEnum>  extends BaseTypeHandler<BaseBizEnum> {

    private Class<E> type;

    //初始化时定义枚举和code的映射关系
    private final Map<Integer,E> enumsMap = new HashMap<>();

    public BizEnumTypeHandler() {

    }

    public BizEnumTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
        for (E enumConstant : type.getEnumConstants()) {
            enumsMap.put(enumConstant.getCode(),enumConstant);
        }
        if (this.enumsMap.size() == 0) {
            throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
        }
    }

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, BaseBizEnum baseBizEnum, JdbcType jdbcType) throws SQLException {
        preparedStatement.setInt(i,baseBizEnum.getCode());
    }

    @Override
    public BaseBizEnum getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        if (resultSet.wasNull()) {
            return null;
        }
        int code = resultSet.getInt(columnName);
        return getEnum(code);
    }

    @Override
    public BaseBizEnum getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {

        if (resultSet.wasNull()) {
            return null;
        }
        int code = resultSet.getInt(columnIndex);
        return getEnum(code);
    }

    @Override
    public BaseBizEnum getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {

        if (callableStatement.wasNull()) {
            return null;
        }
        int code = callableStatement.getInt(columnIndex);
        return getEnum(code);
    }


    private E getEnum(Integer code) {
        try {
            return getEnumByValue(code);
        } catch (Exception ex) {
            throw new IllegalArgumentException(
                    "Cannot convert " + code + " to " + type.getSimpleName() + " by ordinal value.", ex);
        }
    }

    protected E getEnumByValue(Integer code) {
        return enumsMap.get(code);
    }

}

到了这一步,相信懂行的同学都已经明白啥意思了。很简单就是在构造器里简历 code-enum 的对应关系,存储时直接存储枚举的code值,取出时通过code值得到枚举值。那么问题来了,构造器啥时候调用呢。继续往下看。。。

4.指定枚举处理器

@MappedTypes(value = {SexType.class})
@MappedJdbcTypes(value = {JdbcType.INTEGER,JdbcType.TINYINT,JdbcType.SMALLINT})
public class SexTypeEnumTypeHandler extends BizEnumTypeHandler<SexType> {

    /**
     * 实例化时使用父类构造器
     */
    public SexTypeEnumTypeHandler() {
        super(SexType.class);
    }

}

5.加入配置

mybatis:
  type-handlers-package: com.xxx

到这里一切就结束了。此时如果有对象属性SexType==SexType.BOY 数据库将会存储1,SexType.GIRL 同理存储2。
获取时1,2将自动转换为SexType.BOY,SexType.GIRL。下面附上测试类有兴趣的同学可自行测试

@RestController
@RequestMapping(value = "/sex")
public class SexController {
    @Resource
    private TestSexMapper testSexMapper;

    @GetMapping(value = "insertTest")
    public Object insertTest(String name, SexType sexType) {
        MdcUtil.put("开始");
        System.out.println("name="+name+",agreementType="+sexType.name());
        return testSexMapper.insert(name,sexType);
    }
    @GetMapping(value = "getTest")
    public Object getTest( ) {
        MdcUtil.put("开始");
        return testSexMapper.selectByName();
    }
}
public interface TestSexMapper {

    @Insert("insert into test_agreement_info(name,agreement_type) value(#{name},#{sexType})")
    boolean insert(@Param("name") String name, @Param("agreementType") SexType sexType);

    @Select("select  *  from test_agreement_info")
    List<TestSexModel> selectByName();

}
@Data
public class TestSexModel implements Serializable {

    private static final long serialVersionUID = 3285353798809906968L;

    private String name;

    private SexType sexType;

}

别在往下看了,再看也没有了

发表评论

评论列表