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

Spring Data Elasticsearch中如何为Product实体实现复合主键映射?

在Spring Data Elasticsearch中实现复合主键映射

Spring Data Elasticsearch不支持直接在实体类上添加多个@Id注解,要实现id和name组合作为主键的需求,可以通过以下两种方式实现:


方法一:使用复合ID类

创建一个可序列化的复合ID类,封装id和name属性,将其作为实体的主键字段。

1. 定义复合ID类

该类需要实现Serializable接口,重写equals()和hashCode()方法,同时提供无参构造函数供Spring Data Elasticsearch反序列化使用:

import java.io.Serializable;
import java.util.Objects;

public class ProductId implements Serializable {
    private String id;
    private String name;

    public ProductId() {}

    public ProductId(String id, String name) {
        this.id = id;
        this.name = name;
    }

    // Getters & Setters
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ProductId productId = (ProductId) o;
        return Objects.equals(id, productId.id) && Objects.equals(name, productId.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    // 自定义toString生成唯一主键字符串,Elasticsearch会将此作为文档的_id
    @Override
    public String toString() {
        return id + "_" + name;
    }
}

2. 修改Product实体类

将复合ID类作为实体的主键字段,用@Id注解标记:

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.List;

@Document(indexName = "products")
public class Product {
    @Id
    private ProductId productId;

    @Field(type = FieldType.Keyword)
    private Category category;

    @Field(type = FieldType.Double)
    private double price;

    @Field(type = FieldType.Nested)
    private List<ValidAge> age;

    public enum Category {
        CLOTHES,
        ELECTRONICS,
        GAMES;
    }

    // Getters & Setters
    public ProductId getProductId() { return productId; }
    public void setProductId(ProductId productId) { this.productId = productId; }
    public Category getCategory() { return category; }
    public void setCategory(Category category) { this.category = category; }
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
    public List<ValidAge> getAge() { return age; }
    public void setAge(List<ValidAge> age) { this.age = age; }
}

方法二:直接生成组合主键字段

在实体类中新增一个组合字段作为@Id,手动将id和name拼接成唯一字符串作为主键,同时保留id和name作为独立字段存储。

修改后的Product实体类

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.List;

@Document(indexName = "products")
public class Product {
    @Id
    private String compositeId;

    @Field(type = FieldType.Keyword)
    private String id;

    @Field(type = FieldType.Keyword)
    private String name;

    @Field(type = FieldType.Keyword)
    private Category category;

    @Field(type = FieldType.Double)
    private double price;

    @Field(type = FieldType.Nested)
    private List<ValidAge> age;

    public Product() {}

    // 构造时自动生成复合主键
    public Product(String id, String name, Category category, double price, List<ValidAge> age) {
        this.id = id;
        this.name = name;
        this.compositeId = id + "_" + name;
        this.category = category;
        this.price = price;
        this.age = age;
    }

    // 更新id或name时同步更新复合主键
    public void setId(String id) {
        this.id = id;
        updateCompositeId();
    }

    public void setName(String name) {
        this.name = name;
        updateCompositeId();
    }

    private void updateCompositeId() {
        if (id != null && name != null) {
            this.compositeId = id + "_" + name;
        }
    }

    public enum Category {
        CLOTHES,
        ELECTRONICS,
        GAMES;
    }

    // Getters & Setters
    public String getCompositeId() { return compositeId; }
    public void setCompositeId(String compositeId) { this.compositeId = compositeId; }
    public String getId() { return id; }
    public String getName() { return name; }
    public Category getCategory() { return category; }
    public void setCategory(Category category) { this.category = category; }
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
    public List<ValidAge> getAge() { return age; }
    public void setAge(List<ValidAge> age) { this.age = age; }
}

注意事项

  • Elasticsearch的文档主键_id是字符串类型,无论哪种方式,最终都要生成唯一的字符串值,避免主键冲突导致文档被覆盖。
  • 使用复合ID类时,必须保证类实现Serializable且有无参构造函数,否则Spring Data Elasticsearch无法完成反序列化。
  • 方法二中要确保compositeId在id或name修改时同步更新,避免主键与字段值不一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 01:36:25