创建项目

使用SpringBoot初始化工具初始化一个SpringBoot的基础项目,并添加相关的依赖

  1. <dependency>
  2. <groupId>org.modelmapper</groupId>
  3. <artifactId>modelmapper</artifactId>
  4. <version>3.1.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.querydsl</groupId>
  8. <artifactId>querydsl-jpa</artifactId>
  9. <version>5.0.0</version>
  10. </dependency>
  11. <!--JPA包-->
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-starter-data-jpa</artifactId>
  15. </dependency>
  16. <!--ElasticSearch包-->
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-web</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>com.mysql</groupId>
  27. <artifactId>mysql-connector-j</artifactId>
  28. <scope>runtime</scope>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.projectlombok</groupId>
  32. <artifactId>lombok</artifactId>
  33. <optional>true</optional>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.springframework.boot</groupId>
  37. <artifactId>spring-boot-starter-test</artifactId>
  38. <scope>test</scope>
  39. </dependency>

修改SpringBoot的配置文件

  1. server:
  2. port: 8888
  3. spring:
  4. datasource:
  5. driver-class-name: com.mysql.cj.jdbc.Driver
  6. url: jdbc:mysql://localhost:3306/xxxxx?useUnicode=true&characterEncoding=utf-8&useSSL=false
  7. username: xxxxx
  8. password: xxxxx
  9. hikari:
  10. connection-test-query: 'select 1'
  11. jpa:
  12. hibernate:
  13. ddl-auto: update
  14. database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
  15. show-sql: true
  16. open-in-view: false
  17. elasticsearch:
  18. uris: http://localhost:9200
  19. connection-timeout: 10s
  20. socket-timeout: 30s
  21. logging:
  22. file:
  23. name: ./log/out.log
  24. level:
  25. root: info
  26. org.springframework.data.elasticsearch.client.WIRE: trace

配置文件

使用@configuration注解标明ES的配置文件

  1. package com.example.crontestjpa.config;
  2. import org.elasticsearch.client.RestHighLevelClient;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.data.elasticsearch.client.ClientConfiguration;
  6. import org.springframework.data.elasticsearch.client.RestClients;
  7. import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
  8. import java.time.Duration;
  9. /**
  10. * Created By IntelliJ IDEA
  11. *
  12. * @author IceCreamQAQ
  13. * @datetime 2022/11/19 星期六
  14. * Happy Every Coding Time~
  15. */
  16. @Configuration
  17. public class ElasticSearchConfig extends AbstractElasticsearchConfiguration {
  18. @Bean
  19. @Override
  20. public RestHighLevelClient elasticsearchClient() {
  21. ClientConfiguration configuration = ClientConfiguration.builder()
  22. .connectedTo("127.0.0.1:9200")
  23. .withConnectTimeout(Duration.ofSeconds(10))
  24. // 设置
  25. .withSocketTimeout(Duration.ofSeconds(30))
  26. .build();
  27. return RestClients.create(configuration).rest();
  28. }
  29. }

至此,已经完成了在SpringBoot中对ES的配置,之后可以对ES进行相关的操作
而经过了SpringData的整合之后,我们可以像操作数据库一般去实现对ElasticSearch的CRUD
没错,几乎同样的流程:定义实体 -> 创建实体对应的**repository** -> 使用**repository**完成相关的业务

操作ElasticSearch

将一些通用的属性封装成实体类,其他的实体类只需要继承此类便可拥有这些属性,而不用进行重复的操作

  1. /**
  2. * Created By IntelliJ IDEA
  3. *
  4. * @author IceCreamQAQ
  5. * @datetime 2022/11/20 星期日
  6. * Happy Every Coding Time~
  7. */
  8. @MappedSuperclass
  9. @Data
  10. @AllArgsConstructor
  11. @NoArgsConstructor
  12. @EntityListeners(AuditingEntityListener.class)
  13. public class BaseModel implements Serializable {
  14. private static final long serialVersionUID = -8578918541296855061L;
  15. @Id
  16. @GeneratedValue(strategy = GenerationType.IDENTITY)
  17. private Long id;
  18. @CreatedDate
  19. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
  20. @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
  21. private LocalDateTime createTime;
  22. @LastModifiedDate
  23. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
  24. @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
  25. private LocalDateTime updateTime;
  26. @Override
  27. public boolean equals(Object o) {
  28. if (this == o) return true;
  29. if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
  30. BaseModel baseModel = (BaseModel) o;
  31. return id != null && Objects.equals(id, baseModel.id);
  32. }
  33. @Override
  34. public int hashCode() {
  35. return getClass().hashCode();
  36. }
  37. }
  1. /**
  2. * Created By IntelliJ IDEA
  3. *
  4. * @author IceCreamQAQ
  5. * @datetime 2022/11/20 星期日
  6. * Happy Every Coding Time~
  7. */
  8. @Data
  9. @AllArgsConstructor
  10. @NoArgsConstructor
  11. @Document(createIndex = false, indexName = "base")
  12. public class BaseESModel implements Serializable {
  13. private static final long serialVersionUID = 6681455862653089706L;
  14. @Id
  15. @Field(type = FieldType.Keyword)
  16. private String id;
  17. @CreatedDate
  18. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8", shape = JsonFormat.Shape.STRING)
  19. @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
  20. @Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
  21. private LocalDateTime createTime;
  22. @LastModifiedDate
  23. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8", shape = JsonFormat.Shape.STRING)
  24. @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
  25. @Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
  26. private LocalDateTime updateTime;
  27. }

