java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Springboot整合spring-boot-starter-data-elasticsearch

Springboot整合spring-boot-starter-data-elasticsearch的过程

作者:WalkerShen

本文详细介绍了Springboot整合spring-boot-starter-data-elasticsearch的过程,包括版本要求、依赖添加、实体类添加、索引的名称、分片、副本设置等,同时,还介绍了如何使用ElasticsearchRepository类进行增删改查操作

前言

<font style="color:rgb(36, 41, 47);">spring-boot-starter-data-elasticsearch</font> 是 Spring Boot 提供的一个起始依赖,旨在简化与 Elasticsearch 交互的开发过程。它集成了 Spring Data Elasticsearch,提供了一套完整的 API,用于与 Elasticsearch 进行 CRUD 操作、查询、索引等

相对于es原生依赖的话,进行了一下封装,在使用过程中相对便捷

项目源码

项目源码:https://gitee.com/shen-chuhao/walker_open_java.git

elasticsearch和springboot版本要求

在使用 <font style="color:rgb(36, 41, 47);">spring-boot-starter-data-elasticsearch</font> 时,Spring Boot 版本和 Elasticsearch 版本不一致可能导致以下问题:

兼容性问题

功能缺失或错误

配置问题

序列化和反序列化问题

性能问题

相关版本的要求

我的es是7.3.2的,介于6.8.127.6.2之间,所以springboot版本为2.2.x2.3.x即可

所以在使用spring-boot-starter-data-elasticsearch的过程中,还是要保持版本一致

整合步骤

依赖添加

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

yaml配置添加

spring:
  elasticsearch:
    rest:
      uris: localhost:19200
      # 账号密码配置,如果没有则不需要
      password: elastic
      username: elastic

实体类添加

package com.walker.es.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
//索引
// boolean createIndex() default true;  默认会创建索引
@Document(indexName = "alarm_record", shards = 2, replicas = 2,createIndex = true)
public class AlarmRecordEntity {
    //    配置使用的id
    @Id
    private Long id;
    //    事件
//  配置属性,分词器
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String title;
    //    设备
    @Field(type = FieldType.Keyword)
    private String deviceCode;
    //    时间  需要使用keyword,否则无法实现范围查询 
    @Field(type = FieldType.Keyword)
    private String time;
    //    在es中进行忽略的
    @Transient
    private String msg;
}

Repository类

package com.walker.es.repository;
import com.walker.es.entity.AlarmRecordEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
// 添加注解
@Repository
// 继承类 ElasticsearchRepository
public interface AlarmRecordRepository extends ElasticsearchRepository<AlarmRecordEntity, Long> {
    // 你可以在这里添加自定义查询方法
}

继承ElasticsearchRepository类,可以使用ElasticsearchRepository封装好的增删改查等方法

测试类

先注入repository

    @Autowired
    private AlarmRecordRepository alarmRecordRepository;

新增数据

    // 创建新警报记录
    @PostMapping
    public ResponseEntity<AlarmRecordEntity> createAlarmRecord(@RequestBody AlarmRecordEntity alarmRecord) {
//      自动生成雪花id
        alarmRecord.setId(IdUtil.getSnowflakeNextId());
        AlarmRecordEntity savedRecord = alarmRecordRepository.save(alarmRecord);
        return ResponseEntity.ok(savedRecord);
    }

请求参数:

{
  "title": "打架告警",
  "deviceCode": "A001",
  "time": "2024-11-10 10:10:00",
  "msg": "消息描述"
}

执行后,如果第一次插入数据,则会自动生成索引。

副本、分片

字段映射情况

查询插入的数据

修改数据

    @Autowired
    private DocumentOperations documentOperations;
    @Autowired
    private ElasticsearchConverter elasticsearchConverter;
// 更新警报记录
    @PutMapping("/{id}")
    public ResponseEntity<AlarmRecordEntity> updateAlarmRecord(@PathVariable Long id, @RequestBody AlarmRecordEntity alarmRecord) {
        //使用ElasticsearchConverter 将对象转成Document对象
        Document document = elasticsearchConverter.mapObject(alarmRecord);
       // 修改方法
        UpdateQuery updateQuery = UpdateQuery.builder(String.valueOf(id)).withDocument(document).build();
        UpdateResponse updateResponse = documentOperations.update(updateQuery, IndexCoordinates.of("alarm_record"));
        log.info("修改数据结果:{}", JSONUtil.toJsonStr(updateResponse));
        return ResponseEntity.ok(alarmRecord);
    }

实践结果:

修改结果如下:

删除

    // 删除警报记录
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteAlarmRecord(@PathVariable Long id) {
        if (!alarmRecordRepository.existsById(id)) {
            return ResponseEntity.notFound().build();
        }
        alarmRecordRepository.deleteById(id);
        return ResponseEntity.noContent().build();
    }

使用repository的deleteById

批量删除

使用repository进行基础查询

    // 获取所有警报记录
    @GetMapping("/findByDeviceCode")
    public List<AlarmRecordEntity> findByDeviceCode(String deviceCode) {
        return  alarmRecordRepository.findAlarmRecordEntitiesByDeviceCodeEquals(deviceCode);
    }
