SpringBoot3集成ElasticSearch的方法詳解
一、簡(jiǎn)介
Elasticsearch是一個(gè)分布式、RESTful風(fēng)格的搜索和數(shù)據(jù)分析引擎,適用于各種數(shù)據(jù)類型,數(shù)字、文本、地理位置、結(jié)構(gòu)化數(shù)據(jù)、非結(jié)構(gòu)化數(shù)據(jù);
在實(shí)際的工作中,歷經(jīng)過(guò)Elasticsearch從 6.0 到 7.0 的版本升級(jí),而這次SpringBoot3和ES8.0的集成,雖然腳本的語(yǔ)法變化很小,但是Java客戶端的API語(yǔ)法變化很大;
二、環(huán)境搭建
1、下載安裝包
需要注意的是,這些安裝包的版本要選擇對(duì)應(yīng)的,不然容易出問(wèn)題;
軟件包:elasticsearch-8.8.2-darwin-x86_64.tar.gz 分詞器工具:elasticsearch-analysis-ik-8.8.2.zip 可視化工具:kibana-8.8.2-darwin-x86_64.tar.gz
2、服務(wù)啟動(dòng)
不論是ES還是Kibana,在首次啟動(dòng)后,會(huì)初始化很多配置文件,可以根據(jù)自己的需要做相關(guān)的配置調(diào)整,比如常見的端口調(diào)整,資源占用,安全校驗(yàn)等;

1、啟動(dòng)ES elasticsearch-8.8.2/bin/elasticsearch 本地訪問(wèn):localhost:9200 2、啟動(dòng)Kibana kibana-8.8.2/bin/kibana 本地訪問(wèn):http://localhost:5601 # 3、查看安裝的插件 http://localhost:9200/_cat/plugins -> analysis-ik 8.8.2
三、工程搭建
1、工程結(jié)構(gòu)

