美文网首页
SpringData整合Elasticsearch案例

SpringData整合Elasticsearch案例

作者: nextliving | 来源:发表于2018-04-22 15:28 被阅读818次

之前在自己的mac上安装了elasticsearch(以下简称ES),现在想要在java项目中使用ES。容器选择了spring boot,可以较快开发,数据层使用spring data elasticsearch操作ES。

创建spring boot工程

Eclipse上可以安装Spring IDE插件(也被称为spring tool suite),然后可以File->New->Project->Spring Starter Project->next,输入项目名称以及其它项目配置,勾选NoSql中的ElasticSearch组件,然后一路next,至此,一个spring boot工程创建成功。创建成功的工程pom.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>

<groupId>com.ms</groupId>

<artifactId>boot-springDataES-1-demo</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>war</packaging>

<name>boot-springDataES-1-demo</name>

<description>Demo project for Spring Data ElasticSearch</description>

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>1.5.9.RELEASE</version>

<relativePath/> <!-- lookup parent from repository -->

</parent>

<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<java.version>1.8</java.version>

</properties>

<dependencies>

<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>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>

</project>

配置ES

打卡ES安装目录下的config中的elasticsearch.yml,配置cluster.name为以下内容:

cluster.name: iengchen

然后打开项目的application.properties文件,添加以下内容:


#cluster-name要和Elastic Search安装目录下的config中的elasticsearch.yml中的cluster.name保持一致

spring.data.elasticsearch.cluster-name=iengchen

spring.data.elasticsearch.cluster-nodes=localhost:9300

终端中启动ES:


$ elasticsearch

输出日志为


[2018-01-11 15:41:49,345][INFO ][node  ] [Korvus] version[2.4.6], pid[43622], build[5376dca/2017-07-18T12:17:44Z]

[2018-01-11 15:41:49,345][INFO ][node  ] [Korvus] initializing ...

[2018-01-11 15:41:49,730][INFO ][plugins ] [Korvus] modules [reindex, lang-expression, lang-groovy], plugins [], sites []

[2018-01-11 15:41:49,767][INFO ][env ] [Korvus] using [1] data paths, mounts [[/ (/dev/disk1s1)]], net usable_space [280.9gb], net total_space [465.7gb], spins? [unknown], types [apfs]

[2018-01-11 15:41:49,767][INFO ][env ] [Korvus] heap size [989.8mb], compressed ordinary object pointers [true]

[2018-01-11 15:41:49,768][WARN ][env ] [Korvus] max file descriptors [10240] for elasticsearch process likely too low, consider increasing to at least [65536]

[2018-01-11 15:41:50,808][INFO ][node  ] [Korvus] initialized

[2018-01-11 15:41:50,808][INFO ][node  ] [Korvus] starting ...

[2018-01-11 15:41:50,859][INFO ][transport ] [Korvus] publish_address {127.0.0.1:9300}, bound_addresses {[fe80::1]:9300}, {[::1]:9300}, {127.0.0.1:9300}

[2018-01-11 15:41:50,862][INFO ][discovery ] [Korvus] iengchen/PvX7kAP7QF62_N-H9c7X0Q

[2018-01-11 15:41:54,889][INFO ][cluster.service ] [Korvus] new_master {Korvus}{PvX7kAP7QF62_N-H9c7X0Q}{127.0.0.1}{127.0.0.1:9300}, reason: zen-disco-join(elected_as_master, [0] joins received)

[2018-01-11 15:41:54,904][INFO ][http  ] [Korvus] publish_address {127.0.0.1:9200}, bound_addresses {[fe80::1]:9200}, {[::1]:9200}, {127.0.0.1:9200}

[2018-01-11 15:41:54,904][INFO ][node  ] [Korvus] started

[2018-01-11 15:41:54,917][INFO ][gateway ] [Korvus] recovered [0] indices into cluster_state

浏览器打开http://127.0.0.1:9200/ 应显示如下内容:


{

 "name": "Frey",

 "cluster_name": "iengchen",

 "cluster_uuid": "J79XWt8SQKinREan86QdHw",

 "version": {

 "number": "2.4.6",

 "build_hash": "5376dca9f70f3abef96a77f4bb22720ace8240fd",

 "build_timestamp": "2017-07-18T12:17:44Z",

 "build_snapshot": false,

 "lucene_version": "5.5.4"

 },

 "tagline": "You Know, for Search"

}

