美文网首页
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