Spring Boot + ElasticSearch实战之CRUD及多数据源配置

news2024/11/25 16:23:44

概述

本文记录工作中使用Spring Boot + ElasticSearch的实战,Spring Boot版本:2.1.6.RELEASE。

基础

Spring Boot已是Java开发标配,使用SB提供的starter,简单高效。

配置

引入依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

增加配置文件:

spring:
  data:
    elasticsearch:
      cluster-nodes: 100.200.33.203:9300,100.200.33.204:9300,100.200.33.205:9300
      cluster-name: elasticsearch6
      repositories:
        enabled: true

定义实体类:

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;

@Data
@Document(indexName="symptom-inquiry", type="symptom", createIndex = false)
public class SymptomEntity {
    /**
     * 根据key由指定hash算法生成
     */
    @Id
    private Long id;
    private String key;
    // 省略其他字段定义
}

主键Id生成算法util工具类:

import com.google.common.base.Charsets;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;

@Slf4j
public class HashUtils {
    /**
     * 针对 SymptomEntity key 计算hash值
     */
    public static Long getHashCode(String key) {
        if (StringUtils.isEmpty(key)) {
            return 0L;
        }
        /*
         * 使用 MURMUR3_128 hash算法计算hash值
         */
        HashFunction function = Hashing.murmur3_128();
        Hasher hasher = function.newHasher();
        HashCode code = hasher.putString(key, Charsets.UTF_8).hash();
        log.info("目标 key = {}, 生成ID = {}", key, code.asLong());
        return code.asLong();
    }
}

基于Repository提供一个空实现的查询接口类:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import java.util.Optional;

@Configuration
public interface SymptomSearchRepository extends ElasticsearchRepository<SymptomEntity, Long> {
}

根据业务需要,上述接口里可以新增自定义查询方法,满足命名规范即可。如,findByFieldname,Filedname是SymptomEntity实体类里面定义的字段,也是ES文档的字段:

    Page<SymptomEntity> findByKey(String key, Pageable pageable);
    Optional<SymptomEntity> findByDescription(String description);

使用:

@Resource
private SymptomSearchRepository symptomSearchRepository;
@Test
public void test() {
	// 基础使用
    symptomSearchRepository.saveAll();
    symptomSearchRepository.save();
    symptomSearchRepository.findAll();
    symptomSearchRepository.findById(1L);

	// 进阶使用, 拼接组装ES SQL
	BoolQueryBuilder allBoolQueryBuilder = QueryBuilders.boolQuery();
	BoolQueryBuilder bodyTagBoolQueryBuilder = this.setBodyTagBoolQueryBuilder(Sets.newHashSet("手", "胳膊"));
	BoolQueryBuilder sexBoolQueryBuilder = this.setSexBoolQueryBuilder(Sex.MALE);
	allBoolQueryBuilder.should(bodyTagBoolQueryBuilder);
	allBoolQueryBuilder.must(sexBoolQueryBuilder);
    symptomSearchRepository.search(allBoolQueryBuilder);
}

private BoolQueryBuilder setSexBoolQueryBuilder(Sex sex) {
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    String notSex = Sex.getNotSex(sex);
    boolQueryBuilder.mustNot(QueryBuilders.matchPhraseQuery("sex", notSex));
    return boolQueryBuilder;
}

private BoolQueryBuilder setBodyTagBoolQueryBuilder(Set<String> allBodyTags) {
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    /*
     * 部位匹配逻辑 封装 bodyTagBoolQueryBuilder
     */
    for (String bodyTag : allBodyTags) {
        QueryBuilder queryBuilderSearchCondition = QueryBuilders.termQuery("bodyTags", bodyTag);
        boolQueryBuilder.should(queryBuilderSearchCondition);
    }
    return boolQueryBuilder;
}

上述API封装得到的ES SQL如下:

{
  "bool" : {
    "must" : [
      {
        "bool" : {
          "must_not" : [
            {
              "match_phrase" : {
                "sex" : {
                  "query" : "FEMALE",
                  "slop" : 0,
                  "zero_terms_query" : "NONE",
                  "boost" : 1.0
                }
              }
            }
          ],
          "adjust_pure_negative" : true,
          "boost" : 1.0
        }
      }
    ],
    "should" : [
      {
        "bool" : {
          "should" : [
            {
              "term" : {
                "bodyTags" : {
                  "value" : "手",
                  "boost" : 1.0
                }
              }
            },
            {
              "term" : {
                "bodyTags" : {
                  "value" : "胳膊",
                  "boost" : 1.0
                }
              }
            }
          ],
          "adjust_pure_negative" : true,
          "boost" : 1.0
        }
      }
    ],
    "adjust_pure_negative" : true,
    "boost" : 1.0
  }
}

kibana + ES 使用

Dev-Tools

上面拼接的SQL,可以在ES可视化平台,执行并查看数据。

ES可视化客户端工具,首选一般就是Kibana。Dev-Tools,提供自动补全提示功能,支持手写SQL语法,适合有要求的开发者。
在这里插入图片描述
常用的语法:

  • 查询
POST symptom-inquiry/_search
{
  "query": {
    "match": {
      "commonName": "腹痛"
    }
  }
}
  • 删除:以主键Id删除
POST symptom-inquiry/_delete_by_query
{
  "query": {
    "match": {
      "_id": "2193474183642781887"
    }
  }
}
  • 新增
PUT symptom-inquiry-stg/symptom/<_id>
{
  "_id": "111",
  "commonName": "aaa",
  "key": "find:ss"
}

Discover

除了上面的Dev-Tools方式,还提供如下简单点点点操作的Discover
在这里插入图片描述

配置2

除了上面这种使用spring-data-jpa提供的封装好的

数据源配置类的另一种方式:

@Configuration
public class EsConfig {
    @Bean(name = "stgHigh", destroyMethod = "close")
    public RestHighLevelClient stgHigh() {
        final CredentialsProvider provider = new BasicCredentialsProvider();
        provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("", ""));
        RestClientBuilder builder = RestClient.builder(
                new HttpHost("100.199.222.111", 9200),
                new HttpHost("100.199.222.112", 9200),
                new HttpHost("100.199.222.113", 9200));
        builder.setHttpClientConfigCallback(inner -> inner.setDefaultCredentialsProvider(provider));
        return new RestHighLevelClient(builder);
    }
}

使用上面的,测试程序如下:

@Autowired
@Qualifier("stgHigh")
private RestHighLevelClient restHighLevelClient;

@Test
public void test() {
	SearchRequest searchRequest = new SearchRequest("symptom-inquiry");
    searchRequest.source().size(1500);
    SearchResponse stgResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    JSONObject stgJson = (JSONObject) JSON.parse(stgResponse.toString());
    JSONObject stgHits = (JSONObject) stgJson.get("hits");
    Integer total = stgHits.getInteger("total");
    JSONArray stgArray = stgHits.getJSONArray("hits");
    for (int i = 0; i < total; i++) {
        JSONObject item = (JSONObject) stgArray.get(i);
        SymptomEntity stgEntity = JSON.parseObject(item.getString("_source"), SymptomEntity.class);
        if (StringUtils.isEmpty(stgEntity.getKey()) || StringUtils.isEmpty(stgEntity.getCommonName())) {
            log.info("symptomEntity {}", JsonUtil.beanToJson(item));
            continue;
        }
    }
}

多数据配置

如下代码

@Configuration
public class EsConfig {
    @Primary
    @Bean(name = "prod", destroyMethod = "close")
    public RestClient prod() {
        final CredentialsProvider provider = new BasicCredentialsProvider();
        provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("es7", "root"));
        RestClientBuilder clientBuilder = RestClient.builder(
                new HttpHost("170.188.116.203", 9200),
                new HttpHost("170.188.117.204", 9200),
                new HttpHost("170.188.118.205", 9200));
        // 根据需求配置身份验证
        clientBuilder.setHttpClientConfigCallback(builder -> builder.setDefaultCredentialsProvider(provider));
        return clientBuilder.build();
    }

    @Bean(name = "stg", destroyMethod = "close")
    public RestClient stg() {
        final CredentialsProvider provider = new BasicCredentialsProvider();
        provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("es6", "root"));
        RestClientBuilder builder = RestClient.builder(
                new HttpHost("100.119.222.11", 9200),
                new HttpHost("100.119.222.12", 9200),
                new HttpHost("100.119.222.13", 9200));
        builder.setHttpClientConfigCallback(inner -> inner.setDefaultCredentialsProvider(provider));
        return builder.build();
    }
}