package com.walker.es.repository;
import com.walker.es.entity.AlarmRecordEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface AlarmRecordRepository extends ElasticsearchRepository<AlarmRecordEntity, Long> {
// 根据deviceCode=xx进行查询,不需要去重写方法等,相对简便
    List<AlarmRecordEntity> findAlarmRecordEntitiesByDeviceCodeEquals(String deviceCode);
}

分页查询、条件搜索

这个是实际业务场景中用的比较多的了

但是_ElasticsearchRepository 无法实现复杂的查询,所以还是得使用ElasticsearchTemplate 或者_<font style="color:rgb(36, 41, 47);">ElasticsearchOperations</font> 进行处理

spring-data-elasticsearch的操作类主要有以下几种:

<font style="color:rgb(36, 41, 47);">IndexOperations</font> 定义了在索引级别执行的操作,比如创建或删除索引。

<font style="color:rgb(36, 41, 47);">DocumentOperations</font> 定义了基于实体 ID 存储、更新和检索实体的操作。

<font style="color:rgb(36, 41, 47);">SearchOperations</font> 定义了使用查询搜索多个实体的操作。

<font style="color:rgb(36, 41, 47);">ElasticsearchOperations</font> 结合了 <font style="color:rgb(36, 41, 47);">DocumentOperations</font><font style="color:rgb(36, 41, 47);">SearchOperations</font> 接口的功能。

__

__

__

请求体

@Data
public class AlarmSearchDTO {
//    注意,页码需要从0开始,否则会跳过
    private Integer pageIndex=0;
    private Integer pageSize=10;
    private String title;
    private String deviceCode;
    private String startTime;
    private String endTime;
}

__

controllerfangfa

 @PostMapping("/page")
    public PageVo<AlarmRecordEntity> getAlarmRecords(@RequestBody AlarmSearchDTO dto) {
     // 分页类  将页码和页数参数填入
        Pageable pageable = PageRequest.of(dto.getPageIndex(),dto.getPageSize());
        // 构建查询 在spring-data-elastic中有三种查询方式,具体可以查看后面的查询方式
        NativeSearchQueryBuilder  query = new NativeSearchQueryBuilder()
                .withPageable(pageable);
//        如果设备编码非空
        if(StrUtil.isNotEmpty(dto.getDeviceCode())){
//            精确查询 termQuery
            query.withQuery(QueryBuilders.termQuery("deviceCode",dto.getDeviceCode() ));
        }
        if(StrUtil.isNotEmpty(dto.getTitle())){
//            模糊查询 fuzzyQuery   如果是要根据分词拆分查询的话,得使用matchQuery
            query.withQuery(QueryBuilders.fuzzyQuery("title",dto.getTitle() ));
        }
//        范围查询
        if (StrUtil.isNotEmpty(dto.getStartTime())) {
//            range范围查询需要keyword才能生效,如果使用text则会不生效
            query.withQuery(QueryBuilders.rangeQuery("time").gte(dto.getStartTime()).lte(dto.getEndTime()));
        }
        NativeSearchQuery searchQuery = query.build();
//        排序:根据id倒叙
        searchQuery.addSort(Sort.sort(AlarmRecordEntity.class).by(AlarmRecordEntity::getId).descending());
        // 执行查询
        SearchHits<AlarmRecordEntity> search = elasticsearchTemplate.search(searchQuery, AlarmRecordEntity.class);
        PageVo<AlarmRecordEntity> pageVo = new PageVo<>();
        if(search!=null){
            //设置总数
            pageVo.setTotal(search.getTotalHits());
            // 返回的行数
            List<SearchHit<AlarmRecordEntity>> searchHits = search.getSearchHits();
            if(CollUtil.isNotEmpty(searchHits)){
                ArrayList<AlarmRecordEntity> rows = new ArrayList<>();
                for (SearchHit<AlarmRecordEntity> searchHit : searchHits) {
                    AlarmRecordEntity content = searchHit.getContent();
                    rows.add(content);
                }
                pageVo.setList(rows);
            }
        }
        return pageVo;
    }

Query查询方式主要有以下几种

1. CriteriaQuery

定义<font style="color:rgb(36, 41, 47);">CriteriaQuery</font> 是一个用于构建类型安全查询的方式。它允许通过构建查询条件来进行灵活的查询。

使用案例

 CodeCriteriaQuery criteriaQuery = new CriteriaQuery();
criteriaQuery.addCriteria(Criteria.where("fieldName").is("value"));
List<MyEntity> results = elasticsearchOperations.queryForList(criteriaQuery, MyEntity.class);

2. StringQuery

定义<font style="color:rgb(36, 41, 47);">StringQuery</font> 是一种基于字符串的查询方式,允许直接使用 Elasticsearch 的查询 DSL(Domain Specific Language)。

使用案例

