创建项目
使用SpringBoot初始化工具初始化一个SpringBoot的基础项目,并添加相关的依赖
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>5.0.0</version>
</dependency>
<!--JPA包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--ElasticSearch包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
修改SpringBoot的配置文件
server:
port: 8888
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/xxxxx?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: xxxxx
password: xxxxx
hikari:
connection-test-query: 'select 1'
jpa:
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true
open-in-view: false
elasticsearch:
uris: http://localhost:9200
connection-timeout: 10s
socket-timeout: 30s
logging:
file:
name: ./log/out.log
level:
root: info
org.springframework.data.elasticsearch.client.WIRE: trace
配置文件
使用@configuration
注解标明ES的配置文件
package com.example.crontestjpa.config;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
import java.time.Duration;
/**
* Created By IntelliJ IDEA
*
* @author IceCreamQAQ
* @datetime 2022/11/19 星期六
* Happy Every Coding Time~
*/
@Configuration
public class ElasticSearchConfig extends AbstractElasticsearchConfiguration {
@Bean
@Override
public RestHighLevelClient elasticsearchClient() {
ClientConfiguration configuration = ClientConfiguration.builder()
.connectedTo("127.0.0.1:9200")
.withConnectTimeout(Duration.ofSeconds(10))
// 设置
.withSocketTimeout(Duration.ofSeconds(30))
.build();
return RestClients.create(configuration).rest();
}
}
至此,已经完成了在SpringBoot中对ES的配置,之后可以对ES进行相关的操作
而经过了SpringData的整合之后,我们可以像操作数据库一般去实现对ElasticSearch的CRUD
没错,几乎同样的流程:定义实体 -> 创建实体对应的**repository**
-> 使用**repository**
完成相关的业务
操作ElasticSearch
将一些通用的属性封装成实体类,其他的实体类只需要继承此类便可拥有这些属性,而不用进行重复的操作
/**
* Created By IntelliJ IDEA
*
* @author IceCreamQAQ
* @datetime 2022/11/20 星期日
* Happy Every Coding Time~
*/
@MappedSuperclass
@Data
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class BaseModel implements Serializable {
private static final long serialVersionUID = -8578918541296855061L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@CreatedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime createTime;
@LastModifiedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime updateTime;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
BaseModel baseModel = (BaseModel) o;
return id != null && Objects.equals(id, baseModel.id);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
}
/**
* Created By IntelliJ IDEA
*
* @author IceCreamQAQ
* @datetime 2022/11/20 星期日
* Happy Every Coding Time~
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(createIndex = false, indexName = "base")
public class BaseESModel implements Serializable {
private static final long serialVersionUID = 6681455862653089706L;
@Id
@Field(type = FieldType.Keyword)
private String id;
@CreatedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8", shape = JsonFormat.Shape.STRING)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
private LocalDateTime createTime;
@LastModifiedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8", shape = JsonFormat.Shape.STRING)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
private LocalDateTime updateTime;
}
定义对象映射关系
这部分类似于对数据库操作时定义实体
定义ESBook类并继承于上述BaseESModel
/**
* Created By IntelliJ IDEA
*
* @author IceCreamQAQ
* @datetime 2022/11/20 星期日
* Happy Every Coding Time~
*/
@Document(indexName = "es_book")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ESBook extends BaseESModel {
@Field(type = FieldType.Keyword)
private String name;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String author;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String description;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
ESBook esBook = (ESBook) o;
return Objects.equals(name, esBook.name) && Objects.equals(author, esBook.author) && Objects.equals(description, esBook.description);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), name, author, description);
}
}
@Document
:表明这是映射到ES中的DOC,其中的indexName
属性用来表示此实体类对应的索引名称@Id
:对应到ES中的唯一标识Id@Field
:定义字段类型等信息
定义Repository
定义ESBookRepository
接口并继承于ElasticsearchRepository<ESBook, String>
/**
* Created By IntelliJ IDEA
*
* @author IceCreamQAQ
* @datetime 2022/11/20 星期日
* Happy Every Coding Time~
*/
@Repository
public interface ESBookRepository extends ElasticsearchRepository<ESBook, Long> {
}
而同JPA中操作一样,当你继承了这个接口之后,就拥有了一些基础的CRUD的操作能力:
还可以通过
@Query
创建查询,使用@Highlight
来定义关键字高亮
并且,同样支持定义方法的名称来进行自定义的查询操作,方法名支持的关键字如下:
:::success
✋ 在这里,为了简洁,省略了创建数据库实体和对应的repository
的过程。但是需要注意以下几点:
- 定义ElasticSearch的对象映射关系使用的是
@Document
注解,定义数据库实体使用的是@Entity
- 定义两者的持久层接口时,继承的接口不同,一个继承的是
ElasticsearchRepository
,另外一个继承的是JPARepository
- 为了直观体现两者的不同,可以将其定义在不同的包下,如下所示:
- 可以在
SpringBoot
启动类上使用注解配置两者的扫描包路径,如下所示:
:::
定义Service
这里不再是单单创建ES的Service,而是实现一个小需求:在保存业务数据到数据库的时候,将数据保存一份到ES中,在保存到ES中时,我们模拟高并发下的延时情况,使用异步保存的方式
可以看到,我们保存到ES中的操作其实是在用户拿到返回结果之后,也就是我们的异步操作是有效的
但我们也可以在JPA提供的钩子函数中执行保存ES的操作,如下所示:
可以看到,我们在这个函数中是能拿到保存到数据库中的数据的,因此,也可以在这里进行异步保存到ES的操作。
自定义查询
正如上文提到的,我们可以通过设置方法名,来实现自定义查询,这是极其语义化的
但注意的是,我们使用findAll()
方法,获取到的数据类型是Page<ESUser>
,而不是List
列表