美文网首页
SSM与SpringBoot整合solr

SSM与SpringBoot整合solr

作者: 拼搏男孩 | 来源:发表于2020-04-28 21:02 被阅读0次

1、solrj介绍

SolrJ是操作Solr的JAVA客户端,它提供了增加、修改、删除、查询Solr索引的JAVA接口。SolrJ针对 Solr提供了Rest 的HTTP接口进行了封装, SolrJ底层是通过使用httpClient中的方法来完成Solr的操作。HttpClient 是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。学过python爬虫的应该都知道requests库,这个库可以很简单的通过几行代码就构造出一个HTTP请求,并拿到HTTP响应,当然,解析需要另外的库。

2、SSM整合solr

2.1 添加依赖

<dependency>
    <groupId>org.apache.solr</groupId>
    <artifactId>solr-solrj</artifactId>
    <version>8.5.1</version>
</dependency>

要想使用solrj就必须要添加solr-solrj的依赖,由于我的solr版本是8.5.1,所以我添加的依赖版本是8.5.1,具体以你的solr版本为准。

2.2 配置文件

spring-solr.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--
Spring整合Solr
 -->
    <context:property-placeholder location="classpath:db.properties"/>
    <bean id="httpSolrClient" class="org.apache.solr.client.solrj.impl.HttpSolrClient">
        <constructor-arg name="builder" value="${solrUrl}"/>
    </bean>

</beans>

这个配置文件通过IoC的方式注入了HttpSolrClient这个bean,创建关闭对象都交给spring了,我们不用操心了。不过要想这个生效,把这个文件在spring-mvc.xml中导入。

db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/solr
user=root
pass=huwenlong
solrUrl=http://localhost:8081/solr/core_demo

这个properties文件定义了mysql数据库以及solrUrl的信息。

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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="suffix" value=".jsp"/>
    </bean>
    <context:component-scan base-package="com.qianfeng.controller"/>
    <mvc:annotation-driven>
        <!-- 使用fastjson来替代默认的HttpMessageConverter以提高执行效率-->
        <mvc:message-converters>
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <!-- 设置UTF-8编码防止乱码-->
                    <list>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>

            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <mvc:default-servlet-handler/>
    <import resource="classpath:spring-mybatis.xml"/>
    <import resource="classpath:spring-solr.xml"/>
</beans>

这个配置文件设置了FastJson来代替默认的json转换器,因为我们直接返回json字符串,前后端分离。

2.3 给entity添加注解

Products.java

package com.qianfeng.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.solr.client.solrj.beans.Field;

import java.util.Date;
import java.io.Serializable;

/**
 * (Products)实体类
 *
 * @author makejava
 * @since 2020-04-27 17:51:03
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Products implements Serializable {
    private static final long serialVersionUID = 355132763315683774L;
    /**
    * 商品编号
    */

    private Integer pid;
    @Field("id")
    private String id;
    /**
    * 商品名称
    */
    @Field("prod_pname")
    private String pname;
    /**
    * 商品分类ID
    */
    private Integer catalog;
    /**
    * 商品分类名称
    */
    @Field("prod_catalog_name")
    private String catalogName;
    /**
    * 价格
    */
    @Field("prod_price")
    private Double price;
    /**
    * 数量
    */
    private Integer number;
    /**
    * 商品描述
    */
    @Field("prod_description")
    private String description;
    /**
    * 图片名称
    */
    @Field("prod_picture")
    private String picture;
    /**
    * 上架时间
    */
    private Date releaseTime;

}

managed-schema

...  
<field name="prod_pname" type="text_ik" indexed="true" stored="true" required="true"  /> 
  <field name="prod_catalog_name" type="string" indexed="true" stored="true" required="true"  /> 
  <field name="prod_price" type="pdouble" indexed="true" stored="true" required="true"  /> 
  <field name="prod_description" type="text_ik" indexed="true" stored="true" required="true"  /> 
  <field name="prod_picture" type="string" indexed="false" stored="true" required="true"  />
...

由于我在solr_home下的core_demo下的managed-schema文件中自定义五个字段,和数据库中的字段名不一致,所以使用@Field注解将字段对应起来,注意,对应起来的两个字段必须类型一致。

2.4 编写测试类

SolrTest.java

package com.qianfeng.service;

