基于Spring+SpringMVC+MyBatis博客系统的开发教程(十四)

第14课:首页搜索功能(Solr)

Solr 概述

Solr 是一个独立的企业级搜索应用服务器,它对外提供类似于 Web Service 的 API 接口。用户可以通过 HTTP 请求,向搜索引擎服务器提交一定格式的 XML 文件,生成索引;也可以通过 HTTP Get 操作提出查找请求,并得到 XML 格式的返回结果。

它具有以下特点(来自百度百科):

Solr是一个高性能,采用 Java 5 开发,基于 Lucene 的全文搜索服务器。同时对其进行了扩展,提供了比 Lucene 更为丰富的查询语言,同时实现了可配置、可扩展,并对查询性能进行了优化,且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。

我们使用将 Solr 达到什么样的效果呢?

首先能根据搜索条件进行分词,查询出需要的结果,然后将分词高亮显示。

比如查询:“SSM 框架实战”,分词的结果可能是:“SSM 框架 实战”或者“S S M 框 架 实战”等。

我们将根据分词的结果去数据库查询,将查询结果以高亮显示在页面。

本文主要分三部分来介绍:

  1. Solr 单元测试;
  2. Solr 在项目中的应用;
  3. Tomcat 服务器运行 Solr。

Solr单元测试

Solr 单元测试步骤如下:

  1. 开启 Solr 服务;
  2. 添加 Java 操作 Solr 的依赖;
  3. 编写测试代码。
下载和启动 Solr
下载 Solr

我将 Solr 压缩包放在本课程最后的百度网盘中,大家也可从 Solr 官网中下载.

等待3秒自动跳转到下载页面,如图:

我下载的时候版本是 7.3.1。

点击链接进入后总共有三个版本包:

  1. solr-7.3.1-src.tgz 含有 Lucene 源码 tgz 包;
  2. solr-7.3.1.tgz 不含源码 tgz 包;
  3. solr-7.3.1.zip 不含源码 zip 包;

我们下载后面两个,tgz 包用于 Linux 系统,zip 包用于 Windows 系统。

启动 Solr

下载完成后,将 zip 后缀压缩包解压到自定义目录,进入到 solr-7.3.1\bin 目录下,shift+鼠标右键,点击在此处打开命令窗口,如果是 Windows 10 系统先 shift+鼠标右键打开 PowerShell 窗口,然后输入 start cmd,打开命令窗口,在命令窗口中输入:

    solr start

启动 Solr 服务,如图:

访问端口为8983,我们可以通过 localhost:8983 或者 127.0.0.1:8983 访问 Solr 网页。

进入网页后发现提示“no cores avaiable”,即没有可用的内核,其实就是没有 Solr 的索引库,如图:

那我们就创建一个 core,还是在刚才的命令窗口中输入:solr create -c mycore。其中 mycore 为 core 的名字,如图

在 solr-7.3.1\server\solr 目录下发现多一个文件夹 mycore,然后再重新进入 Solr 网页查看,发现已经有了我们刚才创建的 mycore。选择mycore,点击后出现一个列表,点击 Query,执行 Execute Query,查询出了一条数据,这是 Solr 默认的。

补充:Solr 重启命令

   solr restart -p 8983

-p 后面指定端口号

添加 Java 操作 Solr 的依赖——SolrJ

Java 就是通过 Solrj 来操作 Solr 的。它提供了一些增、删、改、查的方法。使用起来很方便。

在 pom.xml 中引入依赖 solr-solrj
    <dependency>
      <groupId>org.apache.solr</groupId>
      <artifactId>solr-solrj</artifactId>
      <version>7.3.0</version>
    </dependency>

通过 Solr 客户端对象调用相关方法,操作 Solr,测试类中会介绍。

Solr 与 Spring 的整合

在 src/main/resources 目录下新建 applicationContext-solr.xml,主要是将 Solr 客户端对象交给 Spring 来管理,配置内容如下:

  <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="httpSolrClient" class="org.apache.solr.client.solrj.impl.HttpSolrClient">
        <constructor-arg name="builder" value="http://localhost:8983/solr/mycore"/>
    </bean>

    </beans>

通过 Ctrl+鼠标右键点击 HttpSolrClient,查看源码。Ctrl+F 搜索 Builder,发现它有一个构造方法,接收的参数名正是 builder,这里就是构造方法注入,它的参数 name="builder" 是固定格式,value 就是 builder 对应的值,是一个 solrCore 的远程地址,mycore 就是我们刚才创建的。

