Mall脚手架总结(二) —— SpringData操作Elasticsearch

前言

        万字长文带你弄清楚SpringData中的Elasticsearch操作以及在脚手架里接口的结构关系!经过前面鉴证授权的整合,荔枝开始熟悉项目的学习的方法了,虽然脚手架中的内容比较简单,但是把边角的知识点全部扫到还是比较花时间的尤其是对于基础不是特别牢固的小伙伴来说~荔枝也希望这篇文章能对正在学习的小伙伴有帮助~~~


文章目录

前言

一、整合ES实现搜索

1.1 SpringData框架

1.2 ElasticsearchRepository

1.3 分页工具:Pageable、Page 

1.3.1 Page接口

1.3.2 Pageable接口

1.4 函数式接口 

1.5 常用注解

1.6 Elasticsearch实现搜索的流程

 总结


一、整合ES实现搜索

        Elasticsearch实现搜索的功能比较简单,我们需要自定义一个Dao层的接口来自定义Mybatis操作数据库并将需要搜索的数据导入到ES中,同时对于相关的操作ES的操作我们通过一个继承ElasticsearchRepository接口的功能接口比如EsProductRepository来实现,其中有关ElasticsearchRepository的知识需要我们着重去理解。同时我们需要清楚的是在SpringBoot中我们操作Elasticsearch是通过Spring Data框架来实现的。

1.1 SpringData框架

        Spring Data是Spring中的一个子项目,通过官网的介绍我们可以了解到Spring Data是为各种数据访问技术提供一种一致的基于Spring编程的模型,同时也保证数据存储的特殊特性。其中包含了比如JDBC、JPA、MongoDB、Redis、 Elasticsearch等技术在Spring项目中的数据操作。在SpringBoot项目中来操作ES使用SpringData无疑是最好的哈哈哈。

Repository 

        Repository是Spring Data框架中定义的一个泛型接口(标记接口),该接口并不会定义任何方法,我们可以通过定义功能接口继承Repository,则该接口会被IOC容器识别为一个Repository Bean纳入到IOC容器中,进而可以在该接口中定义满足一定规范的方法。 

package org.springframework.data.repository;import org.springframework.stereotype.Indexed;@Indexed
public interface Repository<T, ID> {
}

需要注意的是,Spring Data中给出了该接口的基本的继承接口CrudRepository,该接口定义了基本的操作数据库的CRUD方法,比如save开头的保存数据方法、find用来查询数据的方法等

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.data.repository;import java.util.Optional;@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();void deleteById(ID id);void delete(T entity);void deleteAllById(Iterable<? extends ID> ids);void deleteAll(Iterable<? extends T> entities);void deleteAll();
}

继承的子接口:

  • CrudRepository:继承Repository,实现一组CURD相关的方法
  • PagingAndSortingRespository:继承CrudRepository,实现了一组分页排序相关的方法
  • JpaRepository:继承PagingAndSortingRespository,实现了一组JPA规范相关的方法
  • JpaSpecificationExecutor:不属于Repository体系,实现一组 JPA Criteria 查询相关的方法

1.2 ElasticsearchRepository

        ElasticsearchRepository是Repository的一个曾孙子辈的接口,继承关系自PagingAndSortingRepository接口。在前面我们知道该接口在CrudRepository接口基础上添加了一些分页的操作。回到前面Spring Data定义的要整合数据访问技术的初衷,有关ES的操作也就必须会有一个相应的特殊的接口实现了:ElasticsearchRepository。

下面先看看这张图理清楚不同了接口之间的继承关系:

再来看看源码:

@NoRepositoryBean
public interface ElasticsearchRepository<T, ID> extends PagingAndSortingRepository<T, ID> {Page<T> searchSimilar(T entity, @Nullable String[] fields, Pageable pageable);
}

说一下@NoRepositoryBean,该注解是为了防止Spring Data为其创建实例,官网规定我们在定义Repository的子接口的时候加入该注解,具体深入的原因这里就不赘述,可以看看这篇文章:

https://www.cnblogs.com/logoman/p/11707659.html

searchSimilar是一个一个只能使用ID字段进行模糊查询的方法,具体的细节大家可以看看这篇博文

