|
易购商城 |
第四天 |
目 录
1 课程计划 3
2 缓存逻辑实现 3
2.1 需求 3
2.2 缓存逻辑实现 3
2.2.1 第一部分:添加缓存 3
2.2.2 第二部分:缓存同步 9
3 搜索系统实现 9
3.1 系统架构 9
3.2 实现思路 10
4 搭建搜索服务器 10
4.1 第一部分:配置Solr服务器 10
4.1.1 第一步:解压一个Tomcat 10
4.1.2 第二步:部署Solr服务到Tomcat中 10
4.1.3 第三步:添加Solr运行依赖的jar包 11
4.2 第二部分:配置SolrHome 11
4.2.1 第一步:创建SolrHome 11
4.2.2 第二步:配置SolrCore 12
4.3 第三部分:在Solr服务器中加载SolrHome 14
4.3.1 第一步:修改web.xml加载SolrHome 14
4.3.2 第二步:启动Tomcat测试 14
4.4 特别说明 15
4.4.1 快速配置易购商品搜索实例步骤 15
4.4.2 测试 16
5 搭建搜索系统ego-search 17
5.1 系统说明 17
5.2 技术选择 17
5.3 配置步骤 17
5.3.1 第一步:创建Maven项目(war模型) 17
5.3.2 第二步:导入jar依赖 18
5.3.3 第三步:配置SpringMVC核心控制器 20
5.3.4 第四步:Spring整合SpringMVC 21
5.3.5 第五步:Spring整合Mybatis-plus 22
5.3.6 第六步:Spring整合Solr 24
5.3.7 第七步:测试 25
5.4 创建索引库 25
5.4.1 第一步:采集数据 25
5.4.2 第二步:创建文档 27
5.4.3 第三步:将文档写入索引库 29
5.5 创建索引库特别说明 31
5.6 将solr服务器部署到Linux 33
5.6.1 准备工作 33
5.6.2 部署步骤 33
6 实现搜索功能 34
6.1 需求 34
6.2 业务流程 34
6.3 第一部分:在ego-search中发布搜索接口 34
6.3.1 第一步:定义接口规则 34
6.3.2 第二步:业务代码实现 36
6.3.3 第三步:发布接口 39
6.3.4 第四步:测试接口 39
6.4 第二部分:在ego-portal调用搜索服务接口 40
6.4.1 第一步:确定请求路径和参数 40
6.4.2 第二步:创建SearchService接口及其实现类 41
6.4.3 第二步:创建SearchController类 42
6.4.4 第三步:解决多张图片显示问题 42
课程计划
(1)实现缓存逻辑。(Redis)
(2)完成搜索功能。(Solr)
- 缓存逻辑实现
- 需求
我们知道使用缓存,可以提高查询效率,那什么情况下需要使用缓存呢?通常而言,使用缓存需满足以下两个条件:
(1)查询频率较高的数据。
(2)修改频率较低的数据。
对于第一点,在我们开发过程中,如果查询业务比较多,需要频繁的连接数据库,这会对数据库的性能带来极大的损耗;这个时候可以考虑对这部分数据添加缓存。
对于第二点,如果我们业务中,需要频繁的修改某个数据,这个时候是不适合给它添加缓存的,因为每次修改了数据,都需要去更新缓存。
综合上面两点,我们需要给导航菜单、广告位投放的内容添加缓存。
-
- 缓存逻辑实现
- 第一部分:添加缓存
- 缓存逻辑实现
需求:在REST接口中,给导航菜单、首页大广告位内容添加缓存。缓存逻辑在ego-rest工程中实现。
-
-
-
- 第一步:ego-rest添加redis的jar依赖
-
-
<!-- Redis客户端 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> |
-
-
-
- 第二步:Spring整合Redis集群
-
-
在src路径下,添加spring-jedis.xml配置文件,整合redis。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 连接池配置 --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 最大连接数 --> <property name="maxTotal" value="30" /> <!-- 最大空闲连接数 --> <property name="maxIdle" value="10" /> <!-- 每次释放连接的最大数目 --> <property name="numTestsPerEvictionRun" value="1024" /> <!-- 释放连接的扫描间隔(毫秒) --> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <!-- 连接最小空闲时间 --> <property name="minEvictableIdleTimeMillis" value="1800000" /> <!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 --> <property name="softMinEvictableIdleTimeMillis" value="10000" /> <!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 --> <property name="maxWaitMillis" value="1500" /> <!-- 在获取连接的时候检查有效性, 默认false --> <property name="testOnBorrow" value="true" /> <!-- 在空闲时检查有效性, 默认false --> <property name="testWhileIdle" value="true" /> <!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true --> <property name="blockWhenExhausted" value="false" /> </bean> <!-- redis集群 --> <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster"> <constructor-arg index="0"> <set> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.23.12"></constructor-arg> <constructor-arg index="1" value="7001"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.23.12"></constructor-arg> <constructor-arg index="1" value="7002"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.23.12"></constructor-arg> <constructor-arg index="1" value="7003"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.23.12"></constructor-arg> <constructor-arg index="1" value="7004"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.23.12"></constructor-arg> <constructor-arg index="1" value="7005"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.23.12"></constructor-arg> <constructor-arg index="1" value="7006"></constructor-arg> </bean> </set> </constructor-arg> <constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg> </bean> </beans> |
-
-
-
- 第三步:修改ContentServiceImpl类
-
-
public class ContentServiceImpl implements ContentService{
private String EGO_CONTENT = "EGO_CONTENT";
@Autowired private JedisCluster jedisCluster;
@Autowired private ContentMapper mapper;
/* * 缓存逻辑 * (1)首先查找缓存。 * (2)如果缓存有数据,则直接返回数据,不需要查询数据库 * (3)如果缓存中没有数据,则查询数据库。并且将数据放入缓存中。 * * (4)缓存不能影响正常的业务执行,即当前缓存无法使用,则直接查询数据库 * * 确定选用哪一个数据结构类型(string,list,set,hash) * 选用数据结构类型的时候,有一个原则:能用hash尽量使用hash。原因:减少key的数量,在寻址的时候速度更快。 * * {key:{field:value}} * 我们这里使用hash数据结构类型,key定义成一个常量EGO_CONTENT field就使用类型分类的id,value使用内容列表的json格式。 * * 注意:hash结构只能存储string格式的数据 * */ public EgoResult getContentByCatId(Long catId) {
List<Content> list = null; try {
//1、查找缓存 String jsonData = jedisCluster.hget(EGO_CONTENT, catId+"");
if(null!=jsonData && !"".equals(jsonData)){ //缓存里面有数据,则直接返回数据即可 list = JsonUtils.jsonToList(jsonData, Content.class); }else{ //如果缓存中查不到数据,则查询数据库 Map<String, Object> columnMap = new HashMap<>(); columnMap.put("category_id", catId); list = mapper.selectByMap(columnMap); //再将数据放入缓存中 jedisCluster.hset(EGO_CONTENT, catId+"", JsonUtils.objectToJson(list)); }
} catch (Exception e) { e.printStackTrace(); //如果缓存中查不到数据,则查询数据库 Map<String, Object> columnMap = new HashMap<>(); columnMap.put("category_id", catId);
list = mapper.selectByMap(columnMap); }
return EgoResult.ok(list); } } |
-
-
-
- 第四步:测试
-
-
(1)重启rest工程
(2)访问portal工程首页。将缓存添加到redis中。
(3)再次访问portal工程首页。查看缓存是否生效。
-
-
- 第二部分:缓存同步
-
修改、更新导航菜单、网站内容后,要同步修改缓存,或者清空对应的缓存。
- 搜索系统实现
- 系统架构
在本项目中,我们将搜索业务独立出来,创建搜索子系统。这样做,既能提高系统的拓展能力,也能灵活的对系统进行分布式部署。
-
- 实现思路
(1)搭建搜索服务器。
(2)创建搜索系统。
(3)发布搜索服务的公共接口。
(4)在门户系统调用该接口,实现搜索。
- 搭建搜索服务器
- 第一部分:配置Solr服务器
说明:Solr可以独立运行,需要servlet容器加载它。本文使用tomcat。
-
-
- 第一步:解压一个Tomcat
-
解压一个新的Tomcat,专门用来加载Solr。
-
-
- 第二步:部署Solr服务到Tomcat中
-
在Solr的下载包中,提供了Solr的war包程序。(空的war包程序)
拷贝solr.war到Tomcat的webapp目录下。并解压
-
-
- 第三步:添加Solr运行依赖的jar包
-
在Solr的下载包中,提供Solr服务器运行所依赖的jar包。
(1)拷贝/example/lib/ext下的所有包,到solr应用的lib目录中
(2)拷贝/example/resource/log4j.properties,到solr应用的classes目录下。
-
- 第二部分:配置SolrHome
说明:Solr的下载包中,提供了标准的SolrHome配置。
-
-
- 第一步:创建SolrHome
-
说明:拷贝该solr文件夹在本地,修改名称为SolrHome。
|
-
-
-
- SolrHome说明
-
-
(1)SolrHome是Solr配置搜索服务的主目录。
(2)collection1称为Solr服务的一个实例(solrCore)。
(3)一个solr实例对应一个索引库。
(4)Solr可以同时配置多个实例。以便为不同的java程序提供搜索服务。
配置solr服务,就是在配置solr实例。
-
-
- 第二步:配置SolrCore
- Step1:配置SolrCore实例的名称
- 第二步:配置SolrCore
-
说明:每一个实例都有自己的名称。在core.properties文件中配置
在这里,我们将其修改为:soreCore0719
-
-
-
- Step2:配置SolrCore所需的jar依赖
-
-
说明:Solr下载包中,提供SolrCore所需要的所有jar依赖。
(1)在SolrHome同级目录下,创建depJar文件夹。(目的:方便管理jar依赖)
(2)拷贝contrib、dist两个目录到depJar目录下。
(3)修改/collection1/conf目录下的solrconfig.xml,加载jar包
说明:solr是通过<lib>标签,来加载运行所需要的jar包的。
(4)配置索引库目录
说明:solr是通过<dataDir>标签,来指定索引库的目录的。
默认路径是在SolrCore目录下,跟conf目录同级。首次加载时,将自动创建/data目录。
-
- 第三部分:在Solr服务器中加载SolrHome
- 第一步:修改web.xml加载SolrHome
- 第三部分:在Solr服务器中加载SolrHome
在solr的应用中,是通过web.xml来加载SolrHome的。
说明:在这里是通过修改<env-entry>标签,来加载SolrHome的。
-
-
- 第二步:启动Tomcat测试
-
访问地址 http://localhost:8080/solr
-
- 特别说明
我们之前在solr的课程中,已经配置好了solr服务器,并且已经配置好了一个solrCore0719搜索实例。
我们现在只需要在这个solr服务器的基本上,复制solrCore0719,修改其部分配置即可。
-
-
- 快速配置易购商品搜索实例步骤
- 第一步:复制solrCore0719实例
- 快速配置易购商品搜索实例步骤
-
说明:复制collection1文件夹,改名为ego
-
-
-
- 第二步:修改实例名称为ego
-
-
说明:实例名称在core.properties文件中配置
-
-
-
- 第三步:删除原实例的索引库
-
-
说明:当前ego实例是从其它实例复制过来的。因此要删除之前的索引库。
删除data文件夹即可。
-
-
- 测试
-
启动solr服务器所在的Tomcat,访问solr管理控制台。
访问地址:http://localhost:8888/solr
Solr服务器配置成功!!!
- 搭建搜索系统ego-search
- 系统说明
定义搜索服务的接口,供其它系统调用。
-
- 技术选择
核心框架:Spring+SpringMVC+Mybatis-plus
数 据 库:MySQL
搜索框架:Solr
-
- 配置步骤
思路:(1)创建项目
(2)整合框架
-
-
- 第一步:创建Maven项目(war模型)
-
注意:使用maven mudule创建,继承ego-project工程
-
-
- 第二步:导入jar依赖
-
导包说明:
(1)ego-base子工程
(2)Spring核心包
(3)SpringMVC相关
(4)AOP相关包
(5)JSON依赖包
(6)Solr核心包
(7)mysql驱动+druid连接池
(8)Spring-jdbc
(9)Mybatis+Spring整合包
导入插件:Tomcat插件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.gzsxt.ego</groupId> <artifactId>ego-project</artifactId> <version>1.0</version> </parent> <groupId>cn.gzsxt.ego</groupId> <artifactId>ego-search</artifactId> <version>1.0</version> <packaging>war</packaging>
<dependencies>
<dependency> <groupId>cn.gzsxt.ego</groupId> <artifactId>ego-base</artifactId> <version>1.0</version> </dependency> <!-- Mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> <!-- 整合SSM,导入spring、springmvc、mybatis相关依赖 --> <!-- 导入spring核心依赖 4+1 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency>
<!-- 导入springmvc相关依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency>
<!-- 导入mybatis相关依赖 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> </dependency> <!-- 导入spirng-jdbc+事物 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <!-- 导入jdbc、连接池依赖 --> <!-- MySql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <!-- Jackson Json处理工具包 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- solr客户端 --> <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId> </dependency> </dependencies> <build> <plugins> <!-- 配置Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>8083</port> <path>/</path> <uriEncoding>UTF-8</uriEncoding> </configuration> </plugin> </plugins> </build> </project> |
-
-
- 第三步:配置SpringMVC核心控制器
-
说明:可以从ego-rest工程拷贝web.xml文件,修改<url-parten>配置
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_2_5.xsd ">
<!-- 配置编码过滤器 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<!-- 配置springmvc核心控制器 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-*.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <!-- 所有请求搜索系统的请求,都必须在url加上/search/前缀,好处:方便做维护。 --> <url-pattern>/search/*</url-pattern> </servlet-mapping> </web-app> |
-
-
- 第四步:Spring整合SpringMVC
-
说明:可以从rest工程拷贝spring-mvc.xml文件,修改注解包结构。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 开启注解扫描 --> <context:component-scan base-package="cn.gzsxt.search"/> <!-- 开启springmvc注解驱动 --> <mvc:annotation-driven/>
</beans> |
-
-
- 第五步:Spring整合Mybatis-plus
-
(1)创建resourse.properties文件
#配置数据源,即jdbc驱动信息 driver.driverClassName=com.mysql.jdbc.Driver driver.url=jdbc:mysql://localhost:3306/ego driver.username=root driver.password=gzsxt
#配置solr服务器地址 solr.address=http://localhost:8888/solr/ego |
(2)从rest工程拷贝spring-data.xml文件,去掉事物代理配置。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:property-placeholder file-encoding="utf-8" location="classpath:resource.properties"/>
<!-- 1、创建数据源 --> <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${db.driver}"/> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/>
<property name="maxActive" value="20"/> <property name="minIdle" value="5"/> </bean>
<!-- 2、mybatis-plus整合Spring 任何的数据库的框架,要使用spring的事物代理,必须使用spring提供的数据源,必须整合spring才可以使用 --> <bean name="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"> <!-- 加载数据源 --> <property name="dataSource" ref="dataSource"/>
<!-- 指定pojo目录 --> <property name="typeAliasesPackage" value="cn.gzsxt.base.pojo"/>
<!-- 配置mybatis-plus插件 --> <property name="plugins"> <list> <!-- 配置分页插件 --> <bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"/>
<!-- 配置拦截器属性 --> <bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor"> <!-- 配置sql响应时间,开发阶段方便做调优 --> <property name="maxTime" value="1000"/>
<property name="format" value="true"/>
</bean> </list> </property>
<!-- 配置mybatis-plus全局策略 --> <property name="globalConfig" ref="globalConfiguration"></property>
</bean>
<!-- 3、配置mybatis的动态代理 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
<property name="basePackage" value="cn.gzsxt.base.mapper"></property>
</bean>
<!-- 配置mybatis-plus全局属性 --> <!-- 定义 MybatisPlus 的全局策略配置--> <bean id ="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration"> <!-- 在 2.3 版本以后,dbColumnUnderline 默认值是 true,即pojo属性开启驼峰标识 --> <property name="dbColumnUnderline" value="true"></property> <!-- 全局的主键策略 --> <!-- AUTO->`0`("数据库ID自增") INPUT->`1`(用户输入ID") ID_WORKER->`2`("全局唯一ID") UUID->`3`("全局唯一ID") --> <property name="idType" value="0"></property> <!-- 全局的表前缀策略配置 --> <property name="tablePrefix" value="tb_"></property> </bean>
</beans> |
-
-
- 第六步:Spring整合Solr
-
修改spring-mvc.xml文件,使用Spring创建HttpSolrServer客户端
<!-- 创建solr的客户端 --> <bean id="solrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer"> <constructor-arg index="0" value="${solr.address}"></constructor-arg> </bean> |
-
-
- 第七步:测试
-
(1)更新项目,安装到本地仓库。(update、clean、install)
(2)启动项目
-
- 创建索引库
步骤说明。(复习回顾)
(1)采集数据。
(2)将数据转换成Solr文档。
(3)连接solr服务器,将文档写入索引库。
-
-
- 第一步:采集数据
-
说明:需求采集的字段
(1)参与搜索的字段:商品名称、商品卖点、商品价格、商品类别、
(2)参与结果展示的字段:商品id、商品图片
-
-
-
- Step1:创建SearchItem类
-
-
在ego-base工程中创建。
package cn.gzsxt.base.pojo;
/** * 自定义商品搜索类 * @author ccnulyq * */ public class SearchItem {
private Long id; private String title; private String sellPoint; private Long price; private String image; private String categoryName; public SearchItem() { super(); } // 补全get、set方法 } |
-
-
-
- Step2:修改ItemMapper接口
-
-
定义采集数据方法。
package cn.gzsxt.base.mapper;
import java.util.List; import org.apache.ibatis.annotations.Select; import com.baomidou.mybatisplus.mapper.BaseMapper; import cn.gzsxt.base.pojo.Item; import cn.gzsxt.base.vo.SearchItem;
public interface ItemMapper extends BaseMapper<Item>{
/**solr服务采集数据 * * @return */ @Select(value="select i.id,i.sell_point as sellPoint,i.title,i.price,i.image,c.name as categoryName " + "from tb_item i left join tb_item_cat c on i.cid = c.id where i.status=1") List<SearchItem> gathData(); }
|
-
-
-
- Step5:创建SearchService接口及其实现类
-
-
package cn.gzsxt.search.service.impl;
import java.util.ArrayList; import java.util.List;
import org.apache.solr.client.solrj.impl.HttpSolrServer; import org.apache.solr.common.SolrInputDocument; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
import cn.gzsxt.base.mapper.ItemMapper; import cn.gzsxt.base.vo.EgoResult; import cn.gzsxt.base.vo.SearchItem; import cn.gzsxt.search.service.SearchService;
@Service public class SearchServiceImpl implements SearchService{
@Autowired private HttpSolrServer solrServer;
@Autowired private ItemMapper itemMapper;
@Override public List<SearchItem> gataData() {
List<SearchItem> items = itemMapper.gathData();
return items; } } |
-
-
- 第二步:创建文档
-
注意事项:Solr在创建文档的时候,需要指定域。
因此,必须先配置业务域!!!
-
-
-
- Step1:配置业务域
-
-
业务域属性说明:
域 | Tokened | Indexed | Stored |
商品的id | N | Y | Y |
商品的名称 | Y | Y | Y |
商品的类别 | N | Y | Y |
商品的价格 | Y | Y | Y |
商品的图片 | N | N | Y |
商品卖点 | Y | Y | Y |
修改ego实例中的schema.xml文件,添加业务域配置
<field name="item_title" type="text_ik" indexed="true" stored="true"/> <field name="item_sell_point" type="text_ik" indexed="true" stored="true"/> <field name="item_price" type="long" indexed="true" stored="true"/> <field name="item_image" type="string" indexed="false" stored="true" /> <field name="item_category_name" type="string" indexed="true" stored="true" /> |
-
-
-
- Step2:修改SearchService及其实现类
-
-
新增创建文档方法。
@Override public List<SolrInputDocument> gathDocument(List<SearchItem> items) {
List<SolrInputDocument> docs = new ArrayList<>();
//solr的域,必须先定义后使用 SolrInputDocument doc = null; for (SearchItem item : items) { doc = new SolrInputDocument();
doc.addField("id", item.getId()); doc.addField("item_title", item.getTitle()); doc.addField("item_category_name", item.getCategoryName()); doc.addField("item_price", item.getPrice()); doc.addField("item_sell_point", item.getPrice()); doc.addField("item_image", item.getImage());
docs.add(doc); }
return docs; } |
-
-
- 第三步:将文档写入索引库
- Step1:修改SearchService接口及其实现类
- 第三步:将文档写入索引库
-
注意:现在实现类中注入HttpSolrServer对象。
@Override public EgoResult addDocuments(List<SolrInputDocument> docs) {
try { solrServer.add(docs); solrServer.commit(); return EgoResult.ok(); } catch (Exception e) { e.printStackTrace(); return EgoResult.build(400, "连接solr服务器异常"); } } |
-
-
-
- Step2:创建SearchController类
-
-
定义创建索引库的入口。
package cn.gzsxt.search.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;
import cn.gzsxt.base.vo.EgoResult; import cn.gzsxt.search.service.SearchService;
@Controller public class SearchController {
@Autowired private SearchService searchService;
@RequestMapping("/import/all") @ResponseBody public EgoResult createIndex(){
EgoResult result = searchService.addDocuments(searchService.gathDocument(searchService.gataData()));
return result;
} } |
-
-
-
- Step3:测试
-
-
(1)重新启动solr服务器。
(2)更新项目。(update、clean、install)
(3)启动项目
(4)创建索引库
使用postman,访问地址 http://localhost:8083/search/import/all
(5)访问solr管理控制台。
创建索引库成功!!!
-
- 创建索引库特别说明
我们之前已经配置了dataimport插件,我们也可以通过插件来创建索引库。
(1)修改schema.xml文件,配置业务域
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" /> <!--配置商品的业务域--> <field name="item_title" type="text_ik" indexed="true" stored="true"/> <field name="item_category_name" type="string" indexed="true" stored="true"/> <field name="item_price" type="long" indexed="true" stored="true"/> <field name="item_sell_point" type="text_ik" indexed="true" stored="true"/> <field name="item_image" type="string" indexed="false" stored="true"/> |
(2)修改data-config.xml文件,配置映射关系
<?xml version="1.0" encoding="UTF-8" ?> <dataConfig> <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/ego" user="root" password="gzsxt"/> <document> <!--实际上是完成了采集数据+转成数据称文档类型的工作--> <entity name="product" query="select i.id,i.title,i.price,i.sell_point as sellPoint,i.image,c.name as categoryName from tb_item i left join tb_item_cat c on i.cid = c.id where i.status=1"> <!--column 指的是从数据库查询到的字段结果 name 对应的域的名称 --> <field column="id" name="id"/> <field column="title" name="item_title"/> <field column="sellPoint" name="item_sell_point"/> <field column="price" name="item_price"/> <field column="categoryName" name="item_category_name"/> <field column="image" name="item_image"/> </entity> </document> </dataConfig> |
(3)重新启动solr服务器。
(4)访问Solr管理控制台。
Dataimport插件配置成功!!!
-
- 将solr服务器部署到Linux
说明:我们项目开发完成之后,都是要部署到linux环境上。
所以:solr服务器和search工程都要部署到linux上。
-
-
- 准备工作
-
(1)在Linux上创建/usr/local/solr目录
(2)解压一个tomcat到/usr/local/solr目录下。
-
-
- 部署步骤
-
(1)将本地tomcat中的solr程序拷贝到Linux上的tomcat中。
(2)将本地的solrHome和depJar拷贝到Linux的/usr/local/solr目录下
(3)修改solr程序的web.xml文件,修改solrHome的地址: /usr/local/solr/solrHome
(4)修改solr实例的solrconfig.xml文件,修改depJar的路径
(5)如果要使用dataimport插件,要修改solr实例的data-config.xml文件,修改数据源地址。
- 实现搜索功能
- 需求
根据关键词查询索引库。
-
- 业务流程
(1)在ego-search工程中发布搜索接口。
(2)在ego-portal中远程调用接口。
-
- 第一部分:在ego-search中发布搜索接口
思路:(1)定义接口规则
(2)业务代码实现
(3)对外发布搜索接口
-
-
- 第一步:定义接口规则
-
说明:接口规则通常由接口开发者根据需求制定,并提供接口文档。
请求方法 | GET |
URL | http://search.ego.com/search/doSearch |
参数说明 | keyword:关键词 price:价格区间,格式"20-50" page:请求的页码 sort:价格排序,0升序,1降序,null不排序 categoryName:商品类别过滤 |
示例 | http://search.ego.com/search/doSearch?keyword=手机 &price=1000-1999&page=2&sort=1&categoryName=智能手机 |
返回值 | { "curPage": 2, "totalPages": 20, "recordCount": 600, "itemList": [{ "id":100544, "price": 5288, "image":"http://image.ego.com/images/1.jgp", "cateGoryName":"智能手机", "sell_point":"功能强大,经济适用", "title":"华为k20" },{ "id":1005455, "price": 3999, "image":"http://image.ego.com/images/3.jgp", "cateGoryName":"智能手机", "sell_point":"功能强大,经济适用", "title":"华为k18", }] } |
根据接口规则,创建SearchResult返回值类型,在ego-base工程中创建。
public class SearchResult {
private Long recordCount; private List<SearchItem> itemList; private Integer totalPages; private Integer curPage; //补充get、set方法 } |
-
-
- 第二步:业务代码实现
-
(1)修改resource.properties配置文件
#配置solr服务器地址 solr.address=http://192.168.4.253:8888/solr/ego solr.pageSize=60 |
(2)修改SearchService接口及其实现类,定义搜索方法
@Value("${solr.pageSize}") private Integer pageSize;
@Override public SearchResult doSearch(String keyword, String categoryName, String price, int page, Integer sort) {
SearchResult result = new SearchResult();
//1、创建查询对象 SolrQuery query = getSolrQuery(keyword,categoryName,price,page,sort);
try { //2、执行搜索 QueryResponse response = solrServer.query(query);
//3、解析查询结果 if(0==response.getStatus()){ SolrDocumentList documentList = response.getResults();
//获取商品总数量 long numFound = documentList.getNumFound(); result.setRecordCount(numFound);
//获取高亮设置之后的的高亮域的值 Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
//获取商品列表 List<SearchItem> items = new ArrayList<>(); SearchItem item = null; for (SolrDocument doc : documentList) { item = new SearchItem(); item.setId(Long.valueOf((String) doc.get("id")));
boolean flag = true; if(null!=highlighting && highlighting.size()>0){ Map<String, List<String>> map = highlighting.get(doc.get("id")); if(null!=map && map.size()>0){ List<String> list = map.get("item_title"); if(null!=list && list.size()>0){ item.setTitle(list.get(0)); flag = false; } } }
if(flag){ item.setTitle((String) doc.get("item_title")); } item.setImage((String) doc.get("item_image")); item.setSellPoint((String) doc.get("item_sell_point")); item.setCategoryName((String) doc.get("item_category_name")); item.setPrice((long) doc.get("item_price")); items.add(item); } result.setItemList(items); result.setCurPage(page);
//获取总页数 int pageCount = (int) Math.ceil(result.getRecordCount()*1.0/pageSize);
result.setTotalPages(pageCount); } } catch (Exception e) { e.printStackTrace(); }
return result; }
private SolrQuery getSolrQuery(String keyword, String categoryName, String price, int page, Integer sort) { SolrQuery query = new SolrQuery(); //设置查询的关键词 if(null!=keyword && !"".equals(keyword)){ query.set("q", "item_title:"+keyword);
//有关键词时,才需要做高亮设置 query.setHighlight(true); query.setHighlightSimplePre("<font style='color:red'>"); query.setHighlightSimplePost("</font>");
query.addHighlightField("item_title");
}else{ query.set("q", "item_title:*"); } //判断是否有商品类别过滤 if(null!=categoryName && !"".equals(categoryName)){ query.addFilterQuery("item_category_name:"+categoryName); }
//判断是否有价格过滤 0-9 [min TO MAX] if(null!=price && !"".equals(price)){
String[] split = price.split("-"); if(split.length>1){ query.addFilterQuery("item_price:["+split[0]+" TO "+split[1]+"]"); }else{ query.addFilterQuery("item_price:["+split[0]+" TO *]"); } } //分页设置 query.set("start", (page-1)*pageSize); query.set("rows", pageSize);
//判断是否按价格排序 0升序 1降序 if(null!=sort){ if(0==sort){ query.set("sort", "item_price asc"); }else{ query.set("sort", "item_price desc"); } } return query; } |
-
-
- 第三步:发布接口
-
修改SearchController类,发布搜索接口
@RequestMapping(value="/doSearch",method=RequestMethod.GET) @ResponseBody public SearchResult doSearch(String keyword,String categoryName,String price, @RequestParam(defaultValue="1")Integer page,Integer sort){ SearchResult result = searchService.doSearch(keyword, categoryName, price, page, sort);
return result; } |
-
-
- 第四步:测试接口
-
(1)重新编译ego-base、ego-search工程
(2)重启ego-search工程
(3)使用postman工具,访问接口
接口发布成功!!!
-
- 第二部分:在ego-portal调用搜索服务接口
思路:(1)确定请求路径和请求参数
(2)业务代码实现
-
-
- 第一步:确定请求路径和参数
-
(1)确定请求参数
请求参数是在portal系统中定义的。
结论:请求参数名为q,值为搜索文本框输入的关键词。(默认分页)
(2)确定返回值类型
搜索结果是在protal工程中渲染的。
-
-
- 第二步:创建SearchService接口及其实现类
-
(1)修改resourse.properties配置文件
#搜索服务 SEARCH_BASE_URL=http://localhost:8083/search SEARCH_ITEM_URL=/doSearch |
(2)创建SearchService接口及其实现类,定义搜索方法
@Service public class SearchServiceImpl implements SearchService{ @Value("${SEARCH_BASE_URL}") private String SEARCH_BASE_URL; @Value("${SEARCH_ITEM_URL}") private String SEARCH_ITEM_URL;
@Override public SearchResult query(String q, Integer page) {
SearchResult result = null; Map<String,String> params = new HashMap<>(); params.put("keyword", q); params.put("page", page+""); String jsonData = HttpClientUtils.doGet(SEARCH_BASE_URL+ SEARCH_ITEM_URL, params); if(null!=jsonData){ result = JsonUtils.jsonToPojo(jsonData, SearchResult.class); } return result; } } |
-
-
- 第二步:创建SearchController类
-
@Controller public class SearchController {
@Autowired private SearchService searchService;
@RequestMapping("/search") public String query(String q,@RequestParam(defaultValue="1")Integer page,ModelMap map){ SearchResult result = searchService.query(q, page); map.put("query", q); map.put("totalPages", result.getTotalPages()); map.put("itemList", result.getItemList()); map.put("page", page);
return "search"; } } |
-
-
- 第三步:解决多张图片显示问题
-
说明:商品是可以有多张图片的。搜索结果中默认展示第一张图片。
修改SearchItem类,新增String[] images属性。只需要get方法。
private String[] images;
public String[] getImages() { if(null!=this.image){ images = image.split(","); } return images; } |