6 定义业务层

现在,让我们声明一个服务,使用刚才定义的数据层创建业务代码。

package com.mozen.springboothibernatesearch.service;import com.mozen.springboothibernatesearch.model.Plant;
import com.mozen.springboothibernatesearch.repository.PlantRepository;
import org.springframework.stereotype.Service;import java.util.Arrays;
import java.util.List;@Service
public class PlantService {private PlantRepository plantRepository;private static final List<String> SEARCHABLE_FIELDS = Arrays.asList("name","scientificName","family");public PlantService(PlantRepository plantRepository) {this.plantRepository = plantRepository;}public List<Plant> searchPlants(String text, List<String> fields, int limit) {List<String> fieldsToSearchBy = fields.isEmpty() ? SEARCHABLE_FIELDS : fields;boolean containsInvalidField = fieldsToSearchBy.stream(). anyMatch(f -> !SEARCHABLE_FIELDS.contains(f));if(containsInvalidField) {throw new IllegalArgumentException();}return plantRepository.searchBy(text, limit, fieldsToSearchBy.toArray(new String[0]));}
}

我们告诉 Spring 这个 bean 是业务层的一部分。

它包含一个 searchPlant 函数,该函数将调用转发给 SearchRepositorysearchBy 函数。

在转发调用之前,要验证提供的字段。

这些字段是白名单,以检查搜索是否只针对所需的字段进行,这些字段是我们前面用@FullTextField 注释的字段。

如果提供的字段之一不是白名单字段的一部分,则抛出 llegalArgumentException。为了简单起见,不处理异常,但是应该使用 spring 提供的处理异常的多种方法之一来正确处理异常。

7 定义网络层

下一步是定义 REST API来接收来自客户端应用程序的 HTTP 请求。

package com.mozen.springboothibernatesearch.controller;import com.mozen.springboothibernatesearch.model.Plant;
import com.mozen.springboothibernatesearch.model.SearchRequestDTO;
import com.mozen.springboothibernatesearch.service.PlantService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@Slf4j
@RestController
@RequestMapping("/plant")
public class PlantController {private PlantService plantService;public PlantController(PlantService plantService) {this.plantService = plantService;}@GetMapping("/search")public List<Plant> searchPlants(SearchRequestDTO searchRequestDTO) {log.info("Request for plant search received with data : " + searchRequestDTO);return plantService.searchPlants(searchRequestDTO.getText(), searchRequestDTO.getFields(), searchRequestDTO.getLimit());}
}

我们使用带有单个 GET 映射的基本 Rest 控制器。在将调用转发到业务层之前,我们记录事件以跟踪请求的接收情况,从而简化对应用程序的监视。它使用 SearchRequestDTO 接收搜索请求。

package com.mozen.springboothibernatesearch.model;import lombok.Data;import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;@Data
public class SearchRequestDTO {@NotBlankprivate String text;private List<String> fields = new ArrayList<>();@Min(1)private int limit;
}

着是一个简单的 POJO,包含用于搜索的参数。同样,我们使用 javaxbean 验证注释来确保请求是有效的,并使用龙目岛@Data 注释来生成样板代码(getter、 setter、 toString ()、 …)。

注意,通过使用 POJO 作为 REST API 端点的单个参数,我们期望客户机将这些参数作为请求参数发送到 HTTP 请求中。

8 索引数据

最后,为了让 Lucene 能够搜索数据,需要对它进行索引。

在运行时,该索引由 Hibernate 自动管理,通过 Hibernate ORM 在每次执行操作(如创建或删除一个实体)时进行更改。但是,我们仍然需要初始化已经存储在数据库中的数据的索引(如果有的话)。

为此,我们需要在 application.yml 文件中添加一些配置:

server:port: 9000spring:datasource:url: jdbc:h2:mem:mydbusername: mozenpassword: passwordjpa:open-in-view: falseproperties:hibernate:search:backend:type: lucenedirectory.root: ./data/index

我们指明存储 Lucene 索引的根目录。在这里,我们选择将它直接放在项目文件夹中,但是在生产环境中运行时,应该谨慎选择这个目录,这取决于您的应用程序部署在哪里。

