Spring Boot - 数据库集成06 - 集成ElasticSearch
Spring boot 集成 ElasticSearch
文章目录
- Spring boot 集成 ElasticSearch
- 一:前置工作
- 1:项目搭建和依赖导入
- 2:客户端连接相关构建
- 3:实体类相关注解配置说明
- 二:客户端client相关操作说明
- 1:检索流程
- 1.1:构建请求request和源source
- 1.2:查询建造器queryBuilder构建
- 1.3:请求建造器加入到请求源,请求源加入到请求中
- 1.4:结果hits和highlight分析
- 2:各种查询构造器说明
- 2.1:MatchQueryBuilder
- 2.2:RegexQueryBuilder
- 2.3:IdsQueryBuilder
- 2.4:MatchPhraseQueryBuilder
- 2.5:MatchPhrasePrefixQueryBuilder
- 2.6:MultiMatchQueryBuilder
- 2.7:TermQueryBuilder
- 2.8:FuzzyQueryBuilder
- 2.9:RangeQueryBuilder
- 2.10:WildcardQueryBuilder
- 2.11:BoolQueryBuilder
- 2.12:其他的QueryBuilder
- 3:聚合建造器说明
- 3.1:构造聚合条件
- 3.2:将聚合条件交给builder,builder交给源,封装request
- 3.3:结果解析和处理
- 三:repo & template
- 1:ElasticsearchRepository
- 1.1:repo介绍
- 1.2:自定义方法命名规范
- 2:ElasticsearchRestTemplate
- 2.1:template介绍
- 2.2:template查询操作
- 2.2.1:入参特殊情况处理【包括异常入参】
- 2.2.2:构建查询条件Criteria
- 2.2.3:构建高亮条件
- 2.2.4:查询(也可能是其他的方法,不一定是search)
- 2.2.5:结果封装
- 2.3:创建索引和删除索引
- 3:使用实例