2、依賴管理
在 starter-elasticsearch 組件中,實(shí)際上依賴的是 elasticsearch-java 組件的 8.7.1 版本;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>${spring-boot.version}</version>
</dependency>3、配置文件
在上面環(huán)境搭建的過(guò)程中,已經(jīng)禁用了用戶和密碼的登錄驗(yàn)證,配置ES服務(wù)地址即可;
spring:
# ElasticSearch配置
elasticsearch:
uris: localhost:9200四、基礎(chǔ)用法
1、實(shí)體類
通過(guò) Document 和 Field 注解描述ES索引結(jié)構(gòu)的實(shí)體類,注意這里 JsonIgnoreProperties 注解,解決索引中字段和實(shí)體類非一一對(duì)應(yīng)的而引起的JSON解析問(wèn)題;
@JsonIgnoreProperties(ignoreUnknown = true)
@Document(indexName = "contents_index", createIndex = false)
public class ContentsIndex implements Serializable {
private static final long serialVersionUID=1L;
@Field(type= FieldType.Integer)
private Integer id;
@Field(type= FieldType.Keyword)
private String title;
@Field(type= FieldType.Keyword)
private String intro;
@Field(type= FieldType.Text)
private String content;
@Field(type= FieldType.Integer)
private Integer createId;
@Field(type= FieldType.Keyword)
private String createName;
@Field(type= FieldType.Date,format = DateFormat.date_hour_minute_second)
private Date createTime;
}2、初始化索引
基于 ElasticsearchTemplate 類和上述實(shí)體類,實(shí)現(xiàn)索引結(jié)構(gòu)的初始化,并且將 tb_contents 表中的數(shù)據(jù)同步到索引中,最后通過(guò)ID查詢一條測(cè)試數(shù)據(jù);
@Service
public class ContentsIndexService {
private static final Logger log = LoggerFactory.getLogger(ContentsIndexService.class);
@Resource
private ContentsService contentsService ;
@Resource
private ElasticsearchTemplate template ;
/**
* 初始化索引結(jié)構(gòu)和數(shù)據(jù)
*/
public void initIndex (){
// 處理索引結(jié)構(gòu)
IndexOperations indexOps = template.indexOps(ContentsIndex.class);
if (indexOps.exists()){
boolean delFlag = indexOps.delete();
log.info("contents_index exists,delete:{}",delFlag);
indexOps.createMapping(ContentsIndex.class);
} else {
log.info("contents_index not exists");
indexOps.createMapping(ContentsIndex.class);
}
// 同步數(shù)據(jù)庫(kù)表記錄
List<Contents> contentsList = contentsService.queryAll();
if (contentsList.size() > 0){
List<ContentsIndex> contentsIndexList = new ArrayList<>() ;
contentsList.forEach(contents -> {
ContentsIndex contentsIndex = new ContentsIndex() ;
BeanUtils.copyProperties(contents,contentsIndex);
contentsIndexList.add(contentsIndex);
});
template.save(contentsIndexList);
}
// ID查詢
ContentsIndex contentsIndex = template.get("10",ContentsIndex.class);
log.info("contents-index-10:{}",contentsIndex);
}
}3、倉(cāng)儲(chǔ)接口
繼承 ElasticsearchRepository 接口,可以對(duì)ES這種特定類型的存儲(chǔ)庫(kù)進(jìn)行通用增刪改查操作;在測(cè)試類中對(duì)該接口的方法進(jìn)行測(cè)試;
// 1、接口定義
public interface ContentsIndexRepository extends ElasticsearchRepository<ContentsIndex,Long> {
}
// 2、接口測(cè)試
public class ContentsIndexRepositoryTest {
@Autowired
private ContentsIndexRepository contentsIndexRepository;
@Test
public void testAdd (){
// 單個(gè)新增
contentsIndexRepository.save(buildOne());
// 批量新增
contentsIndexRepository.saveAll(buildList()) ;
}
@Test
public void testUpdate (){
// 根據(jù)ID查詢后再更新
Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(14L);
if (contentsOpt.isPresent()){
ContentsIndex contentsId = contentsOpt.get();
System.out.println("id=14:"+contentsId);
contentsId.setContent("update-content");
contentsId.setCreateTime(new Date());
contentsIndexRepository.save(contentsId);
}
}
@Test
public void testQuery (){
// 單個(gè)ID查詢
Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(1L);
if (contentsOpt.isPresent()){
ContentsIndex contentsId1 = contentsOpt.get();
System.out.println("id=1:"+contentsId1);
}
// 批量ID查詢
Iterator<ContentsIndex> contentsIterator = contentsIndexRepository
.findAllById(Arrays.asList(10L,12L)).iterator();
while (contentsIterator.hasNext()){
ContentsIndex contentsIndex = contentsIterator.next();
System.out.println("id="+contentsIndex.getId()+":"+contentsIndex);
}
}
@Test
public void testDelete (){
contentsIndexRepository.deleteById(15L);
contentsIndexRepository.deleteById(16L);
}
}4、查詢語(yǔ)法
無(wú)論是 ElasticsearchTemplate 類還是 ElasticsearchRepository 接口,都是對(duì)ES常用的簡(jiǎn)單功能進(jìn)行封裝,在實(shí)際使用時(shí),復(fù)雜的查詢語(yǔ)法還是依賴 ElasticsearchClient 和原生的API封裝;
這里主要演示七個(gè)查詢方法,主要涉及:ID查詢,字段匹配,組合與范圍查詢,分頁(yè)與排序,分組統(tǒng)計(jì),最大值查詢和模糊匹配;更多的查詢API還是要多看文檔中的案例才行;
public class ElasticsearchClientTest {
@Autowired
private ElasticsearchClient client ;
@Test
public void testSearch1 () throws IOException {
// ID查詢
GetResponse<ContentsIndex> resp = client.get(
getReq ->getReq.index("contents_index").id("7"), ContentsIndex.class);
if (resp.found()){
ContentsIndex contentsIndex = resp.source() ;
System.out.println("contentsIndex-7:"+contentsIndex);
}
}
@Test
public void testSearch2 () throws IOException {
// 指定字段匹配
SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
.query(query -> query.match(field -> field
.field("createName").query("張三"))),ContentsIndex.class);
printResp(resp);
}
@Test
public void testSearch3 () throws IOException {
// 組合查詢:姓名和時(shí)間范圍
Query byName = MatchQuery.of(field -> field.field("createName").query("王五"))._toQuery();
Query byTime = RangeQuery.of(field -> field.field("createTime")
.gte(JsonData.of("2023-07-10T00:00:00"))
.lte(JsonData.of("2023-07-12T00:00:00")))._toQuery();
SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
.query(query -> query.bool(boolQuery -> boolQuery.must(byName).must(byTime))),ContentsIndex.class);
printResp(resp);
}
@Test
public void testSearch4 () throws IOException {
// 排序和分頁(yè),在14條數(shù)據(jù)中,根據(jù)ID倒序排列,從第5條往后取4條數(shù)據(jù)
SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
.from(5).size(4)
.sort(sort -> sort.field(sortField -> sortField.field("id").order(SortOrder.Desc))),ContentsIndex.class);
printResp(resp);
}
@Test
public void testSearch5 () throws IOException {
// 根據(jù)createId分組統(tǒng)計(jì)
SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
.aggregations("createIdGroup",agg -> agg.terms(term -> term.field("createId"))),ContentsIndex.class);
Aggregate aggregate = resp.aggregations().get("createIdGroup");
LongTermsAggregate termsAggregate = aggregate.lterms();
Buckets<LongTermsBucket> buckets = termsAggregate.buckets();
for (LongTermsBucket termsBucket : buckets.array()) {
System.out.println(termsBucket.key() + " : " + termsBucket.docCount());
}
}
@Test
public void testSearch6 () throws IOException {
// 查詢最大的ID
SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
.aggregations("maxId",agg -> agg.max(field -> field.field("id"))),ContentsIndex.class);
for (Map.Entry<String, Aggregate> entry : resp.aggregations().entrySet()){
System.out.println(entry.getKey()+":"+entry.getValue().max().value());
}
}
@Test
public void testSearch7 () throws IOException {
// 模糊查詢title字段,允許1個(gè)誤差
Query byContent = FuzzyQuery.of(field -> field.field("title").value("設(shè)計(jì)").fuzziness("1"))._toQuery();
SearchResponse<ContentsIndex> resp = client.search(
searchReq -> searchReq.index("contents_index").query(byContent),ContentsIndex.class);
printResp(resp);
}
private void printResp (SearchResponse<ContentsIndex> resp){
TotalHits total = resp.hits().total();
System.out.println("total:"+total);
List<Hit<ContentsIndex>> hits = resp.hits().hits();
for (Hit<ContentsIndex> hit: hits) {
ContentsIndex contentsIndex = hit.source();
System.out.println(hit.id()+":"+contentsIndex);
}
}
}五、參考源碼
文檔倉(cāng)庫(kù): https://gitee.com/cicadasmile/butte-java-note 源碼倉(cāng)庫(kù): https://gitee.com/cicadasmile/butte-spring-parent
文檔倉(cāng)庫(kù):https://gitee.com/cicadasmile/butte-java-note
以上就是SpringBoot3集成ElasticSearch的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot3集成ElasticSearch的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java使double保留兩位小數(shù)的多方法 java保留兩位小數(shù)
這篇文章主要介紹了java使double類型保留兩位小數(shù)的方法,大家參考使用吧2014-01-01
SpringBoot集成RabbitMQ實(shí)現(xiàn)用戶注冊(cè)的示例代碼
這篇文章主要介紹了SpringBoot集成RabbitMQ實(shí)現(xiàn)用戶注冊(cè)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
基于SpringBoot實(shí)現(xiàn)HTTP請(qǐng)求簽名驗(yàn)證機(jī)制
在分布式系統(tǒng)交互中,API接口的安全性至關(guān)重要,本文將深入解析基于Spring Boot實(shí)現(xiàn)的HTTP請(qǐng)求簽名驗(yàn)證機(jī)制,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04
java實(shí)現(xiàn)簡(jiǎn)單的客戶信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單的客戶信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
JAVA編程實(shí)現(xiàn)TCP網(wǎng)絡(luò)通訊的方法示例
這篇文章主要介紹了JAVA編程實(shí)現(xiàn)TCP網(wǎng)絡(luò)通訊的方法,簡(jiǎn)單說(shuō)明了TCP通訊的原理并結(jié)合具體實(shí)例形式分析了java實(shí)現(xiàn)TCP通訊的步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-08-08
springboot打包無(wú)法讀取yml、properties等配置文件的解決
這篇文章主要介紹了springboot打包無(wú)法讀取yml、properties等配置文件的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04
Java使用正則表達(dá)式判斷獨(dú)立字符的存在(代碼示例)
通過(guò)使用正則表達(dá)式,我們可以更加靈活地判斷字符串中是否包含特定的字符,并且可以控制匹配的條件,如獨(dú)立的字符,這為我們處理字符串提供了更多的選擇和功能,這篇文章主要介紹了Java使用正則表達(dá)式判斷獨(dú)立字符的存在,需要的朋友可以參考下2023-10-10