import com.qianfeng.entity.Products;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.StringUtils;
import org.apache.solr.common.params.SolrParams;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Map;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-mybatis.xml")
public class SolrTest {
    @Resource
    private HttpSolrClient solrClient;
    @Value("${solrUrl}")
    private String solrUrl;

    /**
     * 每条索引都有一个唯一的id,如果一个Document的id存在,则执行修改操作,如果不存在,执行添加操作
     * @throws IOException
     * @throws SolrServerException
     */
    @Test
    public void testSaveOrUpdate() throws IOException, SolrServerException {
        //HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
        Products p = new Products();
        p.setPid(999999);
        p.setPname("后台测试solr");
        p.setCatalogName("测试类别");
        p.setPrice(11.1);
        p.setDescription("测试测试");
        p.setPicture("bbb.jpg");
        UpdateResponse response = solrClient.addBean(p);
//        solrClient.commit();
//        solrClient.close();
    }

    /**
     * 删除索引
     * @throws IOException
     * @throws SolrServerException
     */
    @Test
    public void testDelete() throws IOException, SolrServerException {
        //HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
        solrClient.deleteById("999999");

        //solrClient.deleteByQuery("*:*");先查询后删除,这个意思是删除全部
        //solrClient.deleteByQuery("prod_price:[20 TO *");
//        solrClient.commit();
//        solrClient.close();
    }
    @Test
    public void testSimpleQuery() throws IOException, SolrServerException {
        HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
        //传递查询参数
        SolrParams solrParams = new SolrQuery("*:*");
        //进行查询,返回查询响应
        QueryResponse response = solrClient.query(solrParams);
        //建立映射
        List<Products> products = response.getBeans(Products.class);
        System.out.println(products.size());
        for (Products product : products) {
            System.out.println(product);
        }
//        solrClient.commit();
//        solrClient.close();
    }
    @Test
    public void testComplexQuery() throws IOException, SolrServerException {
        //HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
        String keyWord = "手机";
        SolrQuery solrQuery = new SolrQuery();
        //关键字查询,查询商品名称
        if(StringUtils.isEmpty(keyWord)){
            solrQuery.set("q","*");
        }else {
            solrQuery.set("q",keyWord);
        }
        String categoryName = "";
        //对类别进行筛选
        if(!StringUtils.isEmpty(categoryName)){
            solrQuery.addFilterQuery("prod_catalog_name:"+categoryName);
        }
        //对价格进行筛选,从前台传过来的参数形如price=10-100或price=-100或price=10-
        String priceStr = "";
        if(!StringUtils.isEmpty(priceStr)){
            String fq = "";
            String[] split = priceStr.split("-");
            if(split.length==1){
                fq = "prod_price:["+split[0]+" TO *]";
            }else if (split.length==2){
                String prefix = split[0];
                if(StringUtils.isEmpty(split[0])){
                    prefix = "*";
                }
                fq = "prod_price:["+prefix+" TO "+split[1]+"]";
            }
            solrQuery.addFilterQuery(fq);
        }
        //对价格进行排序,升序与降序,默认是0,1代表升序,2代表降序
        int priceSort = 1;
        if(priceSort==1){
            solrQuery.addSort("prod_price", SolrQuery.ORDER.asc);
        }else if(priceSort==2){
            solrQuery.addSort("prod_price", SolrQuery.ORDER.desc);
        }

        //从前台获取当前页
        int currentPage = 1;
        //从前台获取每页条目数
        int pageSize = 5;
        //就算开始的索引
        int start = pageSize*(currentPage-1);
        solrQuery.setStart(start);
        solrQuery.setRows(pageSize);

        //设置查询哪些字段
        //solrQuery.setFields("prod_catalog_name","prod_pname");

        //设置默认字段,就不再需要在q中指定prod_pname了
        solrQuery.set("df","prod_pname");

        //设置高亮
        //启用高亮
        solrQuery.setHighlight(true);
        //指定高亮字段
        solrQuery.addHighlightField("prod_pname");
        //指定高亮字段前面的字符
        solrQuery.setHighlightSimplePre("<font color='red'");
        //指定高亮字段后面的字符
        solrQuery.setHighlightSimplePost("</font>");
        QueryResponse response = solrClient.query(solrQuery);
        List<Products> products = response.getBeans(Products.class);
        Map<String, Map<String, List<String>>> map = response.getHighlighting();
        System.out.println(products.size());
        for (Products product : products) {
            Map<String, List<String>> map1 = map.get(product.getId());
            List<String> list = map1.get("prod_pname");
            if (list!=null){
                System.out.println(list.get(0)+"\t"+product.getCatalogName()+"\t"+product.getPname()+"\t"+product.getPrice());
            }
            else {
                System.out.println(product.getCatalogName()+"\t"+product.getPname()+"\t"+product.getPrice());
            }
        }
//        solrClient.commit();
//        solrClient.close();
    }
}

