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
相关产品推荐
相关产品推荐