Elasticsearch中ElasticsearchRepository的searchSimilar使用的坑-CSDN博客

        看了这么多的补充知识,相信大家对大体的SpringData提供的ElasticsearchRepository接口也有了相应的了解,回到脚手架的实现上,这里定义了一个根据名称、标题和关键词来模糊搜索的方法,需要注意的是这里的方法命名语法是有特定的要求的,因为我们需要通过衍生查询来实现ES中的数据操作。

package com.crj.crj_mall_learning.elasticsearch.repository;import com.crj.crj_mall_learning.elasticsearch.document.EsProduct;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;/*** @auther lzddl* @description 商品ES操作类*/
public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {/*** 搜索查询** @param name              商品名称* @param subTitle          商品标题* @param keywords          商品关键字* @param page              分页信息* @return*///该接口交给RS自动生成相应的实现类Page<EsProduct> findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);//再随便写一个根据名字和标题搜索的方法试试Page<EsProduct> findByNameOrSubTitle(String name,String subTitle,Pageable page);
}

IDEA中默认会提示相应的衍生查询的关键字!

衍生查询的关键字

Keyword

Sample

Elasticsearch Query String

And

findByNameAndPrice

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}

Or

findByNameOrPrice

{ "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}

Is

findByName

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}

Not

findByNameNot

{ "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}

Between

findByPriceBetween

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}

LessThan

findByPriceLessThan

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } ] } }}

LessThanEqual

findByPriceLessThanEqual

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}

GreaterThan

findByPriceGreaterThan

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } ] } }}

GreaterThanEqual

findByPriceGreaterThan

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}

Before

findByPriceBefore

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}

After

findByPriceAfter

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}

Like

findByNameLike

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}

StartingWith

findByNameStartingWith

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}

EndingWith

findByNameEndingWith

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}

Contains/Containing

findByNameContaining

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}

In (when annotated as FieldType.Keyword)

findByNameIn(Collectionnames)

{ "query" : { "bool" : { "must" : [ {"bool" : {"must" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}

In

findByNameIn(Collectionnames)

{ "query": {"bool": {"must": [{"query_string":{"query": ""?" "?"", "fields": ["name"]}}]}}}

NotIn (when annotated as FieldType.Keyword)

findByNameNotIn(Collectionnames)

{ "query" : { "bool" : { "must" : [ {"bool" : {"must_not" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}

NotIn

findByNameNotIn(Collectionnames)

{"query": {"bool": {"must": [{"query_string": {"query": "NOT("?" "?")", "fields": ["name"]}}]}}}

True

findByAvailableTrue

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }}

False

findByAvailableFalse

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }}

OrderBy

findByAvailableTrueOrderByNameDesc

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] }

Exists

findByNameExists

{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}

IsNull

findByNameIsNull

{"query":{"bool":{"must_not":[{"exists":{"field":"name"}}]}}}

IsNotNull

findByNameIsNotNull

{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}

IsEmpty

findByNameIsEmpty

{"query":{"bool":{"must":[{"bool":{"must":[{"exists":{"field":"name"}}],"must_not":[{"wildcard":{"name":{"wildcard":"*"}}}]}}]}}}

IsNotEmpty

findByNameIsNotEmpty

{"query":{"bool":{"must":[{"wildcard":{"name":{"wildcard":"*"}}}]}}}

除了这种方式,我们还可以通过@Query注解的方式来使用Elasticsearch原生的查询DSL语句:

public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {@Query("{"bool" : {"must" : {"field" : {"name" : " ? 0"}}}}")Page<EsProduct> findByName(String name, Pageable pageable);
}

1.3 分页工具:Pageable、Page 

1.3.1 Page接口

Page接口表示一个分页查询的结果集,它包含了查询结果的分页信息和数据。Page接口继承自Slice接口,因此也有相应的的分页信息方法。下面我们来看看相应的类图:

1.3.2 Pageable接口

Pageable接口用于表示分页查询的请求信息,它包含了分页查询的相关参数,例如页数、每页记录数、排序方式等。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.data.domain;import java.util.Optional;
import org.springframework.util.Assert;public interface Pageable {static Pageable unpaged() {return Unpaged.INSTANCE;}static Pageable ofSize(int pageSize) {return PageRequest.of(0, pageSize);}default boolean isPaged() {return true;}default boolean isUnpaged() {return !this.isPaged();}//返回请求的页数int getPageNumber();//放回每页包含的记录数int getPageSize();//根据底层页面和页面大小返回偏移量long getOffset();//返回排序信息Sort getSort();default Sort getSortOr(Sort sort) {Assert.notNull(sort, "Fallback Sort must not be null");return this.getSort().isSorted() ? this.getSort() : sort;}Pageable next();//返回前一页的 Pageable 对象,如果当前页已经是第一页则返回第一页。Pageable previousOrFirst();//返回第一页的 Pageable 对象Pageable first();//返回一个新的 Pageable 对象,指定请求的页数。Pageable withPage(int pageNumber);//判断是否有前一页。boolean hasPrevious();default Optional<Pageable> toOptional() {return this.isUnpaged() ? Optional.empty() : Optional.of(this);}
}

这里我们再回顾一下函数式接口的知识哈~

1.4 函数式接口 

在Java8之后为了兼容加进来的lambda表达式,引入了一个函数式接口的概念,函数式接口指的是在接口中只有一个抽象方法。同时需要注意的是,从Java8之后定义接口中是可以存在方法体的,但必须是default默认方法和static静态方法。

二者区别

  • 默认方法允许在接口中提供方法的默认实现,而不需要实现该接口的所有类都去覆盖这个方法,默认方法使得接口的修改更加灵活,因为可以在接口中添加新的方法,而不需要改变现有的实现类;
  • 静态方法可以通过接口的名称直接调用,无需实例化接口的实现类,通常用于提供一些与接口相关的工具方法或辅助方法;

我们可以通过@FunctionalInterface注解来告诉编译器函数式接口的声明,一旦加上该注解,接口中就最多只会有一个抽象方法! 

1.5 常用注解

Spring Data中常用的操作Elasticsearch的注解主要有四个:@Document、@Setting、@Id和@Field。

注解名称作用参数说明

@Document

用于标识映射到Elasticsearch文档上的领域对象

indexName:索引库的名字,MySQL中数据库的概念

@Setting

ES的配置注解

shards:默认分片数
replicas:默认副本数量

@Id

用于标识文档的ID,文档可以认为是MySQL中表行的概念

无参数

@Field

用于标识文档中的字段,可以认为是MySQL中列的概念

type:文档中字段的类型
index:是否建立倒排索引
store:是否进行存储
analyzer:分词器的名称

Field中常用的Type类型

public enum FieldType {Auto("auto"), //自动判断字段类型Text("text"), //会进行分词并建了索引的字符类型Keyword("keyword"), //不会进行分词建立索引的类型Long("long"), //Integer("integer"), //Short("short"), //Byte("byte"), //Double("double"), //Float("float"), //Date("date"), //Boolean("boolean"), //Object("object"), //Nested("nested"), //嵌套对象类型Ip("ip"), //
}

1.6 Elasticsearch实现搜索的流程

在最开始我们其实比较清晰的弄清楚了ES的整合流程,这里荔枝梳理一下一些重要的部分和自己踩的坑。

dao层

dao层其实是比较重要的一环,我们需要将数据库中的数据查询出来,由于商品的信息比较负载,还涉及到嵌套的对象,所以这部分的需求需要自己写一个mapper文件来实现mybatis操作。 

/*** @auther lzddl* @description 搜索系统中的商品管理自定义Dao,根据id来将数据库中的对应商品数据加载到EsProduct对象中*/
public interface EsProductDao {List<EsProduct> getAllEsProductList(@Param("id") Long id);
}

server层的实现类

        这个部分其实是比较重要的,我们可以来看看关系的类图,有关ES的操作其实就在EsProductRepository接口中,有关数据库数据导入的操作就在EsProductDao中,而EsProductService其实就作为control下面直接操作的ES搜索方法。

EsProductServiceImpl 

/*** @auther lzddl* @description 搜索商品管理Service实现类*/
@Service
public class EsProductServiceImpl implements EsProductService {private static final Logger LOGGER = LoggerFactory.getLogger(EsProductServiceImpl.class);@Autowiredprivate EsProductDao productDao;@Autowiredprivate EsProductRepository productRepository;@Overridepublic int importAll() {List<EsProduct> esProductList = productDao.getAllEsProductList(null);Iterable<EsProduct> esProductIterable = productRepository.saveAll(esProductList);Iterator<EsProduct> iterator = esProductIterable.iterator();int result = 0;while (iterator.hasNext()) {result++;iterator.next();}return result;}@Overridepublic void delete(Long id) {productRepository.deleteById(id);}@Overridepublic EsProduct create(Long id) {EsProduct result = null;List<EsProduct> esProductList = productDao.getAllEsProductList(id);if (esProductList.size() > 0) {EsProduct esProduct = esProductList.get(0);result = productRepository.save(esProduct);}return result;}@Overridepublic void delete(List<Long> ids) {if (!CollectionUtils.isEmpty(ids)) {List<EsProduct> esProductList = new ArrayList<>();for (Long id : ids) {EsProduct esProduct = new EsProduct();esProduct.setId(id);esProductList.add(esProduct);}productRepository.deleteAll(esProductList);}}@Overridepublic Page<EsProduct> search(String keyword, Integer pageNum, Integer pageSize) {Pageable pageable = PageRequest.of(pageNum, pageSize);return productRepository.findByNameOrSubTitleOrKeywords(keyword, keyword, keyword, pageable);}}

安装ES踩的坑1:路径不要有空格!  

不然你就会看到这样的报错呜呜呜~~~ 

安装ES踩的坑2:中文分词器的解压路径一定要在plugin/analysis-ik下!


 总结

        看懂脚手架的源码+弄清楚边角知识+梳理文章真的很耗时,学的好慢不过确实有效果哈哈哈,最后也要感谢宏哥和各位大佬博客的帮助!希望荔枝能继续加快脚步给大家输出更有质量的博文,最后一起加油吧~~~

今朝已然成为过去,明日依然向往未来!我是荔枝,在技术成长之路上与您相伴~~~

如果博文对您有帮助的话,可以给荔枝一键三连嘿,您的支持和鼓励是荔枝最大的动力!

如果博文内容有误,也欢迎各位大佬在下方评论区批评指正!!!

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

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

相关文章

C#和JS交互之Microsoft.ClearScript.V8(V8引擎)

之前测试了很多JS引擎&#xff0c;都只支持es5语法&#xff0c;不支持执行es6&#xff0c;测试了下微软的V8反正能跑通&#xff0c;应该是支持的。还得是微软呀。 如图&#xff1a;安装相关包&#xff1a; 这是参考的官方V8代码 using Microsoft.ClearScript.JavaScript; us…

当下测试行业中UI自动化面临的难点及如何解决

经常有人会问&#xff0c;什么样的项目才适合进行UI自动化测试呢&#xff1f;UI自动化测试相当于模拟手工测试&#xff0c;通过程序去操作页面上的控件。而在实际测试过程中&#xff0c;经常会遇到无法找到控件&#xff0c;或者因控件定义变更而带来的维护成本等问题。 哪些场…

jvm--执行引擎

文章目录 1. 执行引擎的工作流程2. 解释器、JIT及时编译器3. 热点代码及探测技术4. HotSpotVM 中 JIT 分类 执行引擎属于 JVM 的下层&#xff0c;里面包括解释器、及时编译器、垃圾回收器 JVM 的主要任务是负责 装载字节码到其内部&#xff0c;但字节码并不能够直接运行在操作…

Zookeeper分布式一致性协议ZAB源码剖析

文章目录 1、ZAB协议介绍2、消息广播 1、ZAB协议介绍 ZAB 协议全称&#xff1a;Zookeeper Atomic Broadcast&#xff08;Zookeeper 原子广播协议&#xff09;。 Zookeeper 是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面&#xff0c;Zookeeper 并…

栈的模拟实现(Java)

目录 1、 栈的概念2、栈的使用3、栈的模拟实现 1、 栈的概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last I…

Linux 部署1Panel 现代化运维管理面板进行公网远程访问

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透2.1 使用一键脚本安装命令 2.2向系统添加服务2.3 启动cpolar服务…

爬虫Python

文章目录 基本数据类型bytes类型python数据类型转换&#xff08;必会&#xff01;&#xff01;&#xff01;&#xff09; python运算符&#xff08;必会&#xff01;&#xff01;&#xff01;&#xff09;python数字数学函数&#xff08;必会&#xff01;&#xff01;&#xff…

uni-app:实现view元素强制换行(解决长字符和英文字符不换行问题)

效果 换行前 换行后 核心代码 word-wrap: break-word; 或 word-break: break-all; 完整代码demo <template><view><view class"all_style"><view class"line1">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</view>…

ubuntu离线编译安装cmake 3.22.5(could not fonud OPENSSL) and cmake-versinon查不到版本问题

1、首先去cmake官网下载压缩包,例如: cmake-3.22.5.tar.gz 2、拉到ubuntu进行解压: tar -zxcf cmake-3.22.5.tar.gz 3、cd 进入目录 cd cmake-3.22.5 4、执行configure可执行文件 ./configure 如果在编译过程中出现报错:Could NOT findOpenSSL,原因可能是缺少ssl库 按…

高压放大器在纳米材料中的应用有哪些

高压放大器是一种重要的电子设备&#xff0c;可以用于增强输入信号的电压。在纳米材料领域&#xff0c;高压放大器也具有广泛的应用。下面西安安泰将介绍高压放大器在纳米材料中的应用&#xff0c;并探讨其可行性和潜在的研究方向。 纳米材料传感器&#xff1a;高压放大器在纳米…

微服务10-Sentinel中的隔离和降级

文章目录 降级和隔离1.Feign整合Sentinel来完成降级1.2总结 2.线程隔离两种实现方式的区别3.线程隔离中的舱壁模式3.2总结 4.熔断降级5.熔断策略&#xff08;根据异常比例或者异常数&#xff09; 回顾 我们的限流——>目的&#xff1a;在并发请求的情况下服务出现故障&…

【踩坑】hive脚本笛卡尔积严重降低查询效率问题

前一阵子查看我们公司的大数据平台的离线脚本运行情况, 结果发现有一个任务居然跑了一天多, 要知道这还只是几千万量级的表, 且这个任务是每天需要执行的 于是我把hive脚本捞出来看了下, 发现无非多join了几个复杂的子查询, 应该不至于这么久, 包括我又检查了是不是没有加上每…

MTK平台闪光灯相关信息

&#xff08;1&#xff09;概念 MTK打闪一般分为预闪、主闪两个阶段。相应的执行一次拍照会有预闪、主闪两次开灯。预闪可以用来防红眼(red-eye reduction)。闪光时使环境发生变化&#xff0c;会按新的光线条件&#xff08;有闪光时&#xff09;重新测光&#xff0c;来实现更精…

python- excel 创建/写入/删sheet+花式遍历

文章目录 前言python- excel 创建/写入/删sheet花式遍历1. excel 创建2. 写入excel3. 创建写入excel demo实战4. 删除sheet5. excel 花式遍历 demo实战5.1. 获取 A1的值5.2. 获取指定列的切片数据&#xff0c;获取 B1到B5的值5.3. 循环整个excel的这个sheet5.4. 遍历指定行&…

从零开始学习:如何使用Selenium和Python进行自动化测试?

安装selenium 打开命令控制符输入&#xff1a;pip install -U selenium 火狐浏览器安装firebug&#xff1a;www.firebug.com&#xff0c;调试所有网站语言&#xff0c;调试功能 Selenium IDE 是嵌入到Firefox 浏览器中的一个插件&#xff0c;实现简单的浏览器操 作的录制与回…

Unity2017适配安卓12

测试版本为Unity2017.4.25f1 1.在自定义AndroidManifest.xml&#xff08;位于Assets\Plugins\Android\&#xff09;中添加android:exported"true" <?xml version"1.0" encoding"utf-8"?> <manifestxmlns:android"http://schema…

探索 Redis 与 MySQL 的双写问题

在日常的应用开发中&#xff0c;我们经常会遇到需要使用多种不同类型的数据库管理系统来满足各种业务需求。其中最典型的就是Redis和MySQL的组合使用。 这两者拥有各自的优点&#xff0c;例如Redis为高性能的内存数据库提供了极快的读写速度&#xff0c;而MySQL则是非常强大的…

选择智慧公厕解决方案,开创智慧城市公共厕所新时代

在城市建设和发展中&#xff0c;公厕作为一个不可或缺的城市基础设施&#xff0c;直接关系到城市形象的提升和居民生活品质的改善。然而&#xff0c;传统的公厕存在着管理不便、卫生状况差、设施陈旧等问题。为了解决这些困扰着城市发展的难题&#xff0c;智慧公厕源头厂家广州…

视频监控系统/视频汇聚平台EasyCVR如何反向代理进行后端保活?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

利用异常实现短期hook

场景1 调用目标call 需要跳过某些判断或者函数 场景2 目标call 只需要部分指令执行 大概实现技术 设置线程上下文设置drX寄存器 实现硬件执行断点 主动调用目标call 通过硬件断点获取寄存器或修改eip 以上实现不改变crc且不通过驱动实现。只对当前执行线程有效&#xff…