这个测试类实现了对solr索引库的增删改查,增加和修改是同一个方法,就是看id是否存在,不存在就是增加,存在就是修改。

2.5 编写service

ProductsServiceImpl.java

package com.qianfeng.service.impl;

import com.qianfeng.entity.Products;
import com.qianfeng.dao.ProductsDao;
import com.qianfeng.service.ProductsService;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * (Products)表服务实现类
 *
 * @author makejava
 * @since 2020-04-27 17:51:03
 */
@Service("productsService")
public class ProductsServiceImpl implements ProductsService {
    @Resource
    private HttpSolrClient solrClient;
    @Resource
    private ProductsDao productsDao;

    /**
     * 通过ID查询单条数据
     *
     * @param pid 主键
     * @return 实例对象
     */
    @Override
    public Products queryById(Integer pid) {
        return this.productsDao.queryById(pid);
    }

    /**
     * 查询多条数据
     *
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return 对象列表
     */
    @Override
    public List<Products> queryAllByLimit(int offset, int limit) {
        return this.productsDao.queryAllByLimit(offset, limit);
    }

    /**
     * 新增数据
     *
     * @param products 实例对象
     * @return 实例对象
     */
    @Override
    public Products insert(Products products) {
        this.productsDao.insert(products);
        return products;
    }

    /**
     * 修改数据
     *
     * @param products 实例对象
     * @return 实例对象
     */
    @Override
    public Products update(Products products) {
        this.productsDao.update(products);
        return this.queryById(products.getPid());
    }

    /**
     * 通过主键删除数据
     *
     * @param pid 主键
     * @return 是否成功
     */
    @Override
    public boolean deleteById(Integer pid) {
        return this.productsDao.deleteById(pid) > 0;
    }

