You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何用Hibernate/Spring Data将枚举列表映射到PostgreSQL字段

当然可以!不用额外创建数据库表,咱们直接利用PostgreSQL的数组类型,配合Hibernate的类型映射能力就能搞定枚举列表的存储。下面给你详细的实现方案和代码示例:

实现步骤

1. 准备数据库字段

首先在person表中,给枚举列表对应的字段设置为text[]类型(PostgreSQL的文本数组,用来存储枚举的名称集合)。创建表的SQL示例:

CREATE TABLE person (
    id BIGSERIAL PRIMARY KEY,
    languages TEXT[]
);

2. 配置Hibernate映射

这里提供两种常用方案,你可以根据项目情况选择:

方案一:使用Hibernate Types库(推荐)

如果你的项目允许引入第三方库,hibernate-types是个非常省心的选择,它已经封装好了各种PostgreSQL特殊类型的映射,包括枚举数组。

首先添加Maven依赖(对应Hibernate 5.x版本,其他版本请调整artifactId):

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>2.19.0</version>
</dependency>

然后在实体类中配置类型映射:

public enum Language { EN, FR, ZH }

@Entity
@Table(name = "person")
// 注册自定义类型别名
@TypeDef(name = "list-array", typeClass = ListArrayType.class)
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator", sequenceName = "person_seq")
    private Long id;

    // 使用注册的类型映射枚举列表
    @Type(type = "list-array")
    @Column(name = "languages", columnDefinition = "text[]")
    private List<Language> languages;

    // 构造器、getter、setter请自行补充
}

方案二:自定义Hibernate类型

如果不想引入额外库,也可以自己实现一个自定义类型来处理枚举数组的转换逻辑:

public class EnumListArrayType extends AbstractSingleColumnStandardBasicType<List<Language>> {
    public EnumListArrayType() {
        // 关联SQL类型描述符和Java类型描述符
        super(ArraySqlTypeDescriptor.INSTANCE, new EnumListTypeDescriptor());
    }

    @Override
    public String getName() {
        return "enum-list-array";
    }

    // 处理枚举列表与数据库数组的转换逻辑
    public static class EnumListTypeDescriptor extends AbstractTypeDescriptor<List<Language>> {
        protected EnumListTypeDescriptor() {
            super((Class<List<Language>>) (Class<?>) List.class, new MutabilityPlan<List<Language>>() {
                @Override
                public List<Language> deepCopy(List<Language> value) {
                    return value != null ? new ArrayList<>(value) : null;
                }

                @Override
                public boolean isMutable() {
                    return true;
                }
            });
        }

        @Override
        public String toString(List<Language> value) {
            return value.stream().map(Enum::name).collect(Collectors.joining(","));
        }

        @Override
        public List<Language> fromString(String string) {
            return Arrays.stream(string.split(","))
                    .map(Language::valueOf)
                    .collect(Collectors.toList());
        }

        @Override
        public <X> X unwrap(List<Language> value, Class<X> type, WrapperOptions options) {
            if (value == null) {
                return null;
            }
            if (String[].class.isAssignableFrom(type)) {
                return (X) value.stream().map(Enum::name).toArray(String[]::new);
            }
            return super.unwrap(value, type, options);
        }

        @Override
        public <X> List<Language> wrap(X value, WrapperOptions options) {
            if (value == null) {
                return null;
            }
            if (String[].class.isInstance(value)) {
                return Arrays.stream((String[]) value)
                        .map(Language::valueOf)
                        .collect(Collectors.toList());
            }
            return super.wrap(value, options);
        }
    }
}

然后在实体类中注册并使用这个自定义类型:

@Entity
@Table(name = "person")
@TypeDef(name = "enum-list-array", typeClass = EnumListArrayType.class)
public class Person {
    // ...主键等其他字段

    @Type(type = "enum-list-array")
    @Column(name = "languages", columnDefinition = "text[]")
    private List<Language> languages;

    // ...getter、setter
}

3. 测试使用

配置完成后,就可以像操作普通字段一样使用枚举列表了:

// 保存示例
Person person = new Person();
person.setLanguages(Arrays.asList(Language.EN, Language.ZH));
entityManager.persist(person);

// 查询示例
Person foundPerson = entityManager.find(Person.class, person.getId());
System.out.println(foundPerson.getLanguages()); // 输出 [EN, ZH]

这样就完美实现了不依赖额外表的枚举列表映射~

内容的提问来源于stack exchange,提问作者vic

火山引擎 最新活动