定义对象映射关系

这部分类似于对数据库操作时定义实体

定义ESBook类并继承于上述BaseESModel

  1. /**
  2. * Created By IntelliJ IDEA
  3. *
  4. * @author IceCreamQAQ
  5. * @datetime 2022/11/20 星期日
  6. * Happy Every Coding Time~
  7. */
  8. @Document(indexName = "es_book")
  9. @Data
  10. @AllArgsConstructor
  11. @NoArgsConstructor
  12. @Builder
  13. public class ESBook extends BaseESModel {
  14. @Field(type = FieldType.Keyword)
  15. private String name;
  16. @Field(type = FieldType.Text, analyzer = "ik_max_word")
  17. private String author;
  18. @Field(type = FieldType.Text, analyzer = "ik_max_word")
  19. private String description;
  20. @Override
  21. public boolean equals(Object o) {
  22. if (this == o) return true;
  23. if (o == null || getClass() != o.getClass()) return false;
  24. if (!super.equals(o)) return false;
  25. ESBook esBook = (ESBook) o;
  26. return Objects.equals(name, esBook.name) && Objects.equals(author, esBook.author) && Objects.equals(description, esBook.description);
  27. }
  28. @Override
  29. public int hashCode() {
  30. return Objects.hash(super.hashCode(), name, author, description);
  31. }
  32. }
  • @Document:表明这是映射到ES中的DOC,其中的indexName属性用来表示此实体类对应的索引名称
  • @Id:对应到ES中的唯一标识Id
  • @Field:定义字段类型等信息

定义Repository

定义ESBookRepository接口并继承于ElasticsearchRepository<ESBook, String>

  1. /**
  2. * Created By IntelliJ IDEA
  3. *
  4. * @author IceCreamQAQ
  5. * @datetime 2022/11/20 星期日
  6. * Happy Every Coding Time~
  7. */
  8. @Repository
  9. public interface ESBookRepository extends ElasticsearchRepository<ESBook, Long> {
  10. }

而同JPA中操作一样,当你继承了这个接口之后,就拥有了一些基础的CRUD的操作能力:
image.png

还可以通过@Query创建查询,使用@Highlight来定义关键字高亮

并且,同样支持定义方法的名称来进行自定义的查询操作,方法名支持的关键字如下:
image.png
image.png

:::success ✋ 在这里,为了简洁,省略了创建数据库实体和对应的repository的过程。但是需要注意以下几点:

  • 定义ElasticSearch的对象映射关系使用的是@Document注解,定义数据库实体使用的是@Entity
  • 定义两者的持久层接口时,继承的接口不同,一个继承的是ElasticsearchRepository,另外一个继承的是JPARepository
  • 为了直观体现两者的不同,可以将其定义在不同的包下,如下所示:
    image.png
  • 可以在 SpringBoot启动类上使用注解配置两者的扫描包路径,如下所示:
    image.png :::

定义Service

这里不再是单单创建ES的Service,而是实现一个小需求:在保存业务数据到数据库的时候,将数据保存一份到ES中,在保存到ES中时,我们模拟高并发下的延时情况,使用异步保存的方式

image.png

image.png
可以看到,我们保存到ES中的操作其实是在用户拿到返回结果之后,也就是我们的异步操作是有效的
但我们也可以在JPA提供的钩子函数中执行保存ES的操作,如下所示:
image.png
可以看到,我们在这个函数中是能拿到保存到数据库中的数据的,因此,也可以在这里进行异步保存到ES的操作。

自定义查询

正如上文提到的,我们可以通过设置方法名,来实现自定义查询,这是极其语义化
image.png

但注意的是,我们使用findAll()方法,获取到的数据类型是Page<ESUser>,而不是List列表
image.png