提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- SpringData
-
- [Spring Data Elasticsearch 介绍](#Spring Data Elasticsearch 介绍)
- 1.SpringData-代码功能集成
- 2.SpringData---索引操作
- 3.SpringData---文档操作(ElasticSearchRepository)
SpringData
- Spring Data是一个用于简化数据库、非关系型数据库、索引库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持 map-reduce框架和云计算数据服务。
- Spring Data可以极大的简化JPA(Elasticsearch...)的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一些常用的功能。
官网: https://spring.io/projects/spring-data
Spring Data Elasticsearch 介绍
- Spring Data Elasticsearch基于Spring Data API简化 Elasticsearch 操作,将原始操作Elasticsearch 的客户端API进行封装。Spring Data为Elasticsearch 项目提供集成搜索引擎。Spring Data Elasticsearch POJO的关键功能区域为中心的模型与Elastichsearch交互文档和轻松地编写一个存储索引库数据访问层。
1.SpringData-代码功能集成
1.增加依赖
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.lun</groupId>
<artifactId>SpringDataWithES</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
</dependencies>
</project>
2.增加配置文件
java
# es 服务地址
elasticsearch.host=127.0.0.1
# es 服务端口
elasticsearch.port=9200
# 配置日志级别,开启 debug 日志
logging.level.com.atguigu.es=debug
3. 数据实体类
java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
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;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Document(indexName = "shopping", shards = 3, replicas = 1)
public class Product {
//必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"_id"
@Id
private Long id;//商品唯一标识
/**
* type : 字段数据类型
* analyzer : 分词器类型
* index : 是否索引(默认:true)
* Keyword : 短语,不进行分词
*/
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title;//商品名称
@Field(type = FieldType.Keyword)
private String category;//分类名称
@Field(type = FieldType.Double)
private Double price;//商品价格
@Field(type = FieldType.Keyword, index = false)
private String images;//图片地址
}
让自定义的类型和ElasticSearch中的一个索引产生关联
- Document - spring data elasticsearch提供的注解, 描述类型,说明类型和索引的关系。
- indexName - 对应的索引的名称。 必要属性。
- shards - 创建索引时,设置的主分片数量。 默认5
- replicas - 创建索引时,设置的副本分片数量。 默认1
- type - 对应的类型的名称。
案例解析
java
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;
/**
* 自定义类型,商品。
* 让自定义的类型和ElasticSearch中的一个索引产生关联。
* Document - spring data elasticsearch提供的注解, 描述类型,说明类型和索引的关系。
* indexName - 对应的索引的名称。 必要属性。
* shards - 创建索引时,设置的主分片数量。 默认5
* replicas - 创建索引时,设置的副本分片数量。 默认1
* type - 对应的类型的名称。
* @create 2021-02-24 上午 10:42
*/
@Document(indexName = "hrt-item", shards = 2, replicas = 2, type = "item")
public class Item {
/**
* Id注解是Spring Data核心工程提供的,是所有的Spring Data二级子工程通用的。
* 代表主键字段。
*/
@Id
private String id;
/**
* Field注解,描述实体类型中属性和ES索引中字段的关系。
* 且可以为这个字段配置自定义映射mapping
* 这个自定义映射必须通过代码逻辑调用设置映射的API才能生效。
* name - 索引中对应的字段名称,默认和属性同名。
* type - 索引中字段的类型,默认是FieldType.Auto,代表ES自动映射类型。
* analyzer - 字段的分词器名称,默认是standard。
* fielddata - 是否开启正向索引。默认关闭。
* 默认只为文本类型的字段创建反向索引,提供快速搜索逻辑。
* fielddata设置为true,则会额外创建一个正向索引,支持排序。
* index - 是否创建默认的反向索引或正向索引。 text文本类型字段默认创建反向索引,其他创建正向索引。
* 没有索引,就不能作为搜索条件。
*/
@Field(name = "title", type = FieldType.Text, analyzer = "ik_max_word", fielddata = true)
private String title; // 商品名称,需要中文分词,且偶尔需要排序, 常用搜索条件之一
@Field(name = "sellPoint", type = FieldType.Text, analyzer = "ik_max_word")
private String sellPoint; // 卖点, 需要中文分词, 常用搜索条件之一
@Field(type = FieldType.Long)
private Long price; // 单价
@Field(type = FieldType.Integer, index = false)
private int num; // 库存
}
4.配置类
- ElasticsearchRestTemplate是spring-data-elasticsearch项目中的一个类,和其他spring项目中的
template类似。 - 在新版的spring-data-elasticsearch 中,ElasticsearchRestTemplate 代替了原来的ElasticsearchTemplate。
- 原因是ElasticsearchTemplate基于TransportClient,TransportClient即将在8.x 以后的版本中移除。所以,我们推荐使用ElasticsearchRestTemplate。
- ElasticsearchRestTemplate基于RestHighLevelClient客户端的。需要自定义配置类,继承AbstractElasticsearchConfiguration,并实现elasticsearchClient()抽象方法,创建RestHighLevelClient对象。
需要自定义配置类,继承AbstractElasticsearchConfiguration,并实现elasticsearchClient()抽象方法,创建RestHighLevelClient对象。
java
import lombok.Data;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
@ConfigurationProperties(prefix = "elasticsearch")
@Configuration
@Data
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration{
private String host ;
private Integer port ;
//重写父类方法
@Override
public RestHighLevelClient elasticsearchClient() {
RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
RestHighLevelClient restHighLevelClient = new
RestHighLevelClient(builder);
return restHighLevelClient;
}
}
5.DAO 数据访问对象
java
import com.lun.model.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductDao extends ElasticsearchRepository<Product, Long>{
}
2.SpringData---索引操作
索引操作
java
import com.lun.model.Product;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataESIndexTest {
//注入 ElasticsearchRestTemplate
@Autowired
private ElasticsearchRestTemplate restTemplate;
//创建索引并增加映射配置
@Test
public void createIndex(){
//创建索引,系统初始化会自动创建索引
System.out.println("创建索引");
}
@Test
public void deleteIndex(){
//创建索引,系统初始化会自动创建索引
boolean flg = restTemplate.deleteIndex(Product.class);
System.out.println("删除索引 = " + flg);
}
}
创建索引
- createIndex(): 创建索引,创建出来的索引是不带有 mapping 信息的。返回值表示是 否创建成功
- putMapping():为已有的索引添加 mapping 信息。不具备创建索引的能力。返回值表 示是否创建成功
java
/**
* 创建索引,并设置映射。
* 需要通过两次访问实现,1、创建索引;2、设置映射。
*/
@Test
public void testInitIndex(){
// 创建索引,根据类型上的Document注解创建
boolean isCreated = restTemplate.createIndex(Item.class);
// 设置映射,根据属性上的Field注解设置 0201
boolean isMapped = restTemplate.putMapping(Item.class);
System.out.println("创建索引是否成功:" + isCreated);
System.out.println("设置映射是否成功:" + isMapped);
}
删除索引
java
/**
* 删除索引
*/
@Test
public void deleteIndex(){
// 扫描Item类型上的Document注解,删除对应的索引。
boolean isDeleted = restTemplate.deleteIndex(Item.class);
System.out.println("删除Item对应索引是否成功:" + isDeleted);
// 直接删除对应名称的索引。
isDeleted = restTemplate.deleteIndex("test_index3");
System.out.println("删除default_index索引是否成功:" + isDeleted);
}
3.SpringData---文档操作(ElasticSearchRepository)
spring-data-elasticsearch是比较好用的一个elasticsearch客户端,本文介绍如何使用它来操作ES。本文使用spring-boot-starter-data-elasticsearch,它内部会引入spring-data-elasticsearch。
Spring Data ElasticSearch有下边这几种方法操作ElasticSearch:
- ElasticsearchRepository(传统的方法,可以使用)
- ElasticsearchRestTemplate(推荐使用。基于RestHighLevelClient)
- ElasticsearchTemplate(ES7中废弃,不建议使用。基于TransportClient)
- RestHighLevelClient(推荐度低于ElasticsearchRestTemplate,因为API不够高级)
- TransportClient(ES7中废弃,不建议使用)
本文仅展示使用ElasticsearchRepository的操作
文档操作
java
import com.lun.dao.ProductDao;
import com.lun.model.Product;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataESProductDaoTest {
@Autowired
private ProductDao productDao;
/**
* 新增
*/
@Test
public void save(){
Product product = new Product();
product.setId(2L);
product.setTitle("华为手机");
product.setCategory("手机");
product.setPrice(2999.0);
product.setImages("http://www.atguigu/hw.jpg");
productDao.save(product);
}
//POSTMAN, GET http://localhost:9200/product/_doc/2
//修改
@Test
public void update(){
Product product = new Product();
product.setId(2L);
product.setTitle("小米 2 手机");
product.setCategory("手机");
product.setPrice(9999.0);
product.setImages("http://www.atguigu/xm.jpg");
productDao.save(product);
}
//POSTMAN, GET http://localhost:9200/product/_doc/2
//根据 id 查询
@Test
public void findById(){
Product product = productDao.findById(2L).get();
System.out.println(product);
}
@Test
public void findAll(){
Iterable<Product> products = productDao.findAll();
for (Product product : products) {
System.out.println(product);
}
}
//删除
@Test
public void delete(){
Product product = new Product();
product.setId(2L);
productDao.delete(product);
}
//POSTMAN, GET http://localhost:9200/product/_doc/2
//批量新增
@Test
public void saveAll(){
List<Product> productList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Product product = new Product();
product.setId(Long.valueOf(i));
product.setTitle("["+i+"]小米手机");
product.setCategory("手机");
product.setPrice(1999.0 + i);
product.setImages("http://www.atguigu/xm.jpg");
productList.add(product);
}
productDao.saveAll(productList);
}
//分页查询
@Test
public void findByPageable(){
//设置排序(排序方式,正序还是倒序,排序的 id)
Sort sort = Sort.by(Sort.Direction.DESC,"id");
int currentPage=0;//当前页,第一页从 0 开始, 1 表示第二页
int pageSize = 5;//每页显示多少条
//设置查询分页
PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort);
//分页查询
Page<Product> productPage = productDao.findAll(pageRequest);
for (Product Product : productPage.getContent()) {
System.out.println(Product);
}
}
}
term 查询 分页
java
import com.lun.dao.ProductDao;
import com.lun.model.Product;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataESSearchTest {
@Autowired
private ProductDao productDao;
/**
* term 查询
* search(termQueryBuilder) 调用搜索方法,参数查询构建器对象
*/
@Test
public void termQuery(){
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "小米");
Iterable<Product> products = productDao.search(termQueryBuilder);
for (Product product : products) {
System.out.println(product);
}
}
/**
* term 查询加分页
*/
@Test
public void termQueryByPage(){
int currentPage= 0 ;
int pageSize = 5;
//设置查询分页
PageRequest pageRequest = PageRequest.of(currentPage, pageSize);
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "小米");
Iterable<Product> products =
productDao.search(termQueryBuilder,pageRequest);
for (Product product : products) {
System.out.println(product);
}
}
}
文档搜索
java
package com.example.demo.controller;
import com.example.demo.dao.BlogRepository;
import com.example.demo.entity.Blog;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Api(tags = "增删改查(文档)")
@RestController
@RequestMapping("crud")
public class CrudController {
@Autowired
private BlogRepository blogRepository;
@ApiOperation("添加单个文档")
@PostMapping("addDocument")
public Blog addDocument() {
Long id = 1L;
Blog blog = new Blog();
blog.setBlogId(id);
blog.setTitle("Spring Data ElasticSearch学习教程" + id);
blog.setContent("这是添加单个文档的实例" + id);
blog.setAuthor("Tony");
blog.setCategory("ElasticSearch");
blog.setCreateTime(new Date());
blog.setStatus(1);
blog.setSerialNum(id.toString());
return blogRepository.save(blog);
}
@ApiOperation("添加多个文档")
@PostMapping("addDocuments")
public Object addDocuments(Integer count) {
List<Blog> blogs = new ArrayList<>();
for (int i = 1; i <= count; i++) {
Long id = (long)i;
Blog blog = new Blog();
blog.setBlogId(id);
blog.setTitle("Spring Data ElasticSearch学习教程" + id);
blog.setContent("这是添加单个文档的实例" + id);
blog.setAuthor("Tony");
blog.setCategory("ElasticSearch");
blog.setCreateTime(new Date());
blog.setStatus(1);
blog.setSerialNum(id.toString());
blogs.add(blog);
}
return blogRepository.saveAll(blogs);
}
/**
* 跟新增是同一个方法。若id已存在,则修改。
* 无法只修改某个字段,只能覆盖所有字段。若某个字段没有值,则会写入null。
* @return 成功写入的数据
*/
@ApiOperation("修改单个文档")
@PostMapping("editDocument")
public Blog editDocument() {
Long id = 1L;
Blog blog = new Blog();
blog.setBlogId(id);
blog.setTitle("Spring Data ElasticSearch学习教程" + id);
blog.setContent("这是修改单个文档的实例" + id);
// blog.setAuthor("Tony");
// blog.setCategory("ElasticSearch");
// blog.setCreateTime(new Date());
// blog.setStatus(1);
// blog.setSerialNum(id.toString());
return blogRepository.save(blog);
}
@ApiOperation("查找单个文档")
@GetMapping("findById")
public Blog findById(Long id) {
return blogRepository.findById(id).get();
}
@ApiOperation("删除单个文档")
@PostMapping("deleteDocument")
public String deleteDocument(Long id) {
blogRepository.deleteById(id);
return "success";
}
@ApiOperation("删除所有文档")
@PostMapping("deleteDocumentAll")
public String deleteDocumentAll() {
blogRepository.deleteAll();
return "success";
}
}