前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JPA出现的数据库枚举映射的问题以及一步步优化

JPA出现的数据库枚举映射的问题以及一步步优化

作者头像
宇宙无敌暴龙战士之心悦大王
发布2022-02-24 08:30:58
4.6K0
发布2022-02-24 08:30:58
举报
文章被收录于专栏:kwaikwai

问题

环境:一个枚举(name,id),数据库只存枚举的id。

当我们从数据库取出这个id对应的整条记录时,JPA会帮助我们对枚举自动映射(id到对应的枚举)。

今天这个地方出错了,id总是映射到错误的枚举上。

解决

1,仅传递枚举名,这样不需要映射。但是对未来修改和扩展有非常非常大的问题。

2,编写工具类xxxEnumUtils。

逻辑:我们可以每次调用工具类,然后手动映射传回去。

操作:遍历枚举的value,对比每个id,相同则返回这个枚举。

缺点:同时多个枚举不能共用同一个,实现在下面。

3,现在的解决方法

大部分情况下,我们需要检查@Enumerated()内的东西。

JPA提供给我们两种枚举映射的方法。

EnumType.Ordinal:

按照顺序,数据库存的是枚举的id。

这玩意有个缺点,一定是按顺序的,我们没办法定义。

所以队友可能会在枚举中间加了个新枚举,导致整体id序列化错误(多一位)。

EnumType.Spring:

存的是枚举的名字,和第一种解决方法一样,我们没办法维护他,就是不能改枚举名。

所以这两种自带的枚举都有非常多的问题,这样我们的解决方法就出现了。

自定义一个枚举转换器,来实现自动转换。

这里我们就可以找到实体转换器,进行自定义转换。

代码语言:javascript
复制
public class EnumConvert implements AttributeConverter<StatusEnum, Integer> {
    @Override
    public Integer convertToDatabaseColumn(StatusEnum attribute) {
        return attribute.getValue();
    }

    @Override
    public StatusEnum convertToEntityAttribute(Integer dbData) {
        return StatusEnum.fromValue(dbData);
    }
}

但是我们肯定不能因为每个枚举都写一个转换器吧。

所以我们需要实现一种公有的转换器。

这时候就利用泛型。

我们肯定要先写一个接口。

代码语言:javascript
复制
public interface IBaseDbEnum {
    /**
     * 用于显示的枚举名
     *
     * @return
     */
    String getDisplay();

    /**
     * 存储到数据库的枚举值
     *
     * @return
     */
    Integer getValue();

    //按枚举的value获取枚举实例
    static <T extends IBaseDbEnum> T fromValue(Class<T> enumType, Integer value) {
        for (T object : enumType.getEnumConstants()) {
            if (Objects.equals(value, object.getValue())) {
                return object;
            }
        }
        throw new IllegalArgumentException("No enum value " + value + " of " + enumType.getCanonicalName());
    }
}

然后按照原来的方式,利用泛型实现

代码语言:javascript
复制
public class EnumConvert<T extends IBaseDbEnum> implements AttributeConverter<T, Integer> {
    @Override
    public Integer convertToDatabaseColumn(T attribute) {
        return attribute.getValue();
    }

    @Override
    public T convertToEntityAttribute(Integer dbData) {
        //先随便写,测试一下
        return (T) StatusEnum.Active;
    }
}

这样就解决问题了。

实体转换器:实现很简单,只需要实现两个接口就好。

关于项目优化的过程

最开始出现映射失误,以为没有加@Enumerated注解(实际原因不是,因为发现默认就是ordinal)。

考虑到后续spring扩展性很垃圾,所以采用ordinal了。

但是发现还是出错,排查后发现是因为ordinal是不看id的,只看顺序,原来定义枚举时从1开始,导致每次都错位。

所以在枚举类中加入了自定义的实体转换器。

后来第二个枚举又出现问题了,决定写个共用的自定义实体转换器,调用即可。

使用:子枚举直接继承这个父类的实体转换器方法就行。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-02-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 解决
  • 关于项目优化的过程
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档