使用上面配置好的两个ES数据源,从stgprod两个数据源取数:

@Component
public class queryFromStgAndProdService {
    @Autowired
    @Qualifier("prod")
    private RestClient prodClient;

    @Autowired
    @Qualifier("stg")
    private RestClient stgClient;
}

参考

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/598985.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Rocketmq如何保证消息不丢失

如果想要保证消息不丢失就要知道&#xff0c;消息可能出现丢失得地方。 1.producer发送消息 2.Broker存储消息 3.Consumer消费消息 4.Broker主从切换 下面一共有9个维度可以保证消息不丢失。 目录 维度一&#xff1a;同步发送 维度二.异步发送 维度三.刷盘策略 维度四…

数据库期末复习(2)

关系数据库 图1 上图为思考题1的答案 \d student #查看完整性约束 模式和实例 关系的模式:关系有哪些、关系又什么类型、关系的约束时什么&#xff0c;一般来说关系的模式一般比较稳定&#xff0c;不会随着动态的变化而变化。 关系的实例:关系的实例一般随着变化的次数比较…

体验 InsCode AI,原来 AI 也扛不住互联网黑话

CSDN AI写作助手上线了&#xff01;InsCode AI 创作助手不仅能够帮助用户高效创作文章&#xff0c;而且能够作为对话式AI回答你想知道的问题。成倍提高生产力&#xff01;以下是我的体验分享 一、你平时会使用这类AI工具吗&#xff1f;你对这类型的工具有什么看法&#xff1f;…

RPC(2):RPC简介

1 RFC RFC(Request For Comments) 是由互联网工程任务组(IETF)发布的文件集。文件集中每个文件都有自己唯一编号&#xff0c;例如&#xff1a;rfc1831。目前RFC文件由互联网协会(Internet Society&#xff0c;ISOC)赞助发行。 RPC就收集到了rfc 1831中。可以通过下面网址查看…

微信小程序websocket使用protobuf,发送arraybuffer

❤️砥砺前行&#xff0c;不负余光&#xff0c;永远在路上❤️ 目录 前言一、如何在小程序websocket中使用 Protobuf 发送buffer二、使用过程遇到的坑&#xff08;版本问题&#xff09;1、需要注意下Protobuf版本 使用 protobufjs6.8.6最好&#xff0c;我在使用的时候安装7.多 …

1_7后端优化

后端优化是指将一段时间内相机所有关键帧的位姿、内参、每个点3维坐标作为参数进行优化&#xff0c;得到最优的内、外参&#xff1b;利用的方法主要是Bundle Adjustment。 所谓Bundle Adjustment可以理解为从任意特征点发射出来的几束光线&#xff0c;它们会在几个相机的成像平…

寄存器-汇编复习(2)

通过阅读本文小节内容&#xff0c;可以清楚的明白汇编承接的能力和机器语言&#xff0c;高级语言之间的表达关系。文中虽然讨论16位cpu&#xff0c;最新的64或以后的128理论都一样的&#xff0c;类推就好了。 继续将 通用寄存器-汇编复习(1)_luozhonghua2000的博客-CSDN博客 …

很多人打商标的主意,悄悄埋伏

很多人在打商标的主意&#xff0c;等着抢劫呢 等你的品牌结了果实&#xff0c;然后出手勒索 趣讲大白话&#xff1a;鬼子进村&#xff0c;打枪的不要 【趣讲信息科技183期】 **************************** 有些公司申请几千件商标 有公司一个月申请100件 春江水暖贼先知 有一条…

WSL2网络配置

WSL2越来越好用了&#xff0c;但是在windows下使用clash时&#xff0c;配置WSL2的网络很麻烦&#xff0c;通常使用设置环境变量export ALL_PROXY"http://127.0.0.1:8000"的方式有很大的弊端&#xff0c;例如pip和conda就不会走代理。 经过我亲身体验&#xff0c;最好…