编写测试代码

单元测试代码都放在了 src/test 目录下。

在 test/java 目录下新建 TestSolrJ.java

代码如下:

@ContextConfiguration(locations = {"classpath:applicationContext-solr.xml"})
    public class TestSolrJ extends AbstractJUnit4SpringContextTests {
    @Autowired
    private SolrClient solrServer;

    @Test
    public void testSave() throws Exception {   

        //1.创建一个文档对象
        SolrInputDocument inputDocument = new SolrInputDocument();
        inputDocument.addField( "id", "32" );
        inputDocument.addField( "item_title", "ssm项目开发实战" );
        inputDocument.addField( "item_image", "www.ssm.png" );
        inputDocument.addField( "author", "wly" );
        //2.将文档写入索引库中
        solrServer.add( inputDocument );
        //3.提交
        solrServer.commit();
    }
    }

代码解读如下:

(1)@ContextConfiguration 注解表示加载配置文件,就是我们之前配置的 Solr 与 Spring 的整合配置文件,如果有多个配置文件用逗号隔开,如:

locations = {"classpath:applicationContext-solr.xml","classpath:spring-mvc.xml"}

(2)测试类要继承 AbstractJUnit4SpringContextTests 测试基类,否则注入不了对象。

(3)通过 @Autowired 注解注入 Solr 客户端对象 SolrClient。

(4)@Test注解,代表这是一个测试方法。

(5)创建文档对象 SolrInputDocument。

(6)向文档中添加字段和对应的值。

(7)通过客户端对象的 add 方法将文档写入索引库中。

(8)提交(一定要提交,否则添加不成功)。

启动单元测试,点击 testSave,右键选择 Run testSave,启动完成后再次访问 Solr 网页。

执行 Execute Query 发现多了一条数据,就是刚才我们插入的,如下图:

Solr 单元测试完成,通过单元测试说明操作 Solr 成功!

但是发现一个问题,查询出来的结果除了 id 都是 ArrayList 集合形式 [],如果取值的话转换起来会很麻烦。怎么解决这个问题呢?

主要就是对 solr-7.3.1\server\solr\mycore\conf\ 下的配置文件managed-schema进行修改,配置对应的字段以及字段类型,如下:

    <field name="id" type="string" multiValued="false" indexed="true" required="true" stored="true"/>
    <field name="item_image" type="string" stored="false" docValues="false"/>
    <field name="item_title" type="string" />
    <field name="item_content" type="string"/>
    <field name="author" type="string"/>

之前对应的 type="text_general" 是 Solr 自动生成的,其中

  • name:字段的名字;
  • type:字段的数据类型;
  • multiValued:是否有多值,有多值时设置为 true,否则设置为 false;
  • indexed:是否创建索引;
  • required:是否必须;
  • stored:是否存储数据,如果设置为 false 则不存储,结合 docValues="false" 使得查询不返回结果。

将 item_image 字段设置 stored="false" 和 docValues="false" 进行测试。

在命令窗口输入:

    solr restart -p 8983

重启 Solr,重启成功后刷新网页,点击 Documents,然后选择 XML,在Document(s) 文本域内输入:

    <delete><id>33</id></delete>

然后点击按钮 Submit Document,删除 id=33 的数据,如图:

点击 Query -> Execute Query 查看,数据已删除。

然后再运行单元测试,执行 Execute Query 查看结果,如下,需要的字段已不包含中括号并且 item_image 字段没有返回查询结果,如图:

其中 item_content_str 和 author_str 等是复制域,在配置文件 managed-schema 的最后可以找到,它可以将某一个 Field 中的数据复制到另一个域中,比如将 item_content 中的数据复制到 item_content_str 中。

当两个域都有可能包含某个关键字,不知道查询的结果在哪个域中的时候,可以将这两个域中的内容复制到一个复制域中,从复制域中进行查找,我们将配置文件中的 item_title 和 item_content 都复制到 item_title_str 域中进行测试,配置如下:

    <copyField source="item_title" dest="item_title_str" maxChars="256"/>
    <copyField source="item_content" dest="item_title_str" maxChars="256"/>

重启 Solr,将测试类中的 id 改为34,然后运行单元测试,查看结果如下:

id=34 的 item_title_str 复制域中包含了 item_title 和 item_content 两个域中的内容。