实体模型编写

建立一个商品的数据模型Product:


@Document(indexName="iengchen",type="product")

public class Product {

@Id

private Long id;

//商品名称

private String prodName;

//商品品牌,eg:松下,华为

private String prodBrand;

//商品分类,eg:服装,电子,软件

private String prodCategory;

//必须有无参构造器,否则会导致Jackson解析错误

public Product() {

// TODO Auto-generated constructor stub

}

public Product(Long id,String prodName,String brand,String prodCategory) {

this.id = id;

this.prodName = prodName;

this.prodBrand = brand;

this.prodCategory = prodCategory;

}

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

public String getProdName() {

return prodName;

}

public void setProdName(String prodName) {

this.prodName = prodName;

}

public String getProdCategory() {

return prodCategory;

}

public void setProdCategory(String prodCategory) {

this.prodCategory = prodCategory;

}

public String getProdBrand() {

return prodBrand;

}

public void setProdBrand(String prodBrand) {

this.prodBrand = prodBrand;

}

}

@Document和@Field用于为某个实体类建立索引,@Field用于注解实体类的属性,本案例中没有使用@Field注解。如果只用@Document,会默认为每一个属性都建立索引。如果@Document和@Field同时使用,只会为@Field注解的属性建立索引。查询该实体类对应的索引的方法为在浏览器中输入 http://127.0.0.1:9200/iengchen/_mapping url中的iengchen对应@Document注解中的indexName值。

Dao层编写

使用spring data elasticsearch时Dao只需要声明一个接口即可:


public interface ProductRepository extends ElasticsearchRepository<Product, Long>{

//根据产品名称查询并分页

Page<Product> findByProdName(String prodname,Pageable pageable);

//根据产品分类查询

List<Product> findByProdCategory(String prodcategory);

}

Service层编写

service层分为接口和实现。接口内容为:


public interface ProductService {

//保存商品

Product save(Product product);

//删除全部产品

void deleteAll();

//根据产品id删除指定商品

void deleteByProductId(long id);

//查询全部商品

Iterable<Product> findAll();

//根据商品id查询指定商品

Product findByProductId(long id);

//根据商品名称查询商品,并分页

Page<Product> findByProductName(String name,PageRequest pageRequest);

//根据商品分类查询商品

List<Product> findByProductCategory(String category);

}

service实现为:


//在实现类而不是接口上添加注解

@Service

public class ProductServiceImpl implements ProductService {

@Autowired

private ProductRepository prodRepository;

@Override

public Product save(Product product) {

return prodRepository.save(product);

}

@Override

public void deleteByProductId(long id) {

prodRepository.delete(id);

}

@Override

public Product findByProductId(long id) {

return prodRepository.findOne(id);

}

@Override

public Page<Product> findByProductName(String name,PageRequest pageRequest) {

return prodRepository.findByProdName(name,pageRequest);

}

@Override

public List<Product> findByProductCategory(String category) {

return prodRepository.findByProdCategory(category);

}

@Override

public void deleteAll() {

prodRepository.deleteAll();

}

@Override

public Iterable<Product> findAll() {

return prodRepository.findAll();

}

}

测试代码编写

测试类内容为:


@RunWith(SpringRunner.class)

@SpringBootTest

