zoukankan      html  css  js  c++  java
  • Solr

    一、Solr的简介

    Solr 是Apache的顶级开源项目,采用Java开发,基于Lucene的全文搜索服务器。可独立运行在Jetty、Tomcat等Servlet容器中,用 POST 方法向 Solr 服务器发送一个描述 Field 及其内容的 XML 文档,Solr 根据XML 文档添加、删除、更新索引。Solr 搜索只需要发送 HTTP GET 请求,然后对 Solr返回XML 、JSON等格式的查询结果进行解析,组织页面布局。

    Solr和Lucene的区别

    (1)Lucene是全文检索引擎工具包,不能独立运行;Solr全文检索引擎,可独立运行

    (2)Lucene开发工作量大(索引维护、索引性能优化、搜索性能优化);Solr可以快速的构建企业的搜索引擎

    应用:站内搜索

    二、Solr的安装和配置

    1、目录结构

    bin:solr的运行脚本

    contrib:solr的一些贡献软件/插件,用于增强solr的功能

    dist:该目录包含build过程中产生的war和jar文件,以及相关的依赖文件

    docs:solr的API文档

    example:solr工程的例子目录

    ----solr:包含了默认配置信息的Solr的Core目录

    ----multicore:包含了在Solr的multicore中设置的多个Core目录

    ----webapps:包括一个solr.war,该war可作为solr的运行实例工程

    licenses:solr相关的一些许可信息

    2、运行环境

    需要运行在一个Servlet容器中,Solr4.10.3要求jdk使用1.7以上。Solr默认提供Jetty(java)

    3、Solr整合Tomcat

    3.1 SolrHome和SolrCare

    复制example/solr到磁盘根目录改名SolrHome(单独的文件夹,不能放到Tomcat下),SolrHome是Solr运行的主目录,包括了运行Solr实例所有的配置文件和数据文件,Solr实例就是

    SolrCore,一个SolrHome可以包括多个SolrCore(Solr实例),每个SolrCore提供单独的搜索和索引服务

    SolrHome目录结构:

    collection1:SolrCore(Solr实例)目录,SolrCore名称不固定,一个solr运行实例对外单独提供索引和搜索接口。solrHome中可以创建多个solr运行实例SolrCore。一个solr的运行

    实例对应一个索引目录

    conf:SolrCore的配置文件目录

    data:存放索引文件需要创建

    3.2 整合

    第一步:安装tomcat。D:apache-tomcat-7.0.53

    第二步:把solr的war包复制到tomcat 的webapp目录下

      把solr-4.10.3distsolr-4.10.3.war复制到D:apache-tomcat-7.0.53webapps下。

    改名为solr.war

    第三步:solr.war解压。使用压缩工具解压或者启动tomcat自动解压。解压之后删除solr.war

    第四步:把solr-4.10.3examplelibext目录下的所有的jar包添加到solr工程中

    第五步:配置solrHome和solrCore

    (1)创建一个solrhome(存放solr所有配置文件的一个文件夹)。solr-4.10.3examplesolr目录就是一个标准的solrhome

    (2)把solr-4.10.3examplesolr文件夹复制到D:根目录下,改名为solrhome

    (3)在solrhome下有一个文件夹叫做collection1这就是一个solrcore。就是一个solr的实例。一个solrcore相当于mysql中一个数据库。Solrcore之间是相互隔离

    1.在solrcore中有一个文件夹叫做conf,包含了索引solr实例的配置信息

    2.在conf文件夹下有一个solrconfig.xml。配置实例的相关信息。如果使用默认配置可以不做任何修改

    Lib:solr服务依赖的扩展包,默认的路径是collection1lib文件夹,如果没有就创建一个

    dataDir:配置了索引库的存放路径。默认路径是collection1data文件夹,如果没有data文件夹,会自动创建

    requestHandler

    第六步 solr服务器配置文件(solrHome)的位置

    tomcatwebappssolrWEB-INFweb.xml

    打开<env-entry>的注释,配置solrHome目录

    第七步:启动tomcat  

    E:Programapache-tomcat-7.0.52testinstartup.bat

    第八步:访问http://localhost:8080/solr

    4、Solr后台管理

    (1)Analysis:测试索引分析器和搜索分析器的执行情况

    (2)Document:创建索引、更新索引、删除索引

    /update表示更新索引,solr默认根据id(唯一约束)域来更新Document的内容,如果根据id值搜索不到id域则会执行添加操作,如果找到则更新

    (3)Query:通过/select执行搜索索引,必须指定“q”查询条件才能搜索

    5、配置中文分词器

    schema.xml,在SolrCore的solrHomecollection1conf目录下,数据表配置文件,定义了加入索引的数据的数据类型的。主要包括FieldTypes、Fields和其他的一些缺省设置

    域(Filed)的分类

    普通域(Filed):string long 等

    动态域(DynamicFiled):起到模糊匹配的效果,可以模糊匹配没有定义过的域名

      例如:xxxx这个域名没有定义,但是xxxx_s这个域名模糊匹配了*_s这个域,所以相当于xxxx_s这个域定义了

    主键域(uniqueKey):<uniqueKey>id</uniqueKey> 一般主键域就用默认的这个就可以不需要更改或者添加

    复制域(copyField): 复制域用于查询的时候从多个域中进行查询,这样可以将多个域复制到某一个统一的域中,然后搜索的时候从这个统一的域中进行查询,就相当于从多个域中查询了

    安装中文分词器

    第一步:IKAnalyzer2012FF_u1.jar导入tomcat/webapps/solr/WEB-INF/lib

    第二步:IKAnalyzer的配置文件IKAnalyzer.cfg.xml、停用字典stopword.dic、扩展字典ext.dic都复制到solr的classpath(WEB-INF)新建的classes目录下tomcat/webapps/solr/WEB-INF/classes

    第三步:solrHomecollection1conf下的schema.xml加入

    自定义的fieldType,使用中文分析器

    <!-- IKAnalyzer Filed Type-->
    <fieldType name="text_ik" class="solr.TextField">
      <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
    </fieldType>

    定义field,指定field的type属性为text_ik

    <!--IKAnalyzer Field-->
    <field name="title_ik" type="text_ik" indexed="true" stored="true" />
    <field name="content_ik" type="text_ik" indexed="true" stored="false" multiValued="true"/>

    第四步:重启Tomcat

    访问http://localhost:8080/solr,Analysis即可显示效果

    是否存储和是否索引无关, 索引后就能查询,不索引就不能根据这个域搜索。存储后就能取出来里面的内容,不存储就取不出这个域内容

    三、管理索引库

    1、维护索引

    1.1 单个添加

    document中添加

    1.2 批量导入

    使用dataimport插件

    从solr/dist中把solr-dataimporthandler-4.10.3.jar、solr-dataimporthandler-extras-4.10.3.jar、数据库驱动包mysql-connector-java-5.1.7-bin.jar 导入到solrHomecollection1lib新建的

    lib包下

    在solrHomecollection1confsolrconfig.xml中添加requestHandler

    <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
        <lst name="defaults">
          <str name="config">data-config.xml</str>
        </lst>
    </requestHandler> 

    在相同目录下新建data-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>  
    <dataConfig>   
    <dataSource type="JdbcDataSource"   
              driver="com.mysql.jdbc.Driver"   
              url="jdbc:mysql://localhost:3306/solr"   
              user="root"   
              password="root"/>   
    <document>   
        <entity name="product" query="SELECT pid,name,catalog_name,price,description,picture FROM products ">
             <field column="pid" name="id"/> 
             <field column="name" name="product_name"/> 
             <field column="catalog_name" name="product_catalog_name"/> 
             <field column="price" name="product_price"/> 
             <field column="description" name="product_description"/> 
             <field column="picture" name="product_picture"/> 
        </entity>   
    </document>   
    
    </dataConfig>

     Dataimport ----> Execute,勾选自动刷新,在query中就可以查询到批量导入的数据

    2、删除文档

    document界面

    2.1 删除指定ID的索引

    <delete>
        <id>8</id>
    </delete>
    <commit />

    2.2 删除查询到的索引数据

    <delete>
        <query>product_catalog_name:幽默杂货</query>
    </delete>
    <commit />

    2.3 删除所有索引数据

    <delete>
        <query>*:*</query>
    </delete>
    <commit />

    3、查询索引

    Query界面

    1. q :查询字符串,必须的,如果查询所有使用*:*

    2. fq:(filter query)过虑查询,作用:在q查询符合结果中同时是fq查询符合的

     过滤查询价格从1到20的记录

    // 域名:条件
    product_price : [1 TO 20]

    可以在“q”查询条件中使用product_price:[1 TO 20]

    可以使用“*”表示无限

    3. sort : 排序

    product_price desc/asc

    4、start:分页显示使用,开始记录下标,从0开始

    5、rows :指定返回结果最多有多少条记录,配合start来实现分页

    6、fl :指定返回那些域内容,用逗号或空格分隔多个

    7、df:指定一个搜索Field

    product_keywords

    也可以在SolrCore目录 中conf/solrconfig.xml文件中的SearchHandler指定默认搜索Field,指定后就可以直接在“q”查询条件中输入关键字。

    8、wt - (writer type)指定输出格式,可以有 xml, json, php, php

    9、hl 是否高亮 ,设置高亮域Field,设置格式前缀和后缀

     四、SolrJ

    Solr用于服务器端,SolrJ作为客户端

    导包solr-4.10.3distsolr-solrj-4.10.3.jar、solrj-lib*、日志包solr-4.10.3examplelibext*

    1、增删改

    没有专门的改,增加的时候改

    public class IndexManagerTest {
    
        @Test
        public void testIndexCreate() throws Exception{
            //创建和Solr服务端连接
            SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
            
            //创建solr文档对象
            SolrInputDocument doc = new SolrInputDocument();
            //域要先定义后使用,还有注意必须要有id主键域
            //solr中没有专用的修改方法, 会自动根据id进行查找,如果找到了则删除原来的将新的加入就是修改,如果没找到,将新的直接加入则就是新增
            doc.addField("id", "a001");
            doc.addField("product_name", "台灯1`111");
            doc.addField("product_price", "12.5");
            
            //将文档加入solrServer对象中
            solrServer.add(doc);
            
            //提交
            solrServer.commit();
        }
        
        @Test
        public void testIndexDel() throws Exception{
            //创建和Solr服务端连接
            SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
            
            //根据主键id进行删除
            //solrServer.deleteById("a001");
            
            //根据查询删除,这里是删除所有*:*
            solrServer.deleteByQuery("*:*");
            //提交
            solrServer.commit();
        }
    }

    2、查询

    public class IndexSearchTest {
    
        @Test
        public void testIndexSearch1() throws Exception{
            //连接solr服务端
            SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
            
            //创建solr查询条件对象
            SolrQuery solrQuery = new SolrQuery();
            //查询所有
            solrQuery.setQuery("*:*");
            
            //查询并获取查询响应对象
            QueryResponse queryResponse = solrServer.query(solrQuery);
            //从查询响应中获取查询结果集对象
            SolrDocumentList results = queryResponse.getResults();
            //打印一共查询到多少条记录,也就是记录总数
            System.out.println("=====count====" + results.getNumFound());
            //遍历查询结果集
            for(SolrDocument doc : results){
                System.out.println("============="+doc.get("id"));
                System.out.println("============="+doc.get("product_name"));
                System.out.println("============="+doc.get("product_price"));
                System.out.println("====================================================");
            }
        }
        
        @Test
        public void testIndexSearch2() throws Exception{
            //连接solr服务端
            SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
            
            //创建solr查询条件对象
            SolrQuery solrQuery = new SolrQuery();
            //查询关键字输入
            solrQuery.setQuery("台灯");
            //设置默认搜索域
            solrQuery.set("df", "product_keywords");
            //设置过滤查询
            solrQuery.addFilterQuery("product_price:[1 TO 100]");
            //设置排序,这里是降序
            solrQuery.setSort("product_price", ORDER.desc);
            //=======设置分页========
            //设置起始条数
            solrQuery.setStart(0);
            //设置查询多少条
            solrQuery.setRows(50);
            
            //========设置高亮显示=======
            //高亮默认是关闭的,所以要手动开启
            solrQuery.setHighlight(true);
            //设置需要高亮显示的域
            solrQuery.addHighlightField("product_name");
            //设置高亮前缀
            solrQuery.setHighlightSimplePre("<span style="color:red">");
            //设置高亮后缀
            solrQuery.setHighlightSimplePost("</span>");
            
            //===================查询并获取查询响应对象=====================================
            QueryResponse queryResponse = solrServer.query(solrQuery);
            //从查询响应中获取查询结果集对象
            SolrDocumentList results = queryResponse.getResults();
            //打印一共查询到多少条记录,也就是记录总数
            System.out.println("=====count====" + results.getNumFound());
            //遍历查询结果集
            for(SolrDocument doc : results){
                System.out.println("============="+doc.get("id"));
                //获取高亮
                Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
                List<String> list = highlighting.get(doc.get("id")).get("product_name");
                if(list != null && list.size() > 0){
                    String hlName = list.get(0);
                    System.out.println("=======high lighting=====" + hlName);
                }
                
                System.out.println("============="+doc.get("product_name"));
                System.out.println("============="+doc.get("product_price"));
                System.out.println("====================================================");
            }
        }
    }

    五、总结

    1.solr是一个全文检索引擎系统,通过部署到tomcat下就可以独立运行,通过http协议对外提供全文检索服务,就是索引和文档的正删改查服务

    2. solr直接操作索引库和文档库, 我们的业务系统中可以使用solrJ(solr的客户端,就是一堆jar包)来调用solr服务端,让solr服务端操作文档库和索引库,完成正删改查的任务,将结果返回

    给solrJ客户端,我们在业务系统中就可以,获取到结果然后返回给客户在浏览器中显示

    3. solrHome:solrhome就是solr最核心的目录, 一个solrhome中可以有多个solr实例

    4. solrCore:一个solrCore就是一个solr实例,solr中实例与实例之间他们的索引库和文档库是相互隔离的。每个实例对外单独的提供索引和文档的增删改查服务,默认实例collection1

    5. 文档和索引的增加和修改必须要有id, 主键域,没有会报错

    6. 域名和类型必须先定义后使用,如果没有定义就使用会报错

    7. 域的分类

      普通域:string long 等

      动态域:起到模糊匹配的效果,可以模糊匹配没有定义过的域名

        例如:xxxx这个域名没有定义,但是xxxx_s这个域名模糊匹配了*_s这个域,所以相当于xxxx_s这个域定义了

      主键域:<uniqueKey>id</uniqueKey> 一般主键域就用默认的这个就可以不需要更改或者添加

      复制域: 复制域用于查询的时候从多个域中进行查询,这样可以将多个域复制到某一个统一的域中,然后搜索的时候从这个统一的域中进行查询,就相当于从多个域中查询了

    6.是否存储和是否索引无关, 索引后就能查询,不索引就不能根据这个域搜索,,存储后就能取出来里面的内容,不存储就取不出这个域内容

    7. 一般企业中将数据全部放入数据库中, 由于查询的时候需要使用like模糊查询,模糊查询数据库中使用的是全表扫描算法,这样效率低级,所以需要使用全文检索,来优化查询速度.

    六、搜索案例

    1、系统架构

    2、导包

    SpringMVC包、solrJ的包、日志包example/lib/*

    2.1 web.xml

    前端控制器、POST乱码

     <!-- 1.配置前端控制器 -->
      <servlet>
          <servlet-name>springmvc</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <init-param>
              <!-- 指定springmvc配置文件的路径 
                  如果不指定默认为:/WEB-INF/${servlet-name}-servlet.xml
              -->
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:springmvc.xml</param-value>
          </init-param>
      </servlet>
      <servlet-mapping>
          <servlet-name>springmvc</servlet-name>
          <url-pattern>*.action</url-pattern>
      </servlet-mapping>
      <!-- 2.解决post乱码问题 -->
      <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>
    </web-app>

    2.2 SpringMVC.xml

    包扫描、注解驱动、视图解析器、SolrServer

    <!-- 1.包扫描,controller、service、dao全部扫描-->
    <context:component-scan base-package="com.guojie"/>
    <!-- 2.配置注解驱动,如果配置此标签可以不用配置处理器映射器和适配器 -->
    <mvc:annotation-driven/>
    <!-- 3.配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 4.SolrServer的配置 -->
    <bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
      <!-- index=0代表调用有一个构造参数的solrServer的构造方法 -->
        <constructor-arg index="0" value="http://localhost:8080/solr"/>
    </bean>

    3、POJO

    // 商品对象模型
    public class ProductModel {
        // 商品编号
        private String pid;
        // 商品名称
        private String name;
        // 商品分类名称
        private String catalog_name;
        // 价格
        private float price;
        // 商品描述
        private String description;
        // 图片名称
        private String picture;
    }
    // 返回值对象模型
    public class ResultModel {
        // 商品列表
        private List<ProductModel> productList;
        // 商品总数
        private Long recordCount;
        // 总页数
        private int pageCount;
        // 当前页
        private int curPage;
    }

    4、DAO

    功能:接收Service层传过来的参数,根据参数查询索引库,返回查询结果

    参数:SolrQuery对象

    返回值:一个商品列表List<ProductModel>,还需要返回查询结果的总数量

    返回:ResultModel

    方法:ResultModel queryProduct(SolrQuery query) throws Exception

    @Repository
    public class ProductDaoImpl implements ProductDao {
        
        @Autowired
        private SolrServer solrServer;
    
        @Override
        public ResultModel queryProducts(SolrQuery solrQuery) throws Exception {
            //查询并获取查询响应
            QueryResponse queryResponse = solrServer.query(solrQuery);
            //从响应中获取查询结果集
            SolrDocumentList docList = queryResponse.getResults();
            
            //创建返回结果对象
            ResultModel resultModel = new ResultModel();
            List<ProductModel> productList = new ArrayList<ProductModel>();
            
            //遍历结果集
            if(docList != null){
                //获取总记录数
                resultModel.setRecordCount(docList.getNumFound());
                for(SolrDocument doc : docList){
                    ProductModel product = new ProductModel();
                    product.setPid(String.valueOf(doc.get("id")));
                    
                    //获取高亮
                    Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
                    if(highlighting != null){
                        List<String> list = highlighting.get(doc.get("id")).get("product_name");
                // 判断list不为空,且list的大小不为0
                        if(list != null && list.size() > 0){
                            product.setName(list.get(0));
                        } else {
                            product.setName(String.valueOf(doc.get("product_name")));
                        }
                    } else {
                        product.setName(String.valueOf(doc.get("product_name")));
                    }
                    // String不为NULL且不为"",Java中没有sizeof
                    if(doc.get("product_price") != null && !"".equals(doc.get("product_price"))){
                        product.setPrice(Float.valueOf(doc.get("product_price").toString()));
                    }
                    product.setCatalog_name(String.valueOf(doc.get("product_catalog_name")));
                    product.setPicture(String.valueOf(doc.get("product_picture")));
                    productList.add(product);
                }
                resultModel.setProductList(productList);
            }
            return resultModel;
        }
    
    }

    5、Service

    封装查询条件

    功能:接收action传递过来的参数,根据参数拼装一个查询条件,调用dao层方法,查询商品列表。接收返回的商品列表和商品的总数量,根据每页显示的商品数量计算总页数

    参数:

    1.查询条件:字符串

    2.商品分类的过滤条件:商品的分类名称,字符串

    3.商品价格区间:传递一个字符串,满足格式:“0-100、101-200、201-*”

    4.排序条件:页面传递过来一个升序或者降序就可以,默认是价格排序。0:升序1:降序

    5.分页信息:每页显示的记录条数创建一个常量60条。传递一个当前页码就可以了

    业务逻辑:

    1.根据参数创建查询对象

    2.调用dao执行查询

    3.根据总记录数计算总页数

    返回值:ResultModel

    方法定义:ResultModel queryProduct(String queryString, String caltalog_name, String price,String sort, Integer page) throws Exception;

    @Service
    public class ProductServiceImpl implements ProductService {
        private static final Integer PAGE_SIZE = 60;
    
        @Autowired
        private ProductDao productDao;
    
        @Override
        public ResultModel query(String queryString, String catalog_name, String price, String sort, Integer page)
                throws Exception {
            //创建查询条件对象
            SolrQuery solrQuery = new SolrQuery();
            //设置默认搜索域
            solrQuery.set("df", "product_keywords");
            //设置查询关键字
            if(queryString != null && !"".equals(queryString)){
                solrQuery.setQuery(queryString);
            } else {
                solrQuery.setQuery("*:*");
            }
            
            //设置过滤条件按照分类名称进行过滤
            if(catalog_name != null && !"".equals(catalog_name)){
                solrQuery.addFilterQuery("product_catalog_name:" + catalog_name);
            }
            //设置过滤条件按照价格进行过滤
            if(price != null && !"".equals(price)){
                String[] split = price.split("-");
                if(split != null && split.length > 1){
                    solrQuery.addFilterQuery("product_price:["+split[0]+" TO "+split[1]+"]");
                }
            }
            //设置排序
            if("1".equals(sort)){
                solrQuery.addSort("product_price", ORDER.asc);
            } else {
                solrQuery.addSort("product_price", ORDER.desc);
            }
            
            //设置分页
            if(page == null){
                page = 1;
            }
            Integer start = (page - 1) * PAGE_SIZE;
            //从第几天记录开始查
            solrQuery.setStart(start);
            //每页显示多少条
            solrQuery.setRows(PAGE_SIZE);
            
            //设置高亮显示
            solrQuery.setHighlight(true);
            //设置高亮显示的域
            solrQuery.addHighlightField("product_name");
            //设置高亮前缀
            solrQuery.setHighlightSimplePre("<span style="color:red">");
            //设置高亮后缀
            solrQuery.setHighlightSimplePost("</span>");
            
            //查询返回结果
            ResultModel resultModel = productDao.queryProducts(solrQuery);
            
            resultModel.setCurPage(Long.parseLong(page.toString()));
            
            //计算总页数
            Long pageCount = resultModel.getRecordCount() / PAGE_SIZE;
            if(resultModel.getRecordCount() % PAGE_SIZE > 0){
                pageCount ++;
            }
            resultModel.setPageCount(pageCount);
            return resultModel;
        }
    }

    6、Controller

    功能:接收页面传递过来的参数调用service查询商品列表。将查询结果返回给jsp页面,还需要查询参数的回显

    参数

    1.查询条件:字符串

    2.商品分类的过滤条件:商品的分类名称,字符串

    3.商品价格区间:传递一个字符串,满足格式:“0-100、101-200、201-*

    4.排序条件:页面传递过来一个升序或者降序就可以,默认是价格排序。0:升序1:降序

    5.分页信息:每页显示的记录条数创建一个常量60条。传递一个当前页码就可以了

    6.Model:相当于request

    返回结果:String类型,就是一个jsp的名称

    方法:String queryProduct(String queryString, String caltalog_name, String price, String sort, Integer page, Model model) throws Exception;

    @Controller
    public class ProductsController {
        
        @Autowired
        private ProductService productService;
    
        @RequestMapping("/list")
        public String list(String queryString, String catalog_name, String price, 
                String sort, Integer page, Model model) throws Exception{
            
            ResultModel result = productService.query(queryString, catalog_name, price, sort, page);
            
            //返回查询结果
            model.addAttribute("result", result);
            
            model.addAttribute("queryString", queryString);
            model.addAttribute("catalog_name", catalog_name);
            model.addAttribute("price", price);
            model.addAttribute("sort", sort);
            return "product_list";
        }
    }

    Java Project编译后的文件在bin下

    Web项目编译后的文件在classes下

    有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
  • 相关阅读:
    类型初始值设定项引发异常的解决方法
    sql修改排序规则,区分大小
    SQLServer查询所有子节点
    Cannot resolve the collation conflict between "Chinese_PRC_CI_AS" and "SQL_L及由于排序规则不同导致查询结果为空的问题
    SQLServer跨库查询--分布式查询
    DataTable对象的操作问题
    .Net插入大批量数据
    SQL修改字段类型
    数据抓包分析
    Qss 皮肤
  • 原文地址:https://www.cnblogs.com/1989guojie/p/7715186.html
Copyright © 2011-2022 走看看