Elasticsearch是面向文档型数据库,一条数据在这里就是一个文档,用 JSON作为文档序列化的格式
{"name" : "John","sex" : "Male","age" : 25,"birthDate": "1990/05/01","about" : "I love to go rock climbing","interests": [ "sports", "music" ]
}
ES和传统的关系型数据库相关术语对比如下:
关系型数据库 | ES |
---|---|
数据库(Database) | 索引(index) |
表(table) | 类型(Type)[es6.0.0废弃] |
行(row) | 文档(document) |
列(column) | 字段(field) |
表结构(schema) | 映射(mapping) |
索引 | 反向索引 |
SQL | 查询DSL |
Select * from table | Get http://… |
update table set… | Put http://… |
delete | Delete http://… |
一:前置工作
1:项目搭建和依赖导入
spring data
->spring-boot-starter-data-elasticsearch
->repo & template
transport & elasticsearch-rest-high-level-client
->client
<dependencies><!-- spring boot启动器 --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- spring boot web启动器 --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- spring boot 测试启动器 --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- commons-lang3工具类 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><!-- spring data elasticsearch --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><!-- es 高阶客户端 --><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId></dependency><!-- es 低阶客户端 --><dependency><groupId>org.elasticsearch.client</groupId><artifactId>transport</artifactId></dependency></dependencies>
spring:elasticsearch:rest:uris: http://localhost:9200,http://localhost:9201,http://localhost:9202 # 集群地址connection-timeout: 5s # 连接超时时间read-timeout: 30s # 读取超时时间max-connections: 100 # 最大连接数max-connections-per-route: 20 # 每个路由的最大连接数password: es_password
2:客户端连接相关构建
package com.cui.es_demo.config;import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 java.util.Arrays;/*** @author cui haida* 2025/1/29*/@Configuration
@ConfigurationProperties(prefix = "spring.elasticsearch.rest")
public class ElasticSearchClientConfig {/*** 集群地址*/private String uris;/*** 用户名和密码*/private String username;private String password;@Beanpublic RestHighLevelClient restHighLevelClient() {// 创建连接, 指定url和用户名密码ClientConfiguration build = ClientConfiguration.builder().connectedTo(uris).withBasicAuth(username, password).build();return RestClients.create(build).rest();}
}
3:实体类相关注解配置说明
package com.cui.es_demo.entity;import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.time.LocalDateTime;/*** 实体类相关注解介绍* - @Document:指定索引库的名称,以及分片数、副本数、刷新间隔、是否允许创建索引* - @Id:指定主键* - @Field:指定字段的名称,以及字段的分词器,以及字段的存储类型,以及字段的分词器* - @JsonFormat:指定日期格式(注意日期类型字段不要用java.util.Date类型,要用java.time.LocalDate或java.time.LocalDateTime类型)* @author cui haida* 2025/1/30*/
@NoArgsConstructor
@AllArgsConstructor
@Data
// 指定对应的索引名称,, 主分片数 = 3, 副本数 = 1, 刷新间隔 = 1s, 允许创建索引
@Document(indexName = "person", shards = 3, replicas = 1, refreshInterval = "1s", createIndex = true)
public class Person {@Idprivate String id;@Field(type = FieldType.Keyword)private String name;@Field(type = FieldType.Keyword)private String age;// text类型,并使用IK最粗粒度的分词器// 检索时的分词器采用的是最细粒度的IK分词器@Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_max_word")private String address;// 注意日期格式的特殊处理// 指定格式化和时区@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private LocalDateTime createTime;
}
二:客户端client相关操作说明
对索引相关的操作都不推荐在这里执行,建议直接使用Kibana
文档的更新和插入操作建议使用repo & template方式
这里只介绍查询相关操作
1:检索流程
1.1:构建请求request和源source
// 声明查询请求对象
SearchRequest client = new SearchRequest();
// 指定使用的索引(数据库)
client.indices(index);// 声明searchSourceBuilder源对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// ======================== 源对象其他常见设置 ===========================
// 1. 排序和浅显分页
// from & size 默认都是-1,也就是有多少显示多少
// from-size浅分页适合数据量不大的情况(官网推荐是数据少于10000条),可以跳码进行查询
searchSourceBuilder.from(0);
searchSourceBuilder.size(9999);// 2. 排序, date、float 等类型添加排序, text类型的字段不允许排序
searchSourceBuilder.sort("age", SortOrder.DESC).sort("name", SortOrder.ASC);
1.2:查询建造器queryBuilder构建
// =============== 以boolean为例 ==================
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("name.keyword", "王五")).mustNot(QueryBuilders.idsQuery().addIds("1", "2", "3"))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "张三"))).boost(2.0f);
// 加入对应的builder到searchSourceBuilder中
searchSourceBuilder.query(boolQueryBuilder);// ============ 如果有高亮设置 ===============
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font style='color: red'>");
highlightBuilder.postTags("</font>");
highlightBuilder.field("name").field("age");
//同一字段中存在多个高亮值 设置都高亮
highlightBuilder.requireFieldMatch(true);// 高亮属性中加入高亮配置器
searchSourceBuilder.highlighter(highlightBuilder);
1.3:请求建造器加入到请求源,请求源加入到请求中
searchSourceBuilder.query(xxxQueryBuilder);
// 封装request,指定要request的索引index,指定source -> searchSourceBuilder
SearchRequest request = new SearchRequest(index).source(searchSourceBuilder);
// 通过client.search获取响应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
1.4:结果hits和highlight分析
List<Map<String, Object>> list = new ArrayList<>();
SearchResponse searchResponse = this.client.search(client, RequestOptions.DEFAULT);
if (searchResponse == null) {log.warn("没有返回值");return;
}// todo: 然后从这个searchResponse解析各种的值 -> 根据返回结构
int failedShards = searchResponse.getFailedShards();
System.out.println("失败的分片数:" + failedShards);
int successfulShards = searchResponse.getSuccessfulShards();
System.out.println("成功的分片数:" + successfulShards);
RestStatus status = searchResponse.status();
System.out.println("状态:" + status);//解析高亮数据
SearchHits hits = searchResponse.getHits();
System.out.println(hits.getMaxScore());
for (SearchHit hit : hits) {System.out.println("fields: " + hit.getFields());System.out.println("index is:" + hit.getIndex());System.out.println("document field is: " + hit.getDocumentFields());System.out.println("metadata field is: " + hit.getMetadataFields());System.out.println("score is: " + hit.getScore());System.out.println("-----------------");//原始数据,不包含高亮的数据Map<String, Object> sourceMap = hit.getSourceAsMap();//高亮数据,拿到高亮字段nameMap<String, HighlightField> highlightFields = hit.getHighlightFields();HighlightField highlightTitle = highlightFields.get("name");//将原始数据的name替换if (highlightTitle != null) {Text[] fragments = highlightTitle.getFragments();if (fragments != null && fragments.length > 0) {sourceMap.replace("name", fragments[0].toString());}}list.add(sourceMap);//循环将数据添加入列表
}
list.forEach(System.out::println);
package com.cui.es_demo.client;import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** 客户端相关操作** @author cui haida* 2025/1/30*/
@Service
@Slf4j
public class ClientDemo {// private final RestHighLevelClient client;public ClientDemo(RestHighLevelClient restHighLevelClient) {this.client = restHighLevelClient;}/*** 主要介绍客户端查询操作*/public void searchTest(String[] args) {// 声明查询请求,同时声明作用的索引(数据库)SearchRequest request = new SearchRequest();request.indices("person");// 声明searchSourceBuilder源对象SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// ======================== 源对象其他常见设置 ===========================// 1. 排序和浅显分页// from & size 默认都是-1,也就是有多少显示多少// from-size浅分页适合数据量不大的情况(官网推荐是数据少于10000条),可以跳码进行查询searchSourceBuilder.from(0);searchSourceBuilder.size(9999);// 2. 排序, date、float 等类型添加排序, text类型的字段不允许排序searchSourceBuilder.sort("age", SortOrder.DESC).sort("name", SortOrder.ASC);// 构造查询建造器,根据不同的业务需求,进行不同的查询// =============== 以boolean为例 ==================BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("name.keyword", "王五")).mustNot(QueryBuilders.idsQuery().addIds("1", "2", "3"))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "张三"))).boost(2.0f);// 请求构造器加入源中searchSourceBuilder.query(boolQueryBuilder);// ============ 如果有高亮设置 ===============HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.preTags("<font style='color: red'>");highlightBuilder.postTags("</font>");highlightBuilder.field("name").field("age");//同一字段中存在多个高亮值 设置都高亮highlightBuilder.requireFieldMatch(true);// 高亮属性中加入源中searchSourceBuilder.highlighter(highlightBuilder);// 进行查询// 1. 设置查询源(source -> request)request.source(searchSourceBuilder);// 2. 通过search方法进行查询List<Map<String, Object>> list = new ArrayList<>();try {SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 结果处理System.out.println("response.getHits().getTotalHits() = " + response.getHits().getTotalHits());// 然后从这个searchResponse解析各种的值 -> 根据返回结构int failedShards = response.getFailedShards();System.out.println("失败的分片数:" + failedShards);int successfulShards = response.getSuccessfulShards();System.out.println("成功的分片数:" + successfulShards);RestStatus status = response.status();System.out.println("状态:" + status);// 解析高亮数据SearchHits hits = response.getHits();System.out.println(hits.getMaxScore());for (SearchHit hit : hits) {System.out.println("fields: " + hit.getFields());System.out.println("index is:" + hit.getIndex());System.out.println("document field is: " + hit.getDocumentFields());System.out.println("metadata field is: " + hit.getMetadataFields());System.out.println("score is: " + hit.getScore());System.out.println("-----------------");// 原始数据,不包含高亮的数据Map<String, Object> sourceMap = hit.getSourceAsMap();// 高亮数据,拿到高亮字段nameMap<String, org.elasticsearch.search.fetch.subphase.highlight.HighlightField> highlightFields = hit.getHighlightFields();HighlightField highlightTitle = highlightFields.get("name");// 将原始数据的name替换if (highlightTitle != null) {Text[] fragments = highlightTitle.getFragments();if (fragments != null && fragments.length > 0) {sourceMap.replace("name", fragments[0].toString());}}// 循环将数据添加入列表list.add(sourceMap);}list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();}}
}
2:各种查询构造器说明
2.1:MatchQueryBuilder
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "李四")// 李和四必须都出现在name字段中才可以, operator可以是OR.operator(Operator.AND)// 匹配度必须是 >= 75%才行.minimumShouldMatch("75%")// 是否忽略数据类型转换异常.lenient(true);
2.2:RegexQueryBuilder
// 张姓开头的doc
RegexpQueryBuilder regexpQueryBuilder = QueryBuilders.regexpQuery("name", "张*").caseInsensitive(true);
2.3:IdsQueryBuilder
String[] ids = new String[]{"1", "2", "3"};
IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery().addIds(ids);
2.4:MatchPhraseQueryBuilder
MatchPhraseQueryBuilder match = QueryBuilders.matchPhraseQuery("name", "李四").slop(2);
2.5:MatchPhrasePrefixQueryBuilder
MatchPhrasePrefixQueryBuilder matchPhrasePrefixQueryBuilder = QueryBuilders.matchPhrasePrefixQuery("name", "张").maxExpansions(1);
2.6:MultiMatchQueryBuilder
String[] fieldNames = new String[]{"name", "age"};
String searchText = "老坛";
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(searchText, fieldNames)// 最多数量匹配.type(MultiMatchQueryBuilder.Type.MOST_FIELDS)// 使用and操作符和minimum_should_match参数来减少相关度低的文档数量.operator(Operator.AND);
2.7:TermQueryBuilder
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name.keyword", "李四");
TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("name.keyword", "李四光", "李四");
2.8:FuzzyQueryBuilder
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("name", "李四");
2.9:RangeQueryBuilder
RangeQueryBuilder ageRangeFilter = QueryBuilders.rangeQuery("age")// greater than 12.gt(12)// less than 17.lt(17);// 默认是true包含头尾,设置false去掉头尾
RangeQueryBuilder ageRangeFilter2 = QueryBuilders.rangeQuery("age").from(12).to(17)// 不包含最后一个元素.includeLower(false)// 包含第一个元素.includeUpper(true);
2.10:WildcardQueryBuilder
// wildcard 通配符查询, 支持*,匹配任何字符序列, 包括空,避免*
String queryString = "Lc*dd";
WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("name", queryString);
2.11:BoolQueryBuilder
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("name.keyword", "王五")).mustNot(QueryBuilders.idsQuery().addIds("1", "2", "3"))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "张三"))).boost(2.0f);
searchSourceBuilder.query(boolQueryBuilder);
2.12:其他的QueryBuilder
// 16: 其他查询
// moreLikeThisQuery: 实现基于内容推荐, 支持实现一句话相似文章查询
// percent_terms_to_match:匹配项(term)的百分比,默认是0.3
// min_term_freq:一篇文档中一个词语至少出现次数,小于这个值的词将被忽略,默认是2
// max_query_terms:一条查询语句中允许最多查询词语的个数,默认是25
// stop_words:设置停止词,匹配时会忽略停止词
// min_doc_freq:一个词语最少在多少篇文档中出现,小于这个值的词会将被忽略,默认是无限制
// max_doc_freq:一个词语最多在多少篇文档中出现,大于这个值的词会将被忽略,默认是无限制
// min_word_len:最小的词语长度,默认是0
// max_word_len:最多的词语长度,默认无限制
// boost_terms:设置词语权重,默认是1
// boost:设置查询权重,默认是1
// analyzer:设置使用的分词器,默认是使用该字段指定的分词器
QueryBuilder queryBuilder = QueryBuilders.moreLikeThisQuery(new String[]{"王"})// 一篇文档中一个词语至少出现次数,小于这个值的词将被忽略,默认是2.minTermFreq(1)// 一条查询语句中允许最多查询词语的个数,默认是25.maxQueryTerms(3);// 查询条件
searchSourceBuilder.query(matchQueryBuilder)// 后置过滤器,id, exists, term, range.postFilter(QueryBuilders.existsQuery("tag"));
3:聚合建造器说明
3.1:构造聚合条件
TermsAggregationBuilder aggregation = AggregationBuilders.terms(等于的值).field(字段名称) // 进行分桶操作.subAggregation(AggregationBuilders.avg(桶的名字,随便起的).field(根据xx字段分桶)) // 对每一个分桶,进行子聚合
3.2:将聚合条件交给builder,builder交给源,封装request
// 声明源
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 将查询建造器放入源,将高亮信息放入源,将聚合信息放入源
searchSourceBuilder.query(boolQueryBuilder);
searchSourceBuilder.highlighter(highlightBuilder);
searchSourceBuilder.aggregation(aggregation);// 将源放入请求request
SearchRequest request = new SearchRequest(index).source(searchSourceBuilder);// 进行查询
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
3.3:结果解析和处理
// 结果解析
List<Object> ans = handleResult(response);
// 结果处理
for (Object o : ans) {System.out.println(o.toString());
}
// todo: 结果解析封装
/*** 聚合查询*/
public void aggTest() {// 声明查询请求,同时声明作用的索引(数据库)SearchRequest request = new SearchRequest();request.indices("person");// 声明searchSourceBuilder源对象SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// ======================== 源对象其他常见设置 ===========================// 1. 排序和浅显分页// from & size 默认都是-1,也就是有多少显示多少// from-size浅分页适合数据量不大的情况(官网推荐是数据少于10000条),可以跳码进行查询searchSourceBuilder.from(0);searchSourceBuilder.size(9999);// 2. 排序, date、float 等类型添加排序, text类型的字段不允许排序searchSourceBuilder.sort("age", SortOrder.DESC).sort("name", SortOrder.ASC);// 构造查询建造器,根据不同的业务需求,进行不同的查询// =============== 以boolean为例 ==================BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("name.keyword", "王五")).mustNot(QueryBuilders.idsQuery().addIds("1", "2", "3"))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "张三"))).boost(2.0f);// 请求构造器加入源中searchSourceBuilder.query(boolQueryBuilder);// ============ 如果有高亮设置 ===============HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.preTags("<font style='color: red'>");highlightBuilder.postTags("</font>");highlightBuilder.field("name").field("age");//同一字段中存在多个高亮值 设置都高亮highlightBuilder.requireFieldMatch(true);// 高亮属性中加入源中searchSourceBuilder.highlighter(highlightBuilder);TermsAggregationBuilder aggregation =// 进行分桶操作AggregationBuilders.terms("Jone").field("name")// 对每一个分桶,进行子聚合.subAggregation(AggregationBuilders.avg("age_avg").field("age"));// 将聚合结果放入源中searchSourceBuilder.aggregation(aggregation);// 进行查询// 1. 设置查询源(source -> request)request.source(searchSourceBuilder);// 2. 通过search方法进行查询List<Map<String, Object>> list = new ArrayList<>();try {SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 结果处理System.out.println("response.getHits().getTotalHits() = " + response.getHits().getTotalHits());// 然后从这个searchResponse解析各种的值 -> 根据返回结构int failedShards = response.getFailedShards();System.out.println("失败的分片数:" + failedShards);int successfulShards = response.getSuccessfulShards();System.out.println("成功的分片数:" + successfulShards);RestStatus status = response.status();System.out.println("状态:" + status);// 解析高亮数据SearchHits hits = response.getHits();System.out.println(hits.getMaxScore());for (SearchHit hit : hits) {System.out.println("fields: " + hit.getFields());System.out.println("index is:" + hit.getIndex());System.out.println("document field is: " + hit.getDocumentFields());System.out.println("metadata field is: " + hit.getMetadataFields());System.out.println("score is: " + hit.getScore());System.out.println("-----------------");// 原始数据,不包含高亮的数据Map<String, Object> sourceMap = hit.getSourceAsMap();// 高亮数据,拿到高亮字段nameMap<String, org.elasticsearch.search.fetch.subphase.highlight.HighlightField> highlightFields = hit.getHighlightFields();HighlightField highlightTitle = highlightFields.get("name");// 将原始数据的name替换if (highlightTitle != null) {Text[] fragments = highlightTitle.getFragments();if (fragments != null && fragments.length > 0) {sourceMap.replace("name", fragments[0].toString());}}// 循环将数据添加入列表list.add(sourceMap);}list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();}
}
三:repo & template
1:ElasticsearchRepository
1.1:repo介绍
ElasticsearchRepository接口封装了Document的CRUD操作,我们直接定义接口继承它即可。
ElasticsearchRepository接口的源码
package org.springframework.data.elasticsearch.repository;import java.io.Serializable;import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.repository.NoRepositoryBean;@NoRepositoryBean
public interface ElasticsearchRepository<T, ID extends Serializable> extends ElasticsearchCrudRepository<T, ID> {<S extends T> S index(S entity);Iterable<T> search(QueryBuilder query);Page<T> search(QueryBuilder query, Pageable pageable);Page<T> search(SearchQuery searchQuery);Page<T> searchSimilar(T entity, String[] fields, Pageable pageable);void refresh();Class<T> getEntityClass();
}
CrudRepository源码
package org.springframework.data.repository;import java.util.Optional;/*** Interface for generic CRUD operations on a repository for a specific type.** @author Oliver Gierke* @author Eberhard Wolff*/
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {// 保存相关<S extends T> S save(S entity);<S extends T> Iterable<S> saveAll(Iterable<S> entities);// 查找相关Optional<T> findById(ID id);boolean existsById(ID id);Iterable<T> findAll();Iterable<T> findAllById(Iterable<ID> ids);// 计数long count();// delete相关void deleteById(ID id);void delete(T entity);void deleteAll(Iterable<? extends T> entities);void deleteAll();
}
PagingAndSortingRepository源码
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends Repository<T, ID> {Iterable<T> findAll(Sort sort);Page<T> findAll(Pageable pageable);
}
可见ElasticsearchRepository为我们封装好了很多常用的方法,我们可以在使用的时候直接调用这些方法进行操作
1.2:自定义方法命名规范
和所有的Spring Data一样,都支持指定格式的函数声明,对于这些函数,不用写具体的实现,而是可以直接调用
SpringData会通过动态代理的方式,帮我们生成基础的CRUD方法
自定义方法命名规范
findBy[fieldName]
:根据指定的单个条件进行等值查询;findBy[fieldName]And[fieldName]And[...]
:根据指定的多条件进行and查询;findBy[fieldName]Or[fieldName]Or[...]
:根据指定的多条件进行or查询;findBy[fieldName]Equals
:根据指定的单个条件进行等值查询;findBy[fieldName]In
:对指定的单个字段进行in查询,入参为一个列表;findBy[fieldName]Like
:对指定的单个字段进行like模糊查询;findBy[fieldName]NotNull
:查询指定字段不为空的数据;findBy[fieldName]GreaterThan
:对指定的单个字段进行]范围查询;findBy[fieldName]GreaterThanEqual
:对指定的单个字段进行]=范围查询;findBy[fieldName]LessThan
:对指定的单个字段进行[范围查询;findBy[fieldName]LessThanEqual
:对指定的单个字段进行[=范围查询;Page[...] findBy[...]
:根据指定的条件进行分页查询;countBy[fieldName]
:根据指定的条件字段进行计数统计;findTop[n]By[fieldName]
:根据指定字段做等值查询,并返回前n条数据;findBy[fieldName]Between
:根据指定字段进行between范围查询;findDistinctBy[fieldName]
:根据指定的单个条件进行去重查询;findFirstBy[fieldName]
:根据指定的单个条件进行等值查询(只返回满足条件的第一个数据);findBy[fieldName1]OrderBy[fieldName2]
:根据第一个字段做等值查询,并根据第二个字段做排序;
各种方法名开头含义
- 以
get、find、read、query、stream
开头,代表是查询数据的方法; - 以
count
开头,代表是计数统计的方法; - 以
delete、remove
开头,代表是删除数据的方法; - 以
exists
开头,代表是判断是否存在的方法; - 以
search
开头,代表是全文搜索的方法; - 以
update
开头,代表是修改数据的方法;
方法名开头后面跟的关键字含义,以find开头的方法为例:
By
:表示当前方法生成的查询语句,会根据By后面的逻辑来组成;FirstBy
:表示当前方法生成的语句,只会返回符合条件的第一条数据;DistinctBy
:表示当前方法生成的语句,会对符合条件的数据去重;TopBy
:表示当前方法生成的语句,只会返回符合条件的前N条数据;[实体类名称]By
:表示当前方法生成的语句,只会返回一条数据;[实体类名称]sBy
:表示当前方法生成的语句,会返回多条数据;AllBy
:表示当前方法生成的语句,会返回多条或所有数据;DistinctFirstBy
:表示当前方法生成的语句,只会返回去重后的第一条数据;DistinctTopBy
:表示当前方法生成的语句,只会返回去重后的前N条数据;
方法名开头跟的关键字之后跟的是字段名[
fieldName
], 字段名称后面可以接的关键字如下
在这些关键字之后,都是跟具体的字段名(实体类的属性名),字段名称后面可以接的关键字如下(同样以find为例):
Or
:表示当前查询方法有多个条件,多个条件之间为“或者”关系;And
:表示当前查询方法有多个条件,多个条件之间为“并且”关系;OrderBy
:表示当前查询会涉及到排序,后面需要跟一个排序字段;Between
:表示当前方法为between范围查询;GreaterThan
:表示当前方法为>查询;GreaterThanEqual
:表示当前方法为>=查询;LessThan
:表示当前方法为 < 查询;LessThanEqual
:表示当前方法为<=查询;After
:和GreaterThan差不多,相当于查询指定数值之后的数据;Before
:和LessThan差不多,查询指定条件之前的数据;Containing
:查询某字段中包含指定字符的数据;Empty
:表示当前方法会查询指定字段为空的数据,与之含义类似的还有Null、Exists;Equals
:表示当前方法会根据指定字段做等值查询;Is
:和Equals差不多;In
:表示当前方法为in多值匹配查询;Like
:表示当前方法为like模糊查询;Not
:可以和上述大多数关键字组合,带有Not的则含义相反,如NotEmpty表示不为空;
2:ElasticsearchRestTemplate
2.1:template介绍
template提供了众多模板方法,只要我们编写好对应的条件,然后调用模板方法即可
2.2:template查询操作
下面是操作流程【查询为例】
2.2.1:入参特殊情况处理【包括异常入参】
2.2.2:构建查询条件Criteria
// 1:构建查询条件
Criteria criteria = new Criteria()// 条件一:xxxx.and(new Criteria("字段名称").contains(条件中的内容))// 条件二:xxx.and(new Criteria("字段名称").is(条件中的内容));
// 2:封装进入到CriteriaQuery
CriteriaQuery filter = new CriteriaQuery(criteria);
// 3:设置分页信息【可能没有,看业务】
CriteriaQuery criteriaQuery = filter.setPageable(PageRequest.of(pageIndex, pageSize));
2.2.3:构建高亮条件
// 1:声明高亮构造器对象
HighlightBuilder highlightBuilder = new HighlightBuilder();
// todo: 设置高亮领域
// todo: 前置后置标签// 2:构建高亮query
HighlightQuery highlightQuery = new HighlightQuery(highlightBuilder);
// 3:将高亮query封装进criteriaQuery中
criteriaQuery.setHighlightQuery(highlightQuery);
2.2.4:查询(也可能是其他的方法,不一定是search)
elasticsearchRestTemplate.search(查询条件,返回信息的实体.class);
2.2.5:结果封装
// 对结果进行封装
2.3:创建索引和删除索引
对于索引的创建和删除,建议还是直接使用Kibana进行相关的操作,如果要使用template,可以输入对应的索引名称进行创建
template会到实体中找到@Document(indexName = "xxx")
对应的信息,根据这个注解的配置拿到settings对应的信息配置
然后根据实体类标注的字段类型和分析器类型创建对应的mappings对应的信息配置
package com.example.es_demo.pojo;import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.time.LocalDateTime;/*** <p>* 功能描述:实体类* </p>** @author cui haida* @date 2024/01/28/8:20*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Document(indexName = "mytest") // 指定对应的索引名称
public class MyTest {@Id@JSONField(serialize = false)private String id;@Field(type = FieldType.Keyword)private String name;@Field(type = FieldType.Keyword)private String age;// text类型,并使用IK最粗粒度的分词器,检索时的分词器采用的是最细粒度的IK分词器@Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_max_word")private String address;// 注意日期格式的特殊处理@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private LocalDateTime createTime;
}
public String create(String indexName) {IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(IndexCoordinates.of(indexName));if (indexOperations.exists()) {return "索引已存在";}indexOperations.create();return "索引创建成功";
}
public String delete(String indexName) {IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(IndexCoordinates.of(indexName));indexOperations.delete();return "索引删除成功";
}
3:使用实例
package com.cui.es_demo.client;import com.cui.es_demo.client.repo.PersonRepository;
import com.cui.es_demo.entity.PageResponse;
import com.cui.es_demo.entity.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;/*** @author cui haida* 2025/1/30*/
@Service
@Slf4j
public class TemplateDemo {// es repo -> 这里提供了Spring Data的基本方法,可以直接使用@AutowiredPersonRepository personRepository;// template@AutowiredElasticsearchRestTemplate elasticsearchRestTemplate;// =========== repo测试 =============// 就是直接调用对应的方法,只要语法满足上面说的Spring Data语法就可以public void saveAll(List<Person> orders) {personRepository.saveAll(orders);}public void deleteById(Integer id) {personRepository.deleteById(id);}public void updateById(Person person) {personRepository.save(person);}public Person findById(Integer id) {return (Person) personRepository.findById(id).orElse(null);}public PageResponse<Person> findAll(Integer pageIndex, Integer pageSize) {Page<Person> page = personRepository.findAll(PageRequest.of(pageIndex, pageSize));PageResponse<Person> pageResponse = new PageResponse<Person>();pageResponse.setTotal(page.getTotalElements());pageResponse.setResult(page.getContent());return pageResponse;}// =============== template测试 ==============// 1:构造查询条件// 2:调用template.xxx(条件)// 3:结果处理(封装)public PageResponse<Person> findList(Person person, Integer pageIndex, Integer pageSize) {// 1:构建查询条件Criteria criteria = new Criteria()// 条件一:文档中的orderDesc字段要包含查询条件的orderDesc字段.and(new Criteria("orderDesc").contains(person.getAddress()))// 条件二:文档中的orderNo字段要等于查询条件的orderNo字段.and(new Criteria("orderNo").is(person.getId()));// 封装进入到CriteriaQueryCriteriaQuery filter = new CriteriaQuery(criteria);// 设置分页信息CriteriaQuery criteriaQuery = filter.setPageable(PageRequest.of(pageIndex, pageSize));// 2:查询 -> elasticsearchRestTemplate.search(查询条件,返回信息的实体.class)SearchHits<Person> searchHits = elasticsearchRestTemplate.search(criteriaQuery, Person.class);// 3:结果封装List<Person> result = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());PageResponse<Person> pageResponse = new PageResponse<>();pageResponse.setTotal(searchHits.getTotalHits());pageResponse.setResult(result);return pageResponse;}public PageResponse<Person> findHighlight(Person person, Integer pageIndex, Integer pageSize) {// 0:特殊情况的处理if (person == null) {PageResponse<Person> pageResponse = new PageResponse<Person>();pageResponse.setTotal(0L);pageResponse.setResult(new ArrayList<>());return pageResponse;}// 1:构建查询条件Criteria criteria = new Criteria()// 条件一:文档中的orderDesc字段要包含查询条件的orderDesc字段.and(new Criteria("id").contains(person.getId()))// 条件二:文档中的orderNo字段要等于查询条件的orderNo字段.and(new Criteria("address").is(person.getAddress()));// 封装进入到CriteriaQueryCriteriaQuery filter = new CriteriaQuery(criteria);// 设置分页信息CriteriaQuery criteriaQuery = filter.setPageable(PageRequest.of(pageIndex, pageSize));// 3:查询 -> elasticsearchRestTemplate.search(查询条件,返回信息的实体.class)SearchHits<Person> searchHits = elasticsearchRestTemplate.search(criteriaQuery, Person.class);// 4:结果封装// 对每一个都设置对应的高亮字段信息List<Person> result = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());// 封装页码信息PageResponse<Person> pageResponse = new PageResponse<>();pageResponse.setTotal(searchHits.getTotalHits());pageResponse.setResult(result);return pageResponse;}
}
相关文章:
Spring Boot - 数据库集成06 - 集成ElasticSearch
Spring boot 集成 ElasticSearch 文章目录 Spring boot 集成 ElasticSearch一:前置工作1:项目搭建和依赖导入2:客户端连接相关构建3:实体类相关注解配置说明 二:客户端client相关操作说明1:检索流程1.1&…...
虚幻UE5手机安卓Android Studio开发设置2025
一、下载Android Studio历史版本 步骤1:虚幻4.27、5.0、5.1、5.2官方要求Andrd Studio 4.0版本; 5.3、5.4、5.5官方要求的版本为Android Studio Flamingo | 2022.2.1 Patch 2 May 24, 2023 虚幻官网查看对应Andrd Studiob下载版本: https:/…...
Flutter开发环境配置
下载 Flutter SDK 下载地址:https://docs.flutter.cn/get-started/install M1/M2芯片选择带arm64字样的Flutter SDK。 解压 cd /Applications unzip ~/Downloads/flutter_macos_arm64_3.27.3-stable.zip执行 /Applications/flutter/bin/flutterManage your Flut…...
[免费]微信小程序智能商城系统(uniapp+Springboot后端+vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序智能商城系统(uniappSpringboot后端vue管理端),分享下哈。 项目视频演示 【免费】微信小程序智能商城系统(uniappSpringboot后端vue管理端) Java毕业设计_哔哩哔哩_bilibili 项目介绍…...
rust操作pgsql、mysql和sqlite
rust中,有很多技术可以操作pgsql、mysql和sqlite,以sqlx为主流技术。我们以sqlx操作sqlite为示例,操作pgsql和mysql的办法是一样的。 Cargo.toml: [package] name "test" version "0.1.0" edition "2021"…...
5. 【Vue实战--孢子记账--Web 版开发】-- 主页UI
我们在实现个人中心的时候简单的搭建了一个主页UI,但是这个主页并不是我们需要的,在这一节我们将一起实现主页UI的搭建。 一、功能 主页UI的原型如下: 首页UI原型包括左侧菜单和顶部header,左侧菜单包含多个功能模块的链接:首页…...
RabbitMQ5-死信队列
目录 死信的概念 死信的来源 死信实战 死信之TTl 死信之最大长度 死信之消息被拒 死信的概念 死信,顾名思义就是无法被消费的消息,一般来说,producer 将消息投递到 broker 或直接到queue 里了,consumer 从 queue 取出消息进…...
使用 PyTorch 实现逻辑回归并评估模型性能
1. 逻辑回归简介 逻辑回归是一种用于解决二分类问题的算法。它通过一个逻辑函数(Sigmoid 函数)将线性回归的输出映射到 [0, 1] 区间内,从而将问题转化为概率预测问题。如果预测概率大于 0.5,则将样本分类为正类;否则分…...
Rust 函数使用详解
Rust 函数使用详解 函数是 Rust 程序的基本构建块之一。通过函数,我们可以将代码组织成可重用的模块。本文将从函数签名语法、函数参数、语句与表达式、函数返回值等角度详细介绍 Rust 函数的使用,并通过综合示例展示这些知识点的实际应用。 1. 函数签名…...
ARM TEE
在ARM的语境中,TEE是Trusted Execution Environment(可信执行环境)的缩写。ARM TEE就是基于ARM架构实现的可信执行环境,以下是具体介绍: 定义与原理 定义:ARM TEE是基于独立硬件,和主操作系统…...
Miniconda 安装及使用
文章目录 前言1、Miniconda 简介2、Linux 环境说明2.1、安装2.2、配置2.3、常用命令2.4、常见问题及解决方案 前言 在 Python 中,“环境管理”是一个非常重要的概念,它主要是指对 Python 解释器及其相关依赖库进行管理和隔离,以确保开发环境…...
解析 Oracle 中的 ALL_SYNONYMS 和 ALL_VIEWS 视图:查找同义词与视图的基础操作
目录 前言1. ALL_SYNONYMS 视图2. ALL_VIEWS 视图3. 扩展 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 1. ALL_SYNONYMS 视图 在 Oracle 数据库中,同义词(Synonym)是对数…...
C#面向对象(封装)
1.什么是封装? C# 封装 封装 被定义为“把一个或多个项目封闭在一个物理的或者逻辑的包中”。 在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。 抽象和封装是面向对象程序设计的相关特性。 抽象允许相关信息可视化,封装则使开发者实现所…...
【深度分析】DeepSeek大模型技术解析:从架构到应用的全面探索
深度与创新:AI领域的革新者 DeepSeek,这个由幻方量化创立的人工智能公司推出的一系列AI模型,不仅在技术架构上展现出了前所未有的突破,更在应用领域中开启了无限可能的大门。从其混合专家架构(MoE)到多头潜…...
【新春特辑】2025年1月科技浪潮中的AI最新时事与科技趋势
2025年1月科技浪潮中的AI最新时事与科技趋势 一、AI科技时事 人工智能代理(AI Agent)的发展 最新进展:人工智能代理正逐步成为科技领域的新热点。这些代理能够自主执行特定任务,如管理日程、回复邮件等。然而,它们仍…...
使用PyTorch实现逻辑回归:从训练到模型保存与性能评估
1. 引入必要的库 首先,需要引入必要的库。PyTorch用于构建和训练模型,pandas和numpy用于数据处理,scikit-learn用于计算性能指标。 import torch import torch.nn as nn import torch.optim as optim import pandas as pd import numpy as …...
【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.24 随机宇宙:生成现实世界数据的艺术
1.24 随机宇宙:生成现实世界数据的艺术 目录 #mermaid-svg-vN1An9qZ6t4JUcGa {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-vN1An9qZ6t4JUcGa .error-icon{fill:#552222;}#mermaid-svg-vN1An9qZ6t4JUc…...
C#面试常考随笔8:using关键字有哪些用法?
1. using 指令:引入命名空间 最常用的用法。通过using 命名空间名字,可以在程序中直接使用该命名空间中的类型,而无需指定类型的完整命名空间路径。例如: using System; using System.Collections.Generic; class Program {sta…...
lstm代码解析1.2
在使用 LSTM(长短期记忆网络)进行训练时,model.fit 方法的输入数据 X 和目标数据 y 的形状要求是不同的。具体来说: 1. 输入数据 X 的形状 LSTM 层期望输入数据 X 是三维张量,形状为 (samples, timesteps, features)…...
JavaScript系列(52)--编译优化技术详解
JavaScript编译优化技术详解 🚀 今天,让我们深入探讨JavaScript的编译优化技术。通过理解和应用这些技术,我们可以显著提升JavaScript代码的执行效率。 编译优化基础概念 🌟 💡 小知识:JavaScript引擎通常…...
【Python】第七弹---Python基础进阶:深入字典操作与文件处理技巧
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【MySQL】【Python】 目录 1、字典 1.1、字典是什么 1.2、创建字典 1.3、查找 key 1.4、新增/修改元素 1.5、删除元素 1.6、遍历…...
【狂热算法篇】探秘图论之Dijkstra 算法:穿越图的迷宫的最短路径力量(通俗易懂版)
羑悻的小杀马特.-CSDN博客羑悻的小杀马特.擅长C/C题海汇总,AI学习,c的不归之路,等方面的知识,羑悻的小杀马特.关注算法,c,c语言,青少年编程领域.https://blog.csdn.net/2401_82648291?typebbshttps://blog.csdn.net/2401_82648291?typebbshttps://blog.csdn.net/2401_8264829…...
Electricity Market Optimization 探索系列(一)
本文参考链接:Linear Programming Mini Example 先从一个线性规划的例子说起: 问题背景: 现在需要使用两台发电机满足用户的用电需求,发电机一的发电功率上限是 6MW,发电机二的发电功率上限是 4MW,发电…...
<iframe>标签和定时调用函数setInterval
iframe 标签和定时调用函数 setInterval 问题描述:解决方法: 问题描述: 今天遇到一个前端问题,在浏览器页面上传Excel文件后,然后点击导入按钮,经后端Java类读取文件内容校验后,将校验结果返回…...
网工_HDLC协议
2025.01.25:网工老姜学习笔记 第9节 HDLC协议 9.1 HDLC高级数据链路控制9.2 HDLC帧格式(*控制字段)9.2.1 信息帧(承载用户数据,0开头)9.2.2 监督帧(帮助信息可靠传输,10开头…...
Elasticsearch:如何搜索含有复合词的语言
作者:来自 Elastic Peter Straer 复合词在文本分析和标记过程中给搜索引擎带来挑战,因为它们会掩盖词语成分之间的有意义的联系。连字分解器标记过滤器等工具可以通过解构复合词来帮助解决这些问题。 德语以其长复合词而闻名:Rindfleischetik…...
【Go语言圣经】第六节:方法
第六章:方法 6.1 方法声明 在函数声明时,在其名字之前放上一个变量,这就是声明了变量对应类型的一个方法,相当于为这种类型定义了一个独占的方法。 下例为 Point 类型声明了计算两个点之间距离的方法: package mai…...
[答疑]DDD伪创新哪有资格和仿制药比
DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 远航 2025-1-24 10:40 最近的热门话题仿制药,想到您经常批评的伪创新,这两者是不是很像? UMLChina潘加宇 伪创新哪有资格和仿制药比。 仿制药的…...
MySQL(高级特性篇) 13 章——事务基础知识
一、数据库事务概述 事务是数据库区别于文件系统的重要特性之一 (1)存储引擎支持情况 SHOW ENGINES命令来查看当前MySQL支持的存储引擎都有哪些,以及这些存储引擎是否支持事务能看出在MySQL中,只有InnoDB是支持事务的 &#x…...
javascript常用函数大全
javascript函数一共可分为五类: •常规函数 •数组函数 •日期函数 •数学函数 •字符串函数 1.常规函数 javascript常规函数包括以下9个函数: (1)alert函数:显示一个警告对话框,包括一个OK按钮。 (2)confirm函数:显…...
第26节课:内容安全策略(CSP)—构建安全网页的防御盾
目录 CSP基础CSP的作用CSP的主要属性 配置CSP通过响应头配置CSP通过HTML <meta>标签配置CSP属性设置详解指定多个来源 配置示例说明 常见错误配置实践:CSP与XSS防护示例1:防止内联脚本和样式说明示例2:限制图片来源说明 限制与注意事项…...
【大坑】使用element-ui弹窗$confirm自动弹出
插入element-ui的弹窗后页面一刷新自动弹出,事件绑定、调用位置(生命周期)均没有问题,通过不断注释组件发现是main.js全局引入导致的问题。如果需要在某些组件中使用三方弹窗,可以按需引入,而不是全局注册 …...
Spring的AOP思想中事物管理注意点
我们以事务管理实现AOP思想 通过在Service层加入事务管理,因为Service层可能使用多个DAO(多条SQL语句) 要保证这些SQL要么同时成功,要么同时失败,例如:学生Serivce:删除学生的时候,还需要删除学生关联信息(选课信息) 只有都删除成功才提交,如果有一条执行失败…...
PHP Mail:高效邮件发送解决方案详解
PHP Mail:高效邮件发送解决方案详解 引言 在互联网时代,邮件作为最常用的沟通方式之一,已经成为企业和个人不可或缺的通讯工具。PHP作为一种流行的服务器端脚本语言,在邮件发送方面具有天然的优势。本文将详细介绍PHP Mail&…...
python recv的概念和使用案例
recv 是网络编程中用于从套接字接收数据的核心函数,常见于 TCP/UDP 通信。以下是其概念、用法和案例详解: 概念 作用:从已连接(TCP)或已绑定(UDP)的套接字接收数据。参数: bufsize:…...
安卓(android)读取手机通讯录【Android移动开发基础案例教程(第2版)黑马程序员】
一、实验目的(如果代码有错漏,可在代码地址查看) 1.熟悉内容提供者(Content Provider)的概念和作用。 2.掌握内容提供者的创建和使用方法。 4.掌握内容URI的结构和用途。 二、实验条件 1.熟悉内容提供者的工作原理。 2.掌握内容提供者访问其…...
Java知识速记 == 与equals
Java知识速记 与equals 1. 操作符概述 操作符用于比较基本数据类型的值,或者比较引用类型的对象是否指向同一内存地址。对于基本数据类型,例如int、float等,会比较其值;但对于对象,只会比较两个对象的引用ÿ…...
web集群
项目名称 基于keepalivednginx构建一个高可用、高性能的web集群 项目架构图 项目描述 基本描述 构建一个基于 Nginx 的 7 层负载均衡的 Web 集群系统,模拟企业级业务环境,实现高并发和高可用性的 Web 集群。通过压力测试验证集群性能,找…...
HTMLCSS :下雪了
这段代码创建了一个动态的雪花飘落加载动画,通过 CSS 技术实现了雪花的下落和消失效果,为页面添加了视觉吸引力和动态感。 大家复制代码时,可能会因格式转换出现错乱,导致样式失效。建议先少量复制代码进行测试,若未能…...
Kafka SSL(TLS)安全协议
文章目录 Kafka SSL(TLS)安全协议1. Kafka SSL 的作用1.1 数据加密1.2 身份认证1.3 数据完整性1.4 防止中间人攻击1.5 确保安全的分布式环境1.6 防止拒绝服务(DoS)攻击 2. Kafka SSL 配置步骤(1)创建 SSL 证…...
WebForms SortedList 深度解析
WebForms SortedList 深度解析 引言 在Web开发领域,对于数据结构的理解与应用至关重要。其中,SortedList类在WebForms中是一个常用的数据结构,它能够帮助开发者高效地管理有序数据集合。本文将深入解析SortedList类在WebForms中的应用,包括其基本概念、常用方法、性能特点…...
项目集成Spring Security认证部分
一、需求分析 在本项目中,使用了Spring Security框架来进行认证和授权管理。由于是前后端分离的项目,所有认证的请求需要通过Token来验证身份,系统中包括了用户登录、角色授权以及资源访问控制等功能。 系统中的资源控制: 白名单…...
【PyTorch】7.自动微分模块:开启神经网络 “进化之门” 的魔法钥匙
目录 1. 梯度基本计算 2. 控制梯度计算 3. 梯度计算注意 4. 小节 个人主页:Icomi 专栏地址:PyTorch入门 在深度学习蓬勃发展的当下,PyTorch 是不可或缺的工具。它作为强大的深度学习框架,为构建和训练神经网络提供了高效且灵活…...
【算法】回溯算法专题① ——子集型回溯 python
目录 引入变形实战演练总结 引入 子集 https://leetcode.cn/problems/subsets/description/ 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 …...
Nginx 安装配置指南
Nginx 安装配置指南 引言 Nginx 是一款高性能的 HTTP 和反向代理服务器,同时也可以作为 IMAP/POP3/SMTP 代理服务器。由于其稳定性、丰富的功能集以及低资源消耗而被广泛应用于各种场景。本文将为您详细介绍 Nginx 的安装与配置过程。 系统要求 在安装 Nginx 之…...
深度学习 DAY3:NLP发展史
NLP发展史 NLP发展脉络简要梳理如下: (远古模型,上图没有但也可以算NLP) 1940 - BOW(无序统计模型) 1950 - n-gram(基于词序的模型) (近代模型) 2001 - Neural language models&am…...
Spring Data JPA 实战:构建高性能数据访问层
1 简介 1.1 Spring Data JPA 概述 1.1.1 什么是 Spring Data JPA? Spring Data JPA 是 Spring Data 项目的一部分,旨在简化对基于 JPA 的数据库访问操作。它通过提供一致的编程模型和接口,使得开发者可以更轻松地与关系型数据库进行交互,同时减少了样板代码的编写。Spri…...
全程Kali linux---CTFshow misc入门(25-37)
第二十五题: 提示:flag在图片下面。 直接检查CRC,检测到错误,就直接暴力破解。 暴力破解CRC的python代码。 import binascii import struct def brute_force_ihdr_crc(filename): # 读取文件二进制数据 with open(filen…...
【Elasticsearch】match_bool_prefix 查询 vs match_phrase_prefix 查询
Match Bool Prefix Query vs. Match Phrase Prefix Query 在 Elasticsearch 中,match_bool_prefix 查询和 match_phrase_prefix 查询虽然都支持前缀匹配,但它们的行为和用途有所不同。以下是它们之间的主要区别: 1. match_bool_prefix 查询…...
被裁与人生的意义--春节随想
还有两个月就要被迫离开工作了十多年的公司了,不过有幸安安稳稳的过了一个春节,很知足! 我是最后一批要离开的,一百多号同事都没“活到”蛇年。看着一批批仁人志士被“秋后斩首”,马上轮到我们十来个,个中滋味很难言清…...