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

如何在Spring Boot中存储含对象列表的Map到PostgreSQL数据库

解决方案

方案一:使用PostgreSQL JSONB存储

如果坚持用JSONB格式存储,需要让JPA支持HashMap与JSONB的转换,步骤如下:

1. 添加依赖

引入Hibernate Types库处理JSONB类型(Spring Boot 2.x用hibernate-types-52,Spring Boot 3.x用hibernate-types-60):

<!-- Maven依赖 -->
<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>2.20.0</version>
</dependency>

2. 修改实体类

给mapRang字段添加注解,指定JSONB类型,建议只存储PeriodeProtocolaireEntity的ID而非完整实体,避免冗余:

@Entity
@Table(name = "classements")
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) // 注册JSONB类型
public class ClassementEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_classement")
    private Long id_classement;

    @ManyToOne
    @JoinColumn(name = "id_agent")
    private AgentEntity agent;

    @Column(name = "max_rank")
    private Integer max_rank;
    
    @Type(type = "jsonb")
    @Column(name = "map_rang", columnDefinition = "jsonb")
    private HashMap<Integer, List<Long>> mapRang; // 存储Periode的ID列表

    // Getters and Setters
}

3. 操作示例

保存时直接赋值HashMap:

HashMap<Integer, List<Long>> rankMap = new HashMap<>();
rankMap.put(1, Arrays.asList(1L, 2L));
rankMap.put(2, Collections.singletonList(3L));

ClassementEntity classement = new ClassementEntity();
classement.setMapRang(rankMap);
classementRepository.save(classement);

查询后通过ID列表关联获取实体:

ClassementEntity classement = classementRepository.findById(1L).orElseThrow();
List<Long> periodeIds = classement.getMapRang().get(1);
List<PeriodeProtocolaireEntity> periodes = periodeProtocolaireRepository.findAllById(periodeIds);

方案二:关系型数据库规范化设计(更优)

对于需要频繁查询、更新排名,或依赖排名做复杂业务逻辑的场景,推荐用规范化表结构,避免JSONB的局限性:

1. 创建中间表

新增classement_periode表存储排名与Periode的关联:

CREATE TABLE classement_periode (
    id SERIAL PRIMARY KEY,
    id_classement BIGINT REFERENCES classements(id_classement),
    rank INTEGER NOT NULL,
    id_periode BIGINT REFERENCES periode_protocolaire(id_periode),
    UNIQUE(id_classement, rank, id_periode) -- 避免重复关联
);

2. 定义关联实体

创建ClassementPeriodeEntity作为中间实体:

@Entity
@Table(name = "classement_periode")
public class ClassementPeriodeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "id_classement")
    private ClassementEntity classement;

    @Column(name = "rank")
    private Integer rank;

    @ManyToOne
    @JoinColumn(name = "id_periode")
    private PeriodeProtocolaireEntity periode;

    // Getters and Setters
}

3. 修改ClassementEntity

移除mapRang,添加与中间实体的关联:

@Entity
@Table(name = "classements")
public class ClassementEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_classement")
    private Long id_classement;

    @ManyToOne
    @JoinColumn(name = "id_agent")
    private AgentEntity agent;

    @Column(name = "max_rank")
    private Integer max_rank;
    
    @OneToMany(mappedBy = "classement", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<ClassementPeriodeEntity> periodeRankings = new ArrayList<>();

    // 便捷添加关联的方法
    public void addPeriodeToRank(PeriodeProtocolaireEntity periode, Integer rank) {
        ClassementPeriodeEntity ranking = new ClassementPeriodeEntity();
        ranking.setClassement(this);
        ranking.setPeriode(periode);
        ranking.setRank(rank);
        periodeRankings.add(ranking);
    }

    // Getters and Setters
}

4. 操作示例

添加排名关联:

ClassementEntity classement = new ClassementEntity();
PeriodeProtocolaireEntity periode1 = periodeProtocolaireRepository.findById(1L).orElseThrow();
PeriodeProtocolaireEntity periode2 = periodeProtocolaireRepository.findById(2L).orElseThrow();

classement.addPeriodeToRank(periode1, 1);
classement.addPeriodeToRank(periode2, 1);
classementRepository.save(classement);

查询指定排名的Periode:

List<ClassementPeriodeEntity> rankings = classementPeriodeRepository.findByClassementIdAndRank(1L, 1);
List<PeriodeProtocolaireEntity> periodes = rankings.stream()
    .map(ClassementPeriodeEntity::getPeriode)
    .collect(Collectors.toList());

方案对比

  • JSONB方案:结构灵活,适合快速开发、排名结构不常变更的场景;但难以做复杂查询,更新单个关联关系时需修改整个JSON,性能较差。
  • 规范化方案:符合关系型数据库设计,支持复杂查询、事务操作,维护性强;开发初期稍繁琐,但长期更适配业务扩展。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 16:52:48