CodeStringQuery stringQuery = new StringQuery("{\"match\":{\"fieldName\":\"value\"}}");
List<MyEntity> results = elasticsearchOperations.queryForList(stringQuery, MyEntity.class);

3. NativeQuery

定义<font style="color:rgb(36, 41, 47);">NativeQuery</font> 允许执行原生的 Elasticsearch 查询,通常用于需要更复杂或特定的查询场景。

使用案例

CodeNativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
searchQueryBuilder.withQuery(QueryBuilders.matchQuery("fieldName", "value"));
NativeSearchQuery searchQuery = searchQueryBuilder.build();
List<MyEntity> results = elasticsearchOperations.queryForList(searchQuery, MyEntity.class);

总结

可以发现,在查询条件中,还是需要手动写出属性的名称,使用起来相对会麻烦一些。

具体的查询方式,可以自己去查找一下,例如范围查询,模糊查询,分词查询等等。

总结

最后:我是Walker,一个热爱分享的程序员,希望我的输出能够帮助到你

附录 repository关键词

KeywordSampleElasticsearch Query String
AndfindByNameAndPrice{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
OrfindByNameOrPrice{ "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
IsfindByName{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
NotfindByNameNot{ "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
BetweenfindByPriceBetween{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
LessThanfindByPriceLessThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } ] } }}
LessThanEqualfindByPriceLessThanEqual{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
GreaterThanfindByPriceGreaterThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } ] } }}
GreaterThanEqualfindByPriceGreaterThanEqual{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
BeforefindByPriceBefore{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
AfterfindByPriceAfter{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
LikefindByNameLike{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
StartingWithfindByNameStartingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
EndingWithfindByNameEndingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
Contains/ContainingfindByNameContaining{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
In (when annotated as FieldType.Keyword)findByNameIn(Collection<String>names){ "query" : { "bool" : { "must" : [ {"bool" : {"must" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
InfindByNameIn(Collection<String>names){ "query": {"bool": {"must": [{"query_string":{"query": "\"?\" \"?\"", "fields": ["name"]}}]}}}
NotIn (when annotated as FieldType.Keyword)findByNameNotIn(Collection<String>names){ "query" : { "bool" : { "must" : [ {"bool" : {"must_not" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
NotInfindByNameNotIn(Collection<String>names){"query": {"bool": {"must": [{"query_string": {"query": "NOT(\"?\" \"?\")", "fields": ["name"]}}]}}}
TruefindByAvailableTrue{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }}
FalsefindByAvailableFalse{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }}
OrderByfindByAvailableTrueOrderByNameDesc{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] }
ExistsfindByNameExists{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}
IsNullfindByNameIsNull{"query":{"bool":{"must_not":[{"exists":{"field":"name"}}]}}}
IsNotNullfindByNameIsNotNull{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}
IsEmptyfindByNameIsEmpty{"query":{"bool":{"must":[{"bool":{"must":[{"exists":{"field":"name"}}],"must_not":[{"wildcard":{"name":{"wildcard":"*"}}}]}}]}}}
IsNotEmptyfindByNameIsNotEmpty{"query":{"bool":{"must":[{"wildcard":{"name":{"wildcard":"*"}}}]}}}

相关注解

@Document:应用于类级别,指示该类可以映射到数据库。最重要的属性包括(检查 API 文档以获取完整属性列表):

@Id:应用于字段级别,用于标记用于身份目的的字段。

@Transient、@ReadOnlyProperty、@WriteOnlyProperty:请参见以下部分“控制哪些属性被写入和从 Elasticsearch 读取”的详细信息。

@PersistenceConstructor:标记在从数据库实例化对象时使用的构造函数——即使是包保护的构造函数。构造函数参数通过名称映射到检索到的文档中的键值。

@Field:应用于字段级别,定义字段的属性,大多数属性映射到相应的 Elasticsearch 映射定义(以下列表并不完整,请检查注解 Javadoc 以获取完整参考):

@GeoPoint:标记字段为 geo_point 数据类型。如果字段是 GeoPoint 类的实例,则可以省略此注解。

@ValueConverter:定义一个类,用于转换给定属性。与注册的 Spring 转换器不同,这仅转换注解的属性,而不是给定类型的所有属性。

@Transient:该注解表示该属性不会包含在映射中。其值不会发送到 Elasticsearch,并且在从 Elasticsearch 检索文档时,该属性不会在实体中被填充。

@ReadOnlyProperty:此注解标记一个属性,该属性不会写入 Elasticsearch,但在数据检索时,将根据 Elasticsearch 文档中的值填充该属性。适用于索引映射中定义的运行时字段。

@WriteOnlyProperty:该注解指示一个属性会存储在 Elasticsearch 中,但在读取文档时不会被填充。通常用于需要索引到 Elasticsearch 但在其他地方不需要使用的合成字段。

参考

官方文档

https://docs.spring.io/spring-data/elasticsearch/docs/

到此这篇关于Springboot整合spring-boot-starter-data-elasticsearch的文章就介绍到这了,更多相关Springboot整合spring-boot-starter-data-elasticsearch内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文