🧩 一、技术栈说明(2025 推荐)

组件 版本 说明
Spring Boot 3.2+3.3+ 使用 Jakarta EE 9+(包名 jakarta.*
Elasticsearch 8.10+8.12+ 官方推荐使用 Elasticsearch Java API Client(非 High Level REST Client)
Spring Data Elasticsearch 5.2+ 与 Spring Boot 3 兼容

⚠️ 注意:

  • Elasticsearch 7.17 是最后一个支持 Transport Client 的版本
  • Spring Boot 3 不再支持旧版 High Level REST Client
  • 官方推荐使用新的 Elasticsearch Java API Client

📦 二、Maven 依赖配置

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Data Elasticsearch (自动集成 Java API Client) -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-elasticsearch</artifactId>
    </dependency>

    <!-- Lombok(可选) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

✅ Spring Boot 3.2+ 会自动引入兼容的 co.elastic.clients:elasticsearch-java


⚙️ 三、application.yml 配置

spring:
  elasticsearch:
    uris: http://localhost:9200
    # 若需认证(如 Elastic Cloud)
    # username: elastic
    # password: your-password
    connection-timeout: 1s
    socket-timeout: 30s

🔐 如果使用 Elastic Cloud 或开启安全认证,请配置用户名密码。


📄 四、定义实体类(Document)

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;

@Document(indexName = "product") // 索引名
public class Product {

    @Id
    private String id;

    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String name;

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

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

    @Field(type = FieldType.Integer)
    private Integer stock;

    // 构造函数、getter/setter 略
}

💡 提示:

  • @Document 对应 ES 中的索引(index)
  • @Field 定义字段类型和分词器(如使用 IK 分词器需提前安装)

🗃️ 五、创建 Repository 接口

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
    
    // 自动实现 CRUD 和简单查询
    
    // 方法命名查询(支持)
    List<Product> findByCategory(String category);
    
    List<Product> findByNameContaining(String name);
}

✅ 继承 ElasticsearchRepository 后,自动获得:

  • save(), findById(), delete()
  • findAll(), count()
  • 命名约定查询(如 findByXxx

🧪 六、Service 层使用示例

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    public Product save(Product product) {
        return productRepository.save(product);
    }

    public Optional<Product> findById(String id) {
        return productRepository.findById(id);
    }

    public List<Product> searchByCategory(String category) {
        return productRepository.findByCategory(category);
    }

    // 批量保存
    public Iterable<Product> saveAll(List<Product> products) {
        return productRepository.saveAll(products);
    }
}

🔍 七、高级查询:使用 ElasticsearchTemplateElasticsearchOperations

对于复杂查询(如 bool、range、highlight),推荐使用 ElasticsearchOperations

@Service
public class ProductSearchService {

    @Autowired
    private ElasticsearchOperations elasticsearchOperations;

    public List<Product> searchProducts(String keyword, Double minPrice) {
        Query query = NativeQuery.builder()
            .withQuery(q -> q
                .bool(b -> b
                    .must(m -> m.match(t -> t.field("name").query(keyword)))
                    .filter(f -> f.range(r -> r.field("price").gte(minPrice.toString())))
                )
            )
            .build();

        SearchHits<Product> hits = elasticsearchOperations.search(query, Product.class);
        return hits.getSearchHits().stream()
                   .map(SearchHit::getContent)
                   .collect(Collectors.toList());
    }
}

✅ 优势:直接使用 Elasticsearch DSL,灵活强大。


🧱 八、索引管理(可选)

8.1 自动创建索引(默认行为)

Spring Data Elasticsearch 会在第一次操作时自动创建索引(但不会自动设置 mapping)。

8.2 手动创建索引 + Mapping

@Component
public class IndexInitializer {

    @EventListener(ApplicationReadyEvent.class)
    public void initIndex() {
        ElasticsearchRestTemplate template = ...; // 或注入 ElasticsearchOperations

        if (!template.indexExists(Product.class)) {
            template.createIndex(Product.class);
            template.putMapping(Product.class);
        }
    }
}

💡 建议在生产环境提前用 Kibana 或脚本创建好索引模板,避免自动创建导致 mapping 不符合预期。


🧪 九、测试验证

  1. 启动 Elasticsearch(Docker 示例):

    docker run -d --name es -p 9200:9200 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:8.12.0
    
  2. 启动 Spring Boot 应用

  3. 调用接口保存数据:

    POST /products
    {
      "name": "iPhone 16",
      "category": "Electronics",
      "price": 7999.0,
      "stock": 100
    }
    
  4. 查看 ES 数据:

    GET http://localhost:9200/product/_search
    

⚠️ 十、常见问题与注意事项

问题 解决方案
No qualifying bean of type 'ElasticsearchOperations' 确保添加了 spring-data-elasticsearch 依赖
中文分词无效 安装 IK 分词器插件,并在 @Field 中指定
连接拒绝 检查 ES 是否运行,防火墙是否开放 9200 端口
Spring Boot 3 报错 javax.* 包不存在 升级到兼容 Jakarta EE 的客户端(Spring Data ES 5.2+ 已解决)
性能慢 避免频繁小批量写入,使用 bulk 操作

📌 总结:最佳实践建议

  1. 优先使用 ElasticsearchRepository 处理简单 CRUD
  2. 复杂查询用 ElasticsearchOperations + NativeQuery
  3. 生产环境提前定义索引模板(Index Template)
  4. 敏感数据不要存 ES,ES 不是数据库!
  5. 监控集群状态(CPU、内存、JVM)

🔗 参考资料

🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨

Logo

纵情码海钱塘涌,杭州开发者创新动! 属于杭州的开发者社区!致力于为杭州地区的开发者提供学习、合作和成长的机会;同时也为企业交流招聘提供舞台!

更多推荐