接下来就是将 Solr 应用到项目中,通过 Solr 搜索并将结果高亮显示。

Solr 在项目中的应用

IKAnalyzer 分词器

Solr 可通过自带的分词器 smartcn 或者第三方分词器 IKAnalyzer 来实现,IKAnalyzer 分词效果较好,所以这里使用 IKAnalyzer 分词器。

使用 IKAnalyzer 分词器需要以下几个步骤:

1. 下载 IKAnalyzer 分词器 Jar 包,我已将 ikanalyzer-solr5.zip 文件放入了百度网盘中。

2. 压缩包解压后,将其中的两个 Jar 包放入 solr-7.3.1\server\solr-webapp\webapp\WEB-INF\lib 下。

3. 配置 solr-7.3.1\server\solr\mycore\conf 下的 managed-schema 文件,添加分词器和需要分词的字段。

    <!-- 添加ik分词器 -->
    <fieldType name="text_ik" class="solr.TextField"> 
      <analyzer type="index" isMaxWordLength="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/> 
      <analyzer type="query" isMaxWordLength="true"  class="org.wltea.analyzer.lucene.IKAnalyzer"/> 
    </fieldType>

type="index" 代表创建索引时分词,type="query" 代表查询时分词。

    <!-- 需要分词的字段 -->
    <field name="title" type="text_ik" indexed="true" stored="true" required="true" multiValued="false" />

我们对文章标题进行分词查询。

4. 配置其它字段名和类型,否则会查询出集合类型的数据。

    <field name="comment_num" type="string"/>
    <field name="downvote" type="string"/>
    <field name="upvote" type="string"/>
    <field name="nick_name" type="string"/>
    <field name="img_url" type="string"/>
    <field name="rpt_time" type="pdate"/>
    <field name="content" type="text_general"/>
    <field name="category" type="string"/>
    <field name="u_id" type="string"/>
    <field name="personal" type="string"/>

注意:content 字段不能设置为 String 类型,否则内容过多会超出范围,报异常。除了日期和 content 字段外其它都设置为 String类型。

5. 重新启动 Solr。

代码如下:

    solr restart -p 8983

6. 刷新网页,点击 Analysis,输入查询内容,选择分词字段,然后点击 Analyse Values 开始分词,查看结果,如下图:

其中:

  • Field Value (Index):索引分词的值;
  • Field Value (Query):查询分词的值。
在 web.xml 中引入 applicationContext-solr.xml 配置文件

代码如下:

     <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      classpath:spring-mybatis.xml,
      classpath:applicationContext-redis.xml,
      classpath:applicationContext-activemq.xml,
      classpath:applicationContext-solr.xml
    </param-value>
    </context-param>
创建 SolrService 接口

在 wang.dreamland.www.service 包下新建 SolrService 接口,接口方法如下:

    public interface SolrService {
    /**
     * 根据关键字搜索文章并分页
     * @param keyword
     * @return
     */
     Page<UserContent> findByKeyWords(String keyword, Integer pageNum, Integer pageSize);
    }

提供根据关键字搜索文章并分页的接口方法。

创建 SolrServiceImpl 实现类

代码如下:

@Autowired
    HttpSolrClient solrClient;
    @Override
     public Page<UserContent> findByKeyWords(String keyword, Integer pageNum, Integer pageSize) {
        SolrQuery solrQuery = new SolrQuery( );
        //设置查询条件
        solrQuery.setQuery( "title:"+keyword );
        //设置高亮
        solrQuery.setHighlight( true );
        solrQuery.addHighlightField( "title" );
        solrQuery.setHighlightSimplePre( "<span style='color:red'>" );
        solrQuery.setHighlightSimplePost( "</span>" );

        //分页
        if (pageNum == null || pageNum < 1) {
            pageNum = 1;
        }
        if (pageSize == null || pageSize < 1) {
            pageSize = 7;
        }
        solrQuery.setStart( (pageNum-1)*pageSize );
        solrQuery.setRows( pageSize );
        solrQuery.addSort("rpt_time", SolrQuery.ORDER.desc);
        //开始查询

        try {
            QueryResponse response = solrClient.query( solrQuery );
            //获得高亮数据集合
            Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
            //获得结果集
            SolrDocumentList resultList = response.getResults();
            //获得总数量
            long totalNum = resultList.getNumFound();
            List<UserContent> list = new ArrayList<UserContent>(  );
            for(SolrDocument solrDocument:resultList){
                //创建文章对象
                UserContent content = new UserContent();
                //文章id
                String id = (String) solrDocument.get( "id" );
                Object content1 = solrDocument.get( "content" );
                Object commentNum = solrDocument.get( "comment_num" );
                Object downvote = solrDocument.get( "downvote" );
                Object upvote = solrDocument.get( "upvote" );
                Object nickName = solrDocument.get( "nick_name" );
                Object imgUrl = solrDocument.get( "img_url" );
                Object uid = solrDocument.get( "u_id" );
                Object rpt_time = solrDocument.get( "rpt_time" );
                Object category = solrDocument.get( "category" );
                Object personal = solrDocument.get( "personal" );
                //取得高亮数据集合中的文章标题
                Map<String, List<String>> map = highlighting.get( id );
                String title = map.get( "title" ).get( 0 );

                content.setId( Long.parseLong( id ) );
                content.setCommentNum( Integer.parseInt( commentNum.toString() ) );
                content.setDownvote( Integer.parseInt( downvote.toString() ) );
                content.setUpvote( Integer.parseInt( upvote.toString() ) );
                content.setNickName( nickName.toString() );
                content.setImgUrl( imgUrl.toString() );
                content.setuId( Long.parseLong( uid.toString() ) );
                content.setTitle( title );
                content.setPersonal( personal.toString() );
                Date date = (Date)rpt_time;
                content.setRptTime(date);
                List<String> clist = (ArrayList)content1;
                content.setContent( clist.get(0).toString() );
                content.setCategory( category.toString() );
                list.add( content );
            }
            PageHelper.startPage(pageNum, pageSize);//开始分页
            PageHelper.Page page = PageHelper.endPage();//分页结束
            page.setResult(list);
            page.setTotal(totalNum);
            return page;
        } catch (SolrServerException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

代码解读如下:

(1)@Service 注解代表这是 Service 层。

(2)通过 @Autowired 注解,注入 Solr 客户端对象 HttpSolrClient。

(3)新建 SolrQuery 对象,然后设置查询条件,title 为要查询的字段,keyword 为要查询的字段值。

(4)高亮显示必须设置 highlight 属性为 true。

(5)设置高亮的字段 tilte。

(6)设置高亮的标签头,可看出 style 的 color 属性值为 red,即红色高亮显示,也可自定义别的颜色。

(7)设置高亮的标签尾。

(8)分页,设置开始索引和每页显示记录数,按照时间倒序。

(9)Solr 客户端对象根据查询条件查询 Solr 索引库,将查询结果放入 QueryResponse 对象中。

(10)下面就是根据 QueryResponse 对象获取高亮结果集和所有数据结果集,然后遍历所有数据结果集,获取 Solr 索引库中的数据,将数据设置到文章对象中;然后遍历高亮结果集,主要是 title 属性,将 title 设置到文章对象中,每遍历一次将结果添加到 list 集合,这样 list 集合就包含了所有的查询结果。

(11)使用 PageHelper 分页工具开始分页,将 list 集合和总记录数等设置到 Page 中,最后将 page 对象返回。

(12)如果发生异常则返回 null。

这样 Service 层方法就写完了,等待 Controller 层的调用。

事件源

index.jsp 页面的搜索对应的 form 表单,如下:

      <form method="post" action="${ctx}/index_list"  id="indexSearchForm"  class="navbar-form navbar-right" role="search" style="margin-top: -35px;margin-right: 10px">
            <div class="form-group">
                <input type="text" id="keyword" name="keyword" value="${keyword}" class="form-control" placeholder="搜索">
            </div>
            &nbsp; &nbsp;<i onclick="searchForm();" class="icon icon-search" style="color: white"></i>
        </form>

action 对应的映射 URL 为:/index_list,搜索对应的点击事件 searchForm 方法如下:

    //搜索
    function searchForm(){
        var keyword =  $("#keyword").val();
        if(keyword!=null && keyword.trim()!=""){
            $("#indexSearchForm").submit();
        }
    }

如果查询条件不为空,则提交表单。

在 IndexJspController 中修改映射 URL 为 index_list 的方法

修改如下:

    @RequestMapping("/index_list")
    public String findAllList(Model model, @RequestParam(value = "keyword",required = false) String keyword,
                                    @RequestParam(value = "pageNum",required = false) Integer pageNum ,
                                    @RequestParam(value = "pageSize",required = false) Integer pageSize) {
        log.info( "===========进入index_list=========" );
        User user = (User)getSession().getAttribute("user");
        if(user!=null){
            model.addAttribute( "user",user );
        }
        if(StringUtils.isNotBlank(keyword)){
            Page<UserContent> page = solrService.findByKeyWords( keyword ,pageNum,pageSize);
            model.addAttribute("keyword", keyword);
            model.addAttribute("page", page);
        }else {
            Page<UserContent> page =  findAll(pageNum,pageSize);
            model.addAttribute( "page",page );
        }
        return "../index";
    }

代码解读如下:

(1)从 Session 获取用户信息,如果不为 null 则添加到 model 中。

(2)如果关键字不为空则根据关键字进行 Solr 高亮搜索,并将返回结果 page 添加到 model 中,将关键字 keyword 也添加到 model 中,用于回显和分页查询条件。

(3)如果关键字为空则进行普通的分页查询,将 page 添加到 model 中。

(4)最后返回到 index.jsp 页面。

运行 Tomcat,输入查询条件后,发现什么都没有,跟我们预期的结果不一样?

这是因为 Solr 索引库中现在还没有数据,所以什么都没查到。我们需要在添加、修改和删除文章时将信息同步到 Solr 索引库中,数据发生变化,Solr 索引库要跟着变化,否则会出现数据不统一的现象。

同步信息到 Solr 索引库

1. 在 SolrService 接口中增加增、删、改方法,如下:

 /**
     * 添加文章到solr索引库中
     * @param userContent
     */
    void addUserContent(UserContent userContent);

    /**
     * 根据solr索引库
     * @param userContent
     */
    void updateUserContent(UserContent userContent);

    /**
     * 根据文章id删除索引库
     * @param userContent
     */
    void deleteById(Long id);

2. 在 SolrServiceImpl 中实现上述方法:

 @Override
    public void addUserContent(UserContent cont) {
        if(cont!=null){
            addDocument(cont);
        }
    }

    @Override
    public void updateUserContent(UserContent cont) {
        if(cont!=null){
            addDocument(cont);
        }
    }

    @Override
    public void deleteById(Long id) {
        try {
            solrClient.deleteById(id.toString());
            solrClient.commit();
        } catch (SolrServerException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void addDocument(UserContent cont){
        try {
            SolrInputDocument inputDocument = new SolrInputDocument();
            inputDocument.addField( "comment_num", cont.getCommentNum() );
            inputDocument.addField( "downvote", cont.getDownvote() );
            inputDocument.addField( "upvote", cont.getUpvote() );
            inputDocument.addField( "nick_name", cont.getNickName());
            inputDocument.addField( "img_url", cont.getImgUrl() );
            inputDocument.addField( "rpt_time", cont.getRptTime() );
            inputDocument.addField( "content", cont.getContent() );
            inputDocument.addField( "category", cont.getCategory());
            inputDocument.addField( "title", cont.getTitle() );
            inputDocument.addField( "u_id", cont.getuId() );
            inputDocument.addField( "id", cont.getId());
            inputDocument.addField( "personal", cont.getPersonal());
            solrClient.add( inputDocument );
            solrClient.commit();
        } catch (SolrServerException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

主要是通过 Solr 客户端对象 SolrCient 操作 Solr,这里的添加和更新方法一样,id 存在时就是根据 id 更新字段。还有就是别忘了 commit 提交。

3. 在 WriteController 中更新如下:

 if(cid ==null){
            userContent.setUpvote( 0 );
            userContent.setDownvote( 0 );
            userContent.setCommentNum( 0 );
            userContentService.addContent( userContent );
            solrService.addUserContent(userContent);

        }else {
            userContentService.updateById(userContent);
            solrService.updateUserContent(userContent);
        }

在添加和更新文章之后分别同步 Solr 索引库信息。注意记得注入 SolrService 对象。

4. 在 PersonalController 中更新如下:

    userContentService.deleteById(cid);
    solrService.deleteById(cid);

在删除文章的时候删除索引库对应的信息。注意记得注入 SolrService 对象。

首页关键词搜索高亮显示

现在 Solr 索引库没有数据,我们直接在单元测试中把所有文章都查询出来,然后批量添加到 Solr 索引库中。

在单元测试类 TestSolrJ 中新建方法 testSaveAll,并将所需要的配置文件都进行加载:

    @ContextConfiguration(locations = {"classpath:applicationContext-redis.xml","classpath:spring-mybatis.xml","classpath:applicationContext-activemq.xml","classpath:applicationContext-solr.xml"})    

具体方法如下:

    @Autowired
    private UserContentService userContentService;
    @Test
    public void testSaveAll() throws IOException, SolrServerException {
        List<UserContent> list = userContentService.findAll();
        if(list!=null && list.size()>0){
            for (UserContent cont : list){
                SolrInputDocument inputDocument = new SolrInputDocument();
                inputDocument.addField( "comment_num", cont.getCommentNum() );
                inputDocument.addField( "downvote", cont.getDownvote() );
                inputDocument.addField( "upvote", cont.getUpvote() );
                inputDocument.addField( "nick_name", cont.getNickName());
                inputDocument.addField( "img_url", cont.getImgUrl() );
                inputDocument.addField( "rpt_time", cont.getRptTime() );
                inputDocument.addField( "content", cont.getContent() );
                inputDocument.addField( "category", cont.getCategory());
                inputDocument.addField( "title", cont.getTitle() );
                inputDocument.addField( "u_id", cont.getuId() );
                inputDocument.addField( "id", cont.getId());
                inputDocument.addField( "personal", cont.getPersonal());
                solrServer.add( inputDocument );
            }
        }
        solrServer.commit();
    }

上面代码将所有文章查询出来之后进行遍历,然后将每篇文章的信息都添加到文档对象中,然后通过 Solr 客户端对象将其添加到 Solr 索引库中,最后提交。

现在 Solr 索引库中已经有数据了,重新启动 Tomcat,然后输入查询条件进行搜索,比如搜索“Solr 高亮显示功能”,查询结果如下图:

先对搜索条件进行分词,然后根据分词后的结果进行查询并将结果高亮显示。

最后,如果查询结果不止一页,想要点击下一页或者指定页码后还能高亮显示,则要将查询条件带上,如下:

<div id="page-info" style="position: absolute;width:900px;background-color: #EBEBEB;height: 80px;left: 0px;">
                <ul class="pager pager-loose">
                    <c:if test="${page.pageNum <= 1}">
                        <li><a href="void(0);">« 上一页</a></li>
                    </c:if>
                    <c:if test="${page.pageNum > 1}">
                        <li class="previous"><a href="${ctx}/index_list?pageNum=${page.pageNum-1}&&keyword=${keyword}">« 上一页</a></li>
                    </c:if>
                    <c:forEach begin="${page.startPage}" end="${page.endPage}" var="pn">
                        <c:if test="${page.pageNum==pn}">
                            <li class="active"><a href="void(0);">${pn}</a></li>
                        </c:if>
                        <c:if test="${page.pageNum!=pn}">
                            <li ><a href="${ctx}/index_list?pageNum=${pn}&&keyword=${keyword}">${pn}</a></li>
                        </c:if>
                    </c:forEach>

                    <c:if test="${page.pageNum>=page.pages}">
                        <li><a href="void(0);">下一页 »</a></li>
                    </c:if>
                    <c:if test="${page.pageNum<page.pages}">
                        <li><a href="${ctx}/index_list?pageNum=${page.pageNum+1}&&keyword=${keyword}">下一页 »</a></li>
                    </c:if>

                </ul>
            </div>

在请求 URL 上拼接 keyword:

    href="${ctx}/index_list?pageNum=${page.pageNum-1}&&keyword=${keyword}"

Tomcat 服务器运行 Solr

上面的 Solr 是在 Jetty 服务器下运行的,下面介绍 Solr 如何在 Tomcat 服务器上运行。

准备

1. 将 solr-7.3.1\server\solr-webapp 下的 webapp 文件复制到 Tomcat 的 webapps 目录下,并重命名为 Solr,如下图:

2. 将 solr-7.3.1\server\lib\ext 目录下的所有 Jar 包复制到刚才重命名的 solr\WEB-INF\lib下。

3. 将 solr-7.3.1\server\lib 下以 metrics 开头的5个 Jar 包也复制到刚才重命名的 solr\WEB-INF\lib 下。

4. 在刚才重命名的 solr\WEB-INF 下新建 classes 文件夹,并将 solr-7.3.1\server\resources下的 log4j.properties 文件复制到刚刚新建的 classes 文件夹下。

5. 将 solr-7.3.1\server 目录下的 Solr 文件夹复制到自定义路径下,我这里直接放在了 D:/ 下,然后重命名为 solr_home

6. 修改之前重命名的 solr\WEB-INF 下的 web.xml 文件。

找到如下图的注释,将其解开,并将路径替换成自己刚才重命名的 solr_home 的路径,如下

<env-entry>
   <env-entry-name>solr/home</env-entry-name>
   <env-entry-value>D:/solr_home</env-entry-value>
   <env-entry-type>java.lang.String</env-entry-type>
</env-entry>

然后将最下面的 security-constraint 标签内的内容注释掉,如下图:

启动 Tomcat

先介绍下 Tomcat 下的 ROOT 文件夹和 Solr 文件夹。

以 ROOT 命名的文件夹,Tomcat 启动后可以通过 localhost:8080 直接访问项目,不用加项目名。

而其他要部署的项目如果不是以 ROOT 命名,比如 Solr,访问的时候需要通过 localhost:8080/solr 的方式访问,即需要加上项目名。

接下来把我们的 dreamland-web 项目打 War包,我是直接用 Maven 命令 install 并跳过测试,如下图:

如果出现 BUILD SUCCESS 即代表成功,然后在 dreamland\dreamland-web\target 目录下可以找到 dreamland-web.war 文件,这就是我们刚刚打包好的 War 包。

紧接着,我们将 dreamland-web.war 复制到 Tomcat 的 webapps 目录下,并重命名为 ROOT.war,将原来的 ROOT 文件夹删除,启动 Tomcat 后,会自动将 ROOT.war 文件解压。

启动Tomcat之前,需先启动 Redis 和 ActiveMQ,然后进入 Tomcat 安装目录下的 bin 目录,我的是:D:\softs\java\apache-tomcat-8.5.9\bin,双击 startup.bat 文件启动 Tomcat,此时就可以通过下面链接访问 Solr 网页了:

http://localhost:8080/solr/index.html#/

访问我们自己的 Web 项目就是:http://localhost:8080,如果想通过 Solr 高亮搜索,则要将 applicationContext-solr.xml 中的端口修改成8080,然后重新打包部署。

     <constructor-arg name="builder" value="http://localhost:8080/solr/mycore"/>

第14课百度网盘地址:

链接:https://pan.baidu.com/s/18rDZpLx-3x-4iOSoyEqxqw 密码:y192

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
(写在前面的话,评论中说的图片显示问题解决了,原因是文件被拦截了,感谢帮忙解决问题的同学!) 基于SSM(spring+springMVC+MyBatis)技术开发的仓库管理系统,是我这学期期末综合课设项目,新鲜热乎,用的是:eclipse+Tomcat8+mysql+jdk1.8,里面有项目源文件直接导入即可,还有数据库sql文件,还有课程设计报告。系统的功能都好用,如果有啥疑问可以跟我私信。 推荐一下这个课设的姊妹篇-------------基于SSH(struts-spring-hibernate)开发的学生成绩管理系统,https://download.csdn.net/download/qq_33654685/10850113 本系统为仓库管理系统系统功能较为基础,同时操作又比较简单。系统开发的总体任务是实现基础的供应商管理模块、经销商管理模块、商品管理模块、库存管理模块、订货单管理模块、出库单管理模块。 供应商管理模块:为管理员提供实现对供应商信息的管理维,包括对供应商的查询、添加、修改和删除等操作。 经销商管理模块:为管理员提供实现对经销商信息的管理维,包括对供应商的查询、添加、修改和删除等操作。 商品管理模块:为管理员提供实现对商品信息的管理维,包括对商品的查询、添加、修改和删除出库等操作; 库存管理模块:为管理员提供实现对库位信息的管理维,包括对库位信息的查询、添加、修改和删除等操作; 订货单管理模块:为管理员提供实现对订货单信息的管理维,包括对订货单信息的查询、添加、修改和删除等操作; ---------------------------------------------分割线--------------- 评论中说的图片显示问题解决了,原因是文件被拦截了,css调用的时候掉不到资源,自带的字体库找不到,调用不了,就变成那个方框框了 需要在web.xml配置下就行,加入以下代码。感谢帮忙解决问题的同学! default *.svg default *.eot default *.woff default *.ttf
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

exodus3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值