public class BootSpringDataEs1DemoApplicationTests {

@Autowired

private ProductService productService;

private ObjectMapper mapper = new ObjectMapper();

//向ES中插入全部所需商品数据

@Test

public void insertAllData() {

productService.save(new Product(1L, "电吹风","飞利浦", "小家电"));

productService.save(new Product(2L, "电吹风","美的", "小家电"));

productService.save(new Product(3L, "电吹风","松下", "小家电"));

productService.save(new Product(4L, "电吹风","索尼", "小家电"));

productService.save(new Product(5L, "电吹风","华为", "小家电"));

productService.save(new Product(6L, "电吹风","海尔", "小家电"));

productService.save(new Product(7L, "电吹风","戴森", "小家电"));

productService.save(new Product(8L, "电吹风","九阳", "小家电"));

productService.save(new Product(9L, "洗衣机","海尔", "小家电"));

productService.save(new Product(10L, "洗衣机","美的", "小家电"));

productService.save(new Product(11L, "电视机", "飞利浦","小家电"));

productService.save(new Product(12L, "电视机", "索尼","小家电"));

productService.save(new Product(13L, "电视机", "夏普","小家电"));

productService.save(new Product(14L, "电视机", "微鲸","小家电"));

productService.save(new Product(15L, "电视机", "小米","小家电"));

productService.save(new Product(16L, "电视机", "长虹","小家电"));

productService.save(new Product(17L, "电视机", "海尔","小家电"));

productService.save(new Product(18L, "电视机", "乐视","小家电"));;

productService.save(new Product(19L, "冰箱","飞利浦", "小家电"));

productService.save(new Product(20L, "音箱", "飞利浦","小家电"));

productService.save(new Product(21L, "手电筒","飞利浦", "小家电"));

productService.save(new Product(22L, "节能灯", "飞利浦","小家电"));

productService.save(new Product(23L, "手机", "华为","数码电子"));

productService.save(new Product(24L, "手机", "vivo","数码电子"));

productService.save(new Product(25L, "手机", "oppo","数码电子"));

productService.save(new Product(26L, "手机", "魅族","数码电子"));

productService.save(new Product(27L, "手机", "小米","数码电子"));

productService.save(new Product(28L, "手机", "摩托罗拉","数码电子"));

productService.save(new Product(29L, "手机", "金立","数码电子"));

productService.save(new Product(30L, "手机", "酷派","数码电子"));

productService.save(new Product(31L, "手机", "格力","数码电子"));

productService.save(new Product(32L, "摄像机", "飞利浦","数码电子"));

productService.save(new Product(33L, "照相机","飞利浦", "数码电子"));

productService.save(new Product(34L, "mp3", "飞利浦","数码电子"));

productService.save(new Product(35L, "手环", "飞利浦","数码电子"));

productService.save(new Product(36L, "iwatch","飞利浦", "数码电子"));

productService.save(new Product(37L, "床", "飞利浦","家具"));

}

//删除ES中存储的全部商品数据

@Test

public void deleteAllData() {

productService.deleteAll();

}

//根据商品id删除ES中指定产品数据

@Test

public void testDeleteByProdId() {

productService.deleteByProductId(1L);

}

//查询全部商品数据

@Test

public void testFindAll() {

ArrayList<Product> products = new ArrayList<>();

Iterator<Product> pIterator = productService.findAll().iterator();

while (pIterator.hasNext()) {

products.add(pIterator.next());

}

System.out.println("全部数据总数为: " + products.size()+",最初插入总数为37");

}

//根据商品id查询

@Test

public void testFindByProdId() {

Product product = productService.findByProductId(1L);

try {

System.out.println("商品id查询数据: " + mapper.writeValueAsString(product));

} catch (JsonProcessingException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

//根据商品名称查询,并分页

@Test

public void testFindByProdName() {

//ES中共有8条电吹风数据

Page<Product> prodPage = productService.findByProductName("电吹风", new PageRequest(0, 10));

//无论分页数设置为5还是10,输出的“数据条目总数:”都是8.只不过设置为5时后面打印“prod page:”时只会输出5条数据 

System.out.println("商品名查询数据条目总数:" + prodPage.getTotalElements());

try {

System.out.println("商品名查询数据: " + mapper.writeValueAsString(prodPage.getContent()));

} catch (JsonProcessingException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

//根据商品大类查询

@Test

public void testFindByProdCategory() {

List<Product> products = productService.findByProductCategory("数码电子");

try {

System.out.println("商品分类查询数据: " + mapper.writeValueAsString(products));

} catch (JsonProcessingException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

测试

插入全部数据

首先需要向ES中插入数据,执行insertAllData方法。这个案例中插入了37条数据,这些数据是后续执行其它测试方法的基础。可以看到终端中输出以下内容:


[2018-01-11 15:42:12,559][INFO ][cluster.metadata  ] [Korvus] [iengchen] creating index, cause [api], templates [], shards [5]/[1], mappings []

[2018-01-11 15:42:12,718][INFO ][cluster.metadata  ] [Korvus] [iengchen] create_mapping [product]

[2018-01-11 15:42:12,773][INFO ][cluster.routing.allocation] [Korvus] Cluster health status changed from [RED] to [YELLOW] (reason: [shards started [[iengchen][4]] ...]).

此时浏览器访问http://127.0.0.1:9200/iengchen/_mapping 查看ES索引库中的数据:


{

 "iengchen": {

 "mappings": {

 "product": {

 "properties": {

 "id": {

 "type": "long"

 },

 "prodBrand": {

 "type": "string"

 },

 "prodCategory": {

 "type": "string"

 },

 "prodName": {

 "type": "string"

 }

 }

 }

 }

 }

}

可以看到索引库中为Product类的每个属性都建立了索引。

查询全部数据

查询刚才插入的全部数据,执行testFindAll方法,eclipse的console中输出:


全部数据总数为: 37,最初插入总数为37

根据商品名称查询数据

执行testFindByProdName,console输出为


商品名查询数据条目总数:8

商品名查询数据: [{"id":8,"prodName":"电吹风","prodBrand":"九阳","prodCategory":"小家电"},{"id":5,"prodName":"电吹风","prodBrand":"华为","prodCategory":"小家电"},{"id":2,"prodName":"电吹风","prodBrand":"美的","prodCategory":"小家电"},{"id":4,"prodName":"电吹风","prodBrand":"索尼","prodCategory":"小家电"},{"id":6,"prodName":"电吹风","prodBrand":"海尔","prodCategory":"小家电"},{"id":1,"prodName":"电吹风","prodBrand":"飞利浦","prodCategory":"小家电"},{"id":7,"prodName":"电吹风","prodBrand":"戴森","prodCategory":"小家电"},{"id":3,"prodName":"电吹风","prodBrand":"松下","prodCategory":"小家电"}]

可以看到查询的结果和最初插入的数据是吻合的。

根据商品分类查询数据

执行testFindByProdCategory,console输出为


商品分类查询数据: [{"id":24,"prodName":"手机","prodBrand":"vivo","prodCategory":"数码电子"},{"id":25,"prodName":"手机","prodBrand":"oppo","prodCategory":"数码电子"},{"id":26,"prodName":"手机","prodBrand":"魅族","prodCategory":"数码电子"},{"id":29,"prodName":"手机","prodBrand":"金立","prodCategory":"数码电子"},{"id":33,"prodName":"照相机","prodBrand":"飞利浦","prodCategory":"数码电子"},{"id":32,"prodName":"摄像机","prodBrand":"飞利浦","prodCategory":"数码电子"},{"id":34,"prodName":"mp3","prodBrand":"飞利浦","prodCategory":"数码电子"},{"id":30,"prodName":"手机","prodBrand":"酷派","prodCategory":"数码电子"},{"id":27,"prodName":"手机","prodBrand":"小米","prodCategory":"数码电子"},{"id":35,"prodName":"手环","prodBrand":"飞利浦","prodCategory":"数码电子"},{"id":36,"prodName":"iwatch","prodBrand":"飞利浦","prodCategory":"数码电子"},{"id":28,"prodName":"手机","prodBrand":"摩托罗拉","prodCategory":"数码电子"},{"id":23,"prodName":"手机","prodBrand":"华为","prodCategory":"数码电子"},{"id":31,"prodName":"手机","prodBrand":"格力","prodCategory":"数码电子"}]

可以看到“数码电子”这个分类下的产品数据都被查询出来。

根据商品id查询

执行testFindByProdId,console输出


商品id查询数据: {"id":1,"prodName":"电吹风","prodBrand":"飞利浦","prodCategory":"小家电"}

可以看到id为1的产品数据被查出来

根据商品id删除

执行testDeleteByProdId,console无输出,再执行上面的testFindByProdId,发现id为1的商品不存在了。再执行testFindAll方法,console输出


全部数据总数为: 36,最初插入总数为37

删除全部数据

执行deleteAllData,console无输出。再执行testFindAll方法,console输出:


全部数据总数为: 0,最初插入总数为37

此时浏览器再次访问http://127.0.0.1:9200/iengchen/_mapping 查看ES索引库中的数据:


{

 "iengchen": {

 "mappings": {

 "product": {

 "properties": {

 "id": {

 "type": "long"

 },

 "prodBrand": {

 "type": "string"

 },

 "prodCategory": {

 "type": "string"

 },

 "prodName": {

 "type": "string"

 }

 }

 }

 }

 }

}

可以看到全部的索引数据还在,可见删除数据并不会删除索引。

附件

案例代码

本案例中的代码已经托管到github,具体请移步springboot-springDataElasticsearch-1-demo

参考资料

相关文章

网友评论

      本文标题:SpringData整合Elasticsearch案例

      本文链接:https://www.haomeiwen.com/subject/gwkclftx.html