You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确将Postgres数组映射到Java @Entity实体类?

解决Postgres数组列映射JPA实体关联列表的问题

问题根源

你遇到的bad value long : {1, 2}错误,本质是JPA默认的集合映射逻辑和Postgres数组列不兼容:

  • @ManyToOne是多对一关联,对应单个实体,完全不适用于集合场景;
  • @ElementCollection默认要求用关联表存储集合元素(每行一个值),但你的数据库是把多个ID存在单个数组列里,Hibernate会把数组的字符串格式{1,2}当成单个Long值解析,自然报错。

可行解决方案

方案1:用Hibernate自定义类型映射Postgres数组(推荐)

借助Hibernate扩展库直接支持Postgres数组类型,再手动关联Region实体:

  1. 先引入依赖(Maven示例):
<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>2.19.0</version> <!-- 对应你的Hibernate版本选择适配版 -->
</dependency>
  1. 在MainTable实体中配置:
@Entity
@Table(name = "Main")
@TypeDefs({
    @TypeDef(name = "long-array", typeClass = LongArrayType.class)
})
public class MainTable {
    // 其他字段...

    // 映射Postgres的bigint[]数组列
    @Column(name = "region_id_list", columnDefinition = "bigint[]")
    @Type(type = "long-array")
    private List<Long> regionIds;

    // 非持久化的Region列表,通过@PostLoad加载
    @Transient
    private List<Region> regions;

    // 注入RegionRepository,或者通过EntityManager查询
    @Autowired
    private RegionRepository regionRepository;

    @PostLoad
    public void loadRegions() {
        if (regionIds != null && !regionIds.isEmpty()) {
            regions = regionRepository.findAllById(regionIds);
        }
    }

    // getter、setter...
}

方案2:自定义Converter解析数组字符串

如果不想引入额外依赖,可以写一个Converter把Postgres的数组字符串转成List<Long>,再手动关联Region:

  1. 实现Converter:
@Converter(autoApply = true)
public class PostgresLongArrayConverter implements AttributeConverter<List<Long>, String> {

    @Override
    public String convertToDatabaseColumn(List<Long> attribute) {
        if (attribute == null || attribute.isEmpty()) {
            return "{}";
        }
        StringBuilder sb = new StringBuilder("{");
        for (int i = 0; i < attribute.size(); i++) {
            sb.append(attribute.get(i));
            if (i < attribute.size() - 1) {
                sb.append(", ");
            }
        }
        sb.append("}");
        return sb.toString();
    }

    @Override
    public List<Long> convertToEntityAttribute(String dbData) {
        if (dbData == null || dbData.equals("{}")) {
            return Collections.emptyList();
        }
        // 剔除前后的{},分割后转成Long列表
        String[] parts = dbData.substring(1, dbData.length() - 1).split(", ");
        return Arrays.stream(parts)
                .map(Long::parseLong)
                .collect(Collectors.toList());
    }
}
  1. 在MainTable中使用:
@Entity
@Table(name = "Main")
public class MainTable {
    // 其他字段...

    @Column(name = "region_id_list")
    @Convert(converter = PostgresLongArrayConverter.class)
    private List<Long> regionIds;

    @Transient
    private List<Region> regions;

    @Autowired
    private RegionRepository regionRepository;

    @PostLoad
    public void loadRegions() {
        if (regionIds != null && !regionIds.isEmpty()) {
            regions = regionRepository.findAllById(regionIds);
        }
    }

    // getter、setter...
}

方案3:修改数据库结构(最符合JPA规范)

如果允许调整数据库,建议把数组列改成关联表(比如main_table_regions),用标准JPA多对多映射:

  1. 创建关联表:
CREATE TABLE main_table_regions (
    main_table_id BIGINT REFERENCES Main(id),
    region_id BIGINT REFERENCES Regions(id),
    PRIMARY KEY(main_table_id, region_id)
);
  1. MainTable实体映射:
@Entity
@Table(name = "Main")
public class MainTable {
    // 其他字段...

    @ManyToMany
    @JoinTable(
        name = "main_table_regions",
        joinColumns = @JoinColumn(name = "main_table_id"),
        inverseJoinColumns = @JoinColumn(name = "region_id")
    )
    private List<Region> regions;

    // getter、setter...
}

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 00:37:25