    @Override
    public List<Products> getProductsFromSolrByPage(String keyWord, String categoryName, String priceStr, int priceSort, int currentPage){
        SolrQuery solrQuery = new SolrQuery();
        //关键字查询,查询商品名称
        if(StringUtils.isEmpty(keyWord)){
            solrQuery.set("q","*");
        }else {
            solrQuery.set("q",keyWord);
        }
        if(!StringUtils.isEmpty(categoryName)){
            solrQuery.addFilterQuery("prod_catalog_name:"+categoryName);
        }
        if(!StringUtils.isEmpty(priceStr)){
            String fq = "";
            String[] split = priceStr.split("-");
            if(split.length==1){
                fq = "prod_price:["+split[0]+" TO *]";
            }else if (split.length==2){
                String prefix = split[0];
                if(StringUtils.isEmpty(split[0])){
                    prefix = "*";
                }
                fq = "prod_price:["+prefix+" TO "+split[1]+"]";
            }
            solrQuery.addFilterQuery(fq);
        }

        if(priceSort==1){
            solrQuery.addSort("prod_price", SolrQuery.ORDER.asc);
        }else if(priceSort==2){
            solrQuery.addSort("prod_price", SolrQuery.ORDER.desc);
        }
        int pageSize = 5;
        //计算开始的索引
        int start = pageSize*(currentPage-1);
        solrQuery.setStart(start);
        solrQuery.setRows(pageSize);

        solrQuery.set("df","prod_pname");

        //设置高亮
        //启用高亮
        solrQuery.setHighlight(true);
        //指定高亮字段
        solrQuery.addHighlightField("prod_pname");
        //指定高亮字段前面的字符
        solrQuery.setHighlightSimplePre("<font color='red'>");
        //指定高亮字段后面的字符
        solrQuery.setHighlightSimplePost("</font>");
        QueryResponse response = null;
        try {
            response = solrClient.query(solrQuery);
            //返回总的条目数,这个条目数是未分页前的总条目数
            long numFound = response.getResults().getNumFound();
            System.out.println(numFound);
            List<Products> products = response.getBeans(Products.class);
            Map<String, Map<String, List<String>>> map = response.getHighlighting();
            for (Products product : products) {
                Map<String, List<String>> map1 = map.get(product.getId());
                List<String> list = map1.get("prod_pname");
                //list有可能为空,因为用户什么也不输入的情况下就是这样
                if (list!=null){
                    product.setPname(list.get(0));
                }
            }
            return products;
        } catch (SolrServerException | IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

getProductsFromSolrByPage这个方法就是根据商品名、类型名、价格范围进行查询,然后根据价格升序还是降序与当前页数进行分页查询,此外还对查询关键字进行了高亮显示。

2.6 编写controller

ProductsController.java

package com.qianfeng.controller;

import com.qianfeng.entity.Products;
import com.qianfeng.service.ProductsService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

/**
 * (Products)表控制层
 *
 * @author makejava
 * @since 2020-04-27 17:51:03
 */
@RestController
@RequestMapping("products")
public class ProductsController {
    /**
     * 服务对象
     */
    @Resource
    private ProductsService productsService;

    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    @GetMapping("selectOne")
    public Products selectOne(Integer id) {
        return this.productsService.queryById(id);
    }
    @GetMapping("search")
    public List<Products> getProductsFromSolrByPage(@RequestParam(value = "keyWord",defaultValue = "")String keyWord,
                                                    @RequestParam(value="cn",defaultValue = "")String categoryName,
                                                    @RequestParam(value = "priceStr",defaultValue = "")String priceStr,
                                                    @RequestParam(value = "priceSort",defaultValue = "0")int priceSort,
                                                    @RequestParam(value = "cp",defaultValue = "1") int currentPage){
        List<Products> products = productsService.getProductsFromSolrByPage(keyWord, categoryName,priceStr, priceSort, currentPage);
        return products;
    }

}

由于使用了RestFul风格,并且前后端分离,所以直接返回的是对象,fastjson会自动帮我们转换为json字符串

2.7 编写前端页面

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主页</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.0/jquery.js"></script>
</head>
<body>
    <form action="#">
        商品名:<input type="text" id="pname"> <br>
        种类:<input type="text" id="cn"> <br>
        价格范围: <input type="text" id="priceStr"> <br>
        <input type="button" onclick="search()" value="提交">
    </form>
    <table>
        <tr>
            <th>商品名</th>
            <th>商品种类</th>
            <th>商品图片</th>
            <th>商品价格</th>
        </tr>
        <tbody id="tb">

        </tbody>
    </table>
</body>
<script>
    function search() {
        var keyWord = $("#pname").val();
        var cn = $("#cn").val();
        var priceStr = $("#priceStr").val();
        $.ajax({
            url:"/products/search",
            type:"get",
            data:{keyWord:keyWord,cn:cn,priceStr:priceStr},
            dataType:"json",
            success:function (data) {
                $("#tb").empty();
                $.each(data,function (n,p) {
                    var tr = $("<tr></tr>");
                    var pname = $("<td></td>").html(p.pname);
                    tr.append(pname);
                    var cn = $("<td></td>").text(p.catalogName);
                    tr.append(cn);
                    var picture = $("<td></td>").text(p.picture);
                    tr.append(picture);
                    var price = $("<td></td>").text(p.price);
                    tr.append(price);
                    $("#tb").append(tr);
                })
            }
        })
    }
</script>
</html>

由于是前后端分离项目,所以使用ajax异步获取json字符串,并进行遍历添加元素。注意,我在这里使用的是get方式请求,如果使用Tomcat插件的话需要加上在configuration节点里面加上<uriEncoding>UTF-8</uriEncoding>,否则会出现乱码,当然,你也可以直接使用post方式

3、SpringBoot整合solr

SpringBoot整合solr和上面的方式只有一点不一样,就是依赖,其余增删改查方式一模一样

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>

添加spring-boot-starter-data-solr这个依赖,然后在application.yml文件中这样配置:

spring:
  data:
    solr:
      host: http://localhost:8081/solr/core_demo

指定了solr服务器core的地址。

相关文章

网友评论

      本文标题:SSM与SpringBoot整合solr

      本文链接:https://www.haomeiwen.com/subject/zpfiwhtx.html