基于小脑模型神经网络的轨迹跟踪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

模拟退火算法(附简单案例及详细matlab源码)

作者&#xff1a;非妃是公主 专栏&#xff1a;《智能优化算法》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录 专栏推荐序一、概论二、物理退火1. 加温…

MyBatis - CRUD 操作

文章目录 1.环境配置1.1 导入相关依赖1.2 基本配置1.3 数据模型 2.基于 XML 开发2.1 创建 Mapper 接口2.2 创建 XML 映射文件2.3 insert2.4 select2.5 delete2.6 update2.7 编写单元测试 3.基于注解开发3.1 常用注解3.2 创建 Mapper 接口 MyBatis 支持通过 XML 和注解两种方式来…

chatgpt赋能python:Python运行程序没反应怎么办?

Python运行程序没反应怎么办&#xff1f; Python作为一种高级编程语言&#xff0c;已经成为了很多开发者的首选语言。然而&#xff0c;在使用Python编写程序时&#xff0c;有时候会出现运行程序却没有任何反应的情况。这是什么原因导致的呢&#xff1f;本文将为大家介绍Python…

单例模式的饿/懒汉模式

目录 1. 什么是单例模式2. 饿汉模式2.1 饿汉模式概念2.2 饿汉模式代码 3. 懒汉模式3.1 懒汉模式概念3.2 单线程情况下的懒汉模式3.3 单例模式的写法(保证线程安全) 4. wait 和 sleep 的区别 1. 什么是单例模式 保证某个类在程序中只存在一份实例&#xff0c;而不会创建多个实例…

Apache Kafka - 跨集群数据镜像 MirrorMaker

文章目录 概述跨集群数据镜像的原理MirrorMaker配置小结 概述 在分布式系统中&#xff0c;数据镜像是一项重要的功能&#xff0c;它可以将数据从一个集群复制到另一个集群&#xff0c;以保证数据的高可用性和容错性。Apache Kafka是一个流处理平台&#xff0c;它提供了一种跨集…

程序设计综合实习(C语言):学生成绩单制作

一、目的 1&#xff0e;掌握结构体变量及数组的定义、赋值、初始化、输入、输出 2&#xff0e;结构体数组的操作。 二、实习环境 Visual Stdio 2022 三、实习内容、步骤与要求 1&#xff0e;定义一个结构体数组&#xff0c;存放10个学生的学号&#xff0c;姓名&#xff0c;三…

Linux 设备树文件手动编译的 shell 脚本

前言 前面通过 Makefile 实现手动编译 Linux 设备树 dts 源文件及其 设备树依赖 dtsi、.h 头文件&#xff0c;如何写成一个 shell 脚本&#xff0c;直接编译呢&#xff1f; 其实就是 把 Makefile 重新编写为 shell 脚本即可 编译设备树 shell 脚本 脚本内容如下&#xff1a…

【六一 iKun】Happy LiuYi, iKuns

六一了&#xff0c;放松下。 Python iKun from turtle import * screensize(1000,1000) speed(6)#把衣服画出来&#xff0c;从右肩膀开始#领子 penup() goto(-141,-179) pensize(3) fillcolor("black") pencolor("black") begin_fill() pendown() left(1)…

【Python实战】Python采集高校信息

前言 大家好,我们今天来爬取某站的高校名单,把其高校名单,成员和内容数获取下来,不过,我们发现这个网站比我们平时多了一个验证,下面看看我是怎么解决的。 环境使用 python 3.9pycharm模块使用 requests模块介绍 requests requests是一个很实用的Python HTTP客户端…

【线性dp必学四道题】线性dp四道经典例题【最长上升子序列】、【最长公共子序列】、【最长公共上升子序列(maxv的由来)】【最长公共子串】

【最长上升子序列】、【最长公共子序列】、【最长公共上升子序列】 最长上升子序列f[i] 表示以i结尾的最长子序列 最长公共子序列f[i][j] 表示 a前i 和 b前j个 最长公共长度 最长公共上升子序列f[i][j]代表所有a[1 ~ i]和b[1 ~ j]中以b[j]结尾的公共上升子序列的集合 最长公共子…