• Spring Data简介


    Spring Boot操作 Elasticsearch

    Spring Data简介

    原生状态下,我们使用JDBC连接数据库,因为代码过于繁琐,所以改为使用Mybatis框架

    在ES的原生状态下,我们java代码需要使用socket访问ES,但是也是过于繁琐,我们可以使用SpringData框架简化

    Spring Data是Spring提供的一套连接各种第三方数据源的框架集

    我们需要使用的是其中连接ES的Spring Data Elasticseatrch

    官方网站:Spring Data

     

    官网中列出了它可以操作的数据源列表

    每个列表中都包含一些使用的介绍

    要想实现Spring Boot操作ES添加依赖后,按照要求编写代码即可

     

    添加依赖

    在上面章节中,我们创建的search模块中添加以下依赖

    1. <!-- Spring Data Elasticsearch依赖 -->
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    5. </dependency>

    application.properties添加配置

    1. # 设置连接ES的ip地址和端口号
    2. spring.elasticsearch.rest.uris=http://localhost:9200
    3. # 为了观察运行状态信息,将日志输出门槛设置为debug
    4. logging.level.xx.xx.search=debug
    5. logging.level.org.elasticsearch.client.RestClient=debug

    演示

    创建一个操作ES的数据类

    和数据库一样

    操作ES时也需要一个类似实体类的数据类,作为操作ES的数据载体

    search项目创建entity包

    在包中创建Item(商品)类

    1. @Data
    2. @Accessors(chain = true) // 生成和链式赋值的set方法
    3. @AllArgsConstructor // 自动生成包含全部参数的构造方法
    4. @NoArgsConstructor // 自动生成无参构造方法
    5. // SpringData要求我们在"实体类"中使用特定注解标记
    6. // @Document注解标记当前类和ES关联
    7. // indexName指定索引名称,我们这里叫items,当操作这个索引时,如果索引不存在,会自动创建
    8. @Document(indexName = "items")
    9. public class Item implements Serializable {
    10. // SpingData标记这个字段为当前类主键
    11. @Id
    12. private Long id;
    13. // SpringData使用@Field标记文档中属性的类型和各种特征
    14. @Field(type = FieldType.Text,
    15. analyzer = "ik_max_word",
    16. searchAnalyzer = "ik_max_word")
    17. private String title; //商品名称
    18. @Field(type = FieldType.Keyword)
    19. private String category; //分类
    20. @Field(type = FieldType.Keyword)
    21. private String brand; //品牌
    22. @Field(type = FieldType.Double)
    23. private Double price; //价格
    24. // 图片地址不会称为搜索条件,所以设置index=false
    25. // 效果是imgPath字段不会生成索引库,节省空间
    26. @Field(type = FieldType.Keyword,index = false)
    27. private String imgPath; //图片地址
    28. // images/hjdsf-ahsa-qwezx-jashjdas.png
    29. // Text和Keyword都是字符串类型,只是Text会分词,而Keyword不会!
    30. }

    创建操作ES的持久层

    我们使用SpringData连接操作ES

    需要使用SpringData框架对持久层的命名规则

    创建repository包,在包中创建接口ItemRepository

    代码如下

    1. // Spring 家族下持久层名称都叫repository
    2. @Repository
    3. public interface ItemRepository extends ElasticsearchRepository<Item,Long> {
    4. // 当前接口继承ElasticsearchRepository父接口后
    5. // 会自动在类中生成基本的增删改查方法,直接可以使用
    6. // 它自动识别或自动生成的规则,是我们定义的两个泛型ElasticsearchRepository<[实体类名],[主键类型]>
    7. }

    测试操作ES

    打开test测试类进行测试

    1. @SpringBootTest
    2. class SearchApplicationTests {
    3. // 注入SpringData操作Es的持久层对象
    4. @Autowired
    5. private ItemRepository itemRepository;
    6. // 单增
    7. @Test
    8. void addOne() {
    9. // 实例化Item对象,赋值并新增到ES
    10. Item item=new Item()
    11. .setId(1L)
    12. .setTitle("罗技激光无线游戏鼠标")
    13. .setCategory("鼠标")
    14. .setBrand("罗技")
    15. .setPrice(128.0)
    16. .setImgPath("/1.jpg");
    17. // 利用自动生成的方法将item新增到ES,索引不存在会自动创建
    18. itemRepository.save(item);
    19. System.out.println("ok");
    20. }
    21. // 按id查询
    22. @Test
    23. void getOne(){
    24. // SpringData框架自带的按id查询的方法
    25. // Optional是一个类似包装类的概念,查询的结果封装到了这个类型中
    26. Optional<Item> optional=itemRepository.findById(1L);
    27. // 需要使用查询内容时使用optional.get()即可
    28. System.out.println(optional.get());
    29. }
    30. // 批量增
    31. @Test
    32. void addList(){
    33. // 实例化一个List集合
    34. List<Item> list=new ArrayList<>();
    35. // 将要新增的Item对象保存到这个List中
    36. list.add(new Item(2L,"罗技激光有线办公鼠标","鼠标",
    37. "罗技",89.0,"/2.jpg"));
    38. list.add(new Item(3L,"雷蛇机械无线游戏键盘","键盘",
    39. "雷蛇",299.0,"/3.jpg"));
    40. list.add(new Item(4L,"微软有线静音办公鼠标","鼠标",
    41. "微软",208.0,"/4.jpg"));
    42. list.add(new Item(5L,"罗技有线机械背光键盘","键盘",
    43. "罗技",266.0,"/5.jpg"));
    44. // 下面使用SpringData提供的方法执行批量新增
    45. itemRepository.saveAll(list);
    46. System.out.println("ok");
    47. }
    48. // 全查
    49. @Test
    50. void getAll(){
    51. // 利用SpringData的方法从ES中查询所有数据
    52. Iterable<Item> items=itemRepository.findAll();
    53. // for(Item item: items){
    54. // System.out.println(item);
    55. // }
    56. items.forEach(item -> System.out.println(item));
    57. }
    58. }

    SpringData自定义查询

    SpringData框架提供基本增删改查方法

    但是如果有具体的针对性的查询逻辑,一定还是需要我们自己编写代码

    例如实现类似数据库中的模糊查询

    单条件查询

    我们的查询需求是查询title属性中包含"游戏"这个分词的商品信息

    参考模糊查询代码

    select * from item where title like '%游戏%'
    

    我们使用ES查询,本质上运行的就是我们在es.http文档中编写的查询语句

    但是SpringData框架下,编写查询语句更加简单

    我们在ItemRepository接口中添加如下代码

    1. // SpringData自定义查询
    2. // 遵循SpringData框架规定的格式的前提下,编写方法名会自动生成查询逻辑
    3. // query: 表示当前方法是一个查询功能,类似sql中的select
    4. // Item\Items: 表示查询结果的实体类,带s的返回集合
    5. // By:标识开始设置条件,类似sql的where
    6. // Title: 要查询的字段名称
    7. // Matches: 是要执行的查询操作,这里是分词查询,类似sql的like
    8. Iterable<Item> queryItemsByTitleMatches(String title);

    再测试类中进行测试

    1. //单条件自定义查询
    2. @Test
    3. void queryOne(){
    4. // 查询 ES中title字段包含"游戏"分词的数据
    5. Iterable<Item> items=itemRepository.queryItemsByTitleMatches("游戏");
    6. items.forEach(item -> System.out.println(item));
    7. }

    上面代码运行时底层运行的查询语句为:

    1. ### 单条件搜索
    2. POST http://localhost:9200/items/_search
    3. Content-Type: application/json
    4. {
    5. "query": {"match": { "title": "游戏" }}
    6. }

    多条件查询

    在相对复杂的查询逻辑下

    经常使用多个条件来定位查询需要的数据

    这样就需要逻辑运算符"and"/"or"

    ItemRepository接口中添加多条件的查询方法

    1. // 多条件查询
    2. // 两个或多个条件之间直接编写And或Or表示查询逻辑
    3. // 参数名称实际上没有要求必须和字段名称匹配,底层代码是按照参数顺序赋值的
    4. Iterable<Item> queryItemsByTitleMatchesAndBrandMatches(String title,String brand);

    上面的查询添加了品牌作为条件

    逻辑关系是and(与)

    测试代码如下

    1. // 多条件自定义查询
    2. @Test
    3. void queryTwo(){
    4. Iterable<Item> items=itemRepository
    5. .queryItemsByTitleMatchesAndBrandMatches("游戏","雷蛇");
    6. items.forEach(item -> System.out.println(item));
    7. }

    底层运行的请求

    1. ### 多字段搜索
    2. POST http://localhost:9200/items/_search
    3. Content-Type: application/json
    4. {
    5. "query": {
    6. "bool": {
    7. "must": [
    8. { "match": { "title": "游戏"}},
    9. { "match": { "brand": "雷蛇"}}
    10. ]
    11. }
    12. }
    13. }

    当查询条件关系为And时,查询语句关键字为must

    当查询条件关系为Or时,查询语句关键字为should

     

    自定义查询

    排序查询

    如果实施排序需求,就在Repository接口中添加方法如下

    1. // 排序查询
    2. // 默认情况下,ES查询结果按score排序,如果想按其他的规则排序可以加OrderBy
    3. // 和数据库一样,默认升序排序 Desc会降序
    4. Iterable<Item> queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(
    5. String title, String brand);

    测试代码如下

    1. // 排序查询
    2. @Test
    3. void queryOrder(){
    4. Iterable<Item> items=itemRepository
    5. .queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc("游戏","罗技");
    6. items.forEach(item -> System.out.println(item));
    7. }

    底层代码逻辑

    1. ### 多字段搜索
    2. POST http://localhost:9200/items/_search
    3. Content-Type: application/json
    4. {
    5. "query": {
    6. "bool": {
    7. "should": [
    8. { "match": { "title": "游戏"}},
    9. { "match": { "brand": "罗技"}}
    10. ]
    11. }
    12. },"sort":[{"price":"desc"}]
    13. }

    分页查询

    SpringData框架支持分页查询

    只需要修改参数和返回值就能实现自动分页的效果

    修改ItemRepository接口代码如下

    1. // 分页查询
    2. // 当查询数据较多时,我们可以利用SpringData的分页功能,按用户要求的页码查询需要的数据
    3. // 返回值修改为Page类型,这个类型对象除了包含Iterable能够包含的集合信息之外,还包含分页信息
    4. Page<Item> queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(
    5. String title, String brand, Pageable pageable);

    测试代码如下

    1. // 分页查询
    2. @Test
    3. void queryPage(){
    4. int pageNum=1; //页码
    5. int pageSize=2; //每页条数
    6. Page<Item> page= itemRepository
    7. .queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(
    8. "游戏","罗技", PageRequest.of(pageNum-1,pageSize));
    9. page.forEach(item -> System.out.println(item));
    10. // page对象中还包含了一些基本的分页信息
    11. System.out.println("总页数:"+page.getTotalPages());
    12. System.out.println("当前页:"+page.getNumber());
    13. System.out.println("每页条数:"+page.getSize());
    14. System.out.println("当前页是不是首页:"+page.isFirst());
    15. System.out.println("当前页是不是末页:"+page.isLast());
    16. }
  • 相关阅读:
    Java毕业设计-基于springboot开发的网上租赁系统设计与实现-毕业论文(附毕设源代码)
    自制操作系统日志——第二十四天
    ttkefu客服系统的优势及应用领域
    新加坡大带宽服务器托管优势
    leetcode每日一题-周复盘
    Vue08/Vue 路由重定向、Vue路由404页面、Vue路由hash history
    数据湖和数据仓库的区别?
    群晖下虚拟机编译部署WOW服务端TrinityCore
    Chrome 115之后的版本,安装和使用chromedriver
    WebView 以及如何测试
  • 原文地址:https://blog.csdn.net/ClearDream__/article/details/125508107