我们还创建了一个组件来封装与 Lucene 索引相关的所有操作。

package com.mozen.springboothibernatesearch.index;import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.massindexing.MassIndexer;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;import javax.persistence.EntityManager;@Transactional
@Component
public class Indexer {private EntityManager entityManager;private static final int THREAD_NUMBER = 4;public Indexer(EntityManager entityManager) {this.entityManager = entityManager;}public void indexPersistedData(String indexClassName) throws IndexException {try {SearchSession searchSession = Search.session(entityManager);Class<?> classToIndex = Class.forName(indexClassName);MassIndexer indexer =searchSession.massIndexer(classToIndex).threadsToLoadObjects(THREAD_NUMBER);indexer.startAndWait();} catch (ClassNotFoundException e) {throw new IndexException("Invalid class " + indexClassName, e);} catch (InterruptedException e) {throw new IndexException("Index Interrupted", e);}}
}

对于我们的简单演示应用程序,我们只声明一个函数,该函数使用进程中指定数量的线程为给定的 Class 构建索引。

现在,我们需要调用这个函数,将以前定义的 Plant 类作为参数传递给它。

为此,我们还可以创建一个新的 REST Controler,其中包含一个通过 HTTP 请求触发索引的端点,以便能够随意重建索引。

我们只需要使用一个 ApplicationRunner,它将在每次启动时被调用。

@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Beanpublic ApplicationRunner buildIndex(Indexer indexer) throws Exception {return (ApplicationArguments args) -> {indexer.indexPersistedData("com.mozen.springboothibernatesearch.model.Plant");};}
}

我们直接在主 Application 类中定义 Bean。

方法使用 Indexer 作为参数通过依赖注入。该方法在应用程序启动期间、上下文初始化之后以及在 spring 引导应用程序启动之前执行。

9 把它们放在一起

让我们首先初始化示例数据。

package com.mozen.springboothibernatesearch;import com.mozen.springboothibernatesearch.index.Indexer;
import com.mozen.springboothibernatesearch.model.Plant;
import com.mozen.springboothibernatesearch.repository.PlantRepository;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;import java.util.Arrays;
import java.util.List;@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Beanpublic ApplicationRunner initializeData(PlantRepository plantRepository) throws Exception {return (ApplicationArguments args) -> {List<Plant> plants = Arrays.asList(new Plant("subalpine fir", "abies lasiocarpa", "pinaceae"),new Plant("sour cherry", "prunus cerasus", "rosaceae"),new Plant("asian pear", "pyrus pyrifolia", "rosaceae"),new Plant("chinese witch hazel", "hamamelis mollis", "hamamelidaceae"),new Plant("silver maple", "acer saccharinum", "sapindaceae"),new Plant("cucumber tree", "magnolia acuminata", "magnoliaceae"),new Plant("korean rhododendron", "rhododendron mucronulatum", "ericaceae"),new Plant("water lettuce", "pistia", "araceae"),new Plant("sessile oak", "quercus petraea", "fagaceae"),new Plant("common fig", "ficus carica", "moraceae"));plantRepository.saveAll(plants);};}@Beanpublic ApplicationRunner buildIndex(Indexer indexer) throws Exception {return (ApplicationArguments args) -> {indexer.indexPersistedData("com.mozen.springboothibernatesearch.model.Plant");};}

我们用第二个 Bean 声明扩展了主 Application 类。在应用程序启动期间,它将以与第一个应用程序相同的方式运行。

注意,我们没有在任何地方声明数据 SQL 模式。因为我们的数据库是一个嵌入式数据库,所以属性 spring.jpa.hibernate.ddl-auto 被默认设置为 create-drop,并且我们的数据库模式是自动生成的,这对于像这样的简单应用程序来说非常简洁。

现在,让我们通过首先启动应用程序来测试。

mvn spring-boot:run

Java | 使用 Hibernate Search 构建一个带有全文搜索的 Spring Boot REST API (二)相关推荐

  1. Java趣谈——如何构建一个高效且可伸缩的缓存

    Java趣谈--如何构建一个高效且可伸缩的缓存 本集概要: 怎样构建一个线程安全而又高效.可伸缩的缓存? 怎样利用设计模式,把缓存做成通用的工具? 除了synchronize和volatile,我们还 ...

  2. 《深入理解 Spring Cloud 与微服务构建》第十六章 Spring Boot Security 详解

    <深入理解 Spring Cloud 与微服务构建>第十六章 Spring Boot Security 详解 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...

  3. 带有第三方工具的Spring Boot Initilizr

    This is continuation to my two previous posts. Before reading this post, please go through my previo ...

  4. java application.xml_第4章 零XML配置的Spring Boot Application

    第4章 零XML配置的Spring Boot Application Spring Boot 提供了一种统一的方式来管理应用的配置,允许开发人员使用属性properties文件.YAML 文件.环境变 ...

  5. Java(Spring boot)实现生成二维码

    文章目录 一.引入spring boot依赖: 二.工具类代码: 三.调用工具类生成二维码 1.将链接生成二维码图片并保存到指定路径 2.将链接生成二维码直接显示在页面 3.将以get请求传参链接生成 ...

  6. 怎样把一个项目加入微服务器,构建微服务:快速搭建Spring Boot项目

    Spring Boot简介: Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员 ...

  7. filter java oauth_java – 带有自定义安全过滤器的Spring Boot OAuth2

    我有一个带有OAuth2授权和资源服务器的 spring boot设置.用户可以通过向/ oauth / token发出POST请求来获取令牌.到现在为止还挺好. 但是,我不想通过BASIC auth ...

  8. erp开发和java开发区别_Java程序员求职必学:Spring boot学习指南!

    黑马程序员上海中心 学姐微信:CZBKSH 关注 咳咳,今天学姐就来和你们说说Spring对于Java程序员的重要性. 首先,Spring 官网首页是这么介绍自己的--"Spring: th ...

  9. Spring boot锦集(二):整合邮件发送的四种方法 | 纯文本的邮件、带有图片的邮件、带Html的邮件、带附件的邮件(很详细)

    前言 邮件发送,听着很神秘,然而对于Spring Boot来说,这个功能已被集成好,只需引入spring-boot-starter-mail依赖后,少量代码即可实现大部分邮件发送需求. 本文以异常教程 ...

最新文章

  1. time_t和字符串间的转化
  2. 关于 Java Collections API 您不知道的 5 件事--转
  3. 数组去重(包括es6)
  4. MTK 修改后置Camera方向/镜像 Patch
  5. 三星电子与索尼在CMOS图像传感器市场份额差距缩小
  6. Unity3D-Rigidbody
  7. 在MFC类中各种类的指针的获取和应用
  8. SharePoint2010传入电子邮件
  9. python中特殊符号怎么输入_python中怎么输入引号
  10. CentOS 7.6 安装 nginx,配置端口访问网站,切换root目录
  11. 程序员入门:如何自学编程
  12. c语言运算符优先级(c语言运算符优先级由高到低的顺序)
  13. python使用jpype模块调用java的jar包来实现功能
  14. 身份证归属地查询免费api接口代码
  15. cad布局教程_10+篇CAD三维模型一键出工程图,你掌握了?
  16. 支持向量机 (SVM)算法的原理及优缺点
  17. 安卓开发--Eclipse搭建开发环境
  18. 中国书法家协会理事、陕西武警总队原司令员王春新莅临秦储指导交流
  19. covariate shift和batch normalization问题
  20. 并联电容器总结与理解

热门文章

  1. Apple开发者账号续费问题
  2. Jordan 块的几何
  3. 神舟Z8-CU7NA 安装ubuntu,无线网RTL8821CE不能使用的解决思路
  4. 2021年阿里菜鸟网络春招实习岗面试分享,简历+面试+面经全套资料!
  5. python批量修改图片分辨率完整程序
  6. 【Excel函数】OFFSET函数
  7. html5绘制 布局图,html5 div布局与table布局详解
  8. 网络安全系列-二十七: 基于pkts.io解析pcap,生成五元组及payload
  9. 超实数进校园,,迈向现代数学的一大步
  10. STC-Seg:首个超越PointTrack的弱监督视频MOTS算法