超詳細之Spring Boot結合Jest實現對ElasticSearch的全文檢索(從mysql導入數據)

1 Elasticsearch與Solr區別

在Java的全文檢索裏面有solrelasticsearch兩大高級玩意
首先我們來看看他們的區別:
在這裏插入圖片描述
1)Solr建立索引時候,搜索效率下降,實時搜索效率不高,es實時搜索效率高
2)Solr利用Zookeeper進行分佈式管理,而Elasticsearch自身帶有分佈式協調管理功能。
3)Solr支持更多格式的數據,比如JSON、XML、CSV,而Elasticsearch僅支持json文件格式。
4)Solr官方提供的功能更多,而Elasticsearch本身更注重於核心功能,高級功能多有第三方插件提供
5)Solr在傳統的搜索應用中表現好於Elasticsearch,但在處理實時搜索應用時效率明顯低於Elasticsearch。
6)Solr是傳統搜索應用的有力解決方案,但Elasticsearch更適用於新興的實時搜索應用
因此我們選擇Elasticsearch

2 elasticsearch的安裝

2.1 創建用戶

因爲elasticsearch出於系統安全考慮,不能使用root啓動.所以要建立其他的普通用戶.
創建用戶dev,密碼:123456

[root@dev-2 ~]# useradd dev
[root@dev-2 ~]# passwd dev
Changing password for user dev.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
[root@dev-2 ~]# id dev

2.2 下載elasticsearch且配置環境變量

下載:

[root@dev-2 elasticsearch]# wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-
6.3.0.tar.gz
--2018-09-05 11:32:45-- https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.0.tar.gz
Resolving artifacts.elastic.co (artifacts.elastic.co)... 107.21.253.15, 184.72.242.47, 107.21.237.95,
...
Connecting to artifacts.elastic.co (artifacts.elastic.co)|107.21.253.15|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 91423553 (87M) [application/x-gzip]
Saving to: ‘elasticsearch-6.3.0.tar.gz’

100%
[=======================================================================================================
==============================================>] 91,423,553 947KB/s in 1m 55s
2018-09-05 11:34:31 (776 KB/s) - ‘elasticsearch-6.3.0.tar.gz’ saved [91423553/91423553]
[root@dev-2 elasticsearch]#

解壓

[root@dev-2 elasticsearch]# tar -xvf elasticsearch-6.3.0.tar.gz

配置環境變量

[root@dev-2 elasticsearch-6.3.0]# vim /etc/profile
# import elasticsearch
export SEARCH_HOME='/opt/soft/elasticsearch/elasticsearch-6.3.0'
export PATH=$PATH:$SEARCH_HOME/bin
[root@dev-2 elasticsearch-6.3.0]# source /etc/profile

設置所有者爲dev

[root@dev-2 elasticsearch]# ll
total 89284
drwxr-xr-x. 8 root root 143 Sep 05 11:43 elasticsearch-6.3.0
[root@dev-2 elasticsearch]# chown -R dev:dev elasticsearch-6.3.0
[root@dev-2 elasticsearch]# ll
total 89284
drwxr-xr-x. 8 dev dev 143 Sep 05 11:43 elasticsearch-6.3.0
[root@dev-2 elasticsearch]

2.3elasticsearch的配置

修改JVM參數
-Xms和-Xmx的值要一致.

[root@dev-2 elasticsearch-6.3.0]# cd config/
[root@dev-2 config]# ls
elasticsearch.yml jvm.options log4j2.properties role_mapping.yml roles.yml users users_roles
[root@dev-2 config]# vim jvm.options
# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space
-Xms1g
-Xmx1g
[root@dev-2 config]#

修改啓動參數

[root@dev-2 config]# vim elasticsearch.yml
# Set the bind address to a specific IP (IPv4 or IPv6):
#使用 雲服務器時用以下地址;若爲本地服務器時則使用本地服務器地址;
network.host: 0.0.0.0
#
# Set a custom port for HTTP:
#
http.port: 9200
#
# 開啓跨域訪問支持,默認爲false
#
http.cors.enabled: true
#
# 跨域訪問允許的域名地址,(允許所有域名)以上使用正則
#
http.cors.allow-origin: /.*/
[root@dev-2 config]#

3 啓動Elasticsearch

切換到dev啓動

[root@dev-2 elasticsearch-6.3.0]# su dev
[dev@dev-2 elasticsearch-6.3.0]$ elasticsearch
[2018-09-05T11:50:22,771][INFO ][o.e.n.Node ] [] initializing ...

但啓動報錯,如下

[2018-09-05T11:50:40,429][INFO ][o.e.b.BootstrapChecks ] [eme3ye6] bound or publishing to a nonloopback address, enforcing bootstrap checks
ERROR: [4] bootstrap checks failed
[1]: initial heap size [268435456] not equal to maximum heap size [536870912]; this can cause resize
pauses and prevents mlockall from locking the entire heap
[2]: max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
[3]: max number of threads [3852] for user [dev] is too low, increase to at least [4096]
[4]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
[2018-09-05T11:50:40,506][INFO ][o.e.n.Node ] [eme3ye6] stopping ...
[2018-09-05T11:50:40,606][INFO ][o.e.n.Node ] [eme3ye6] stopped
[2018-09-05T11:50:40,606][INFO ][o.e.n.Node ] [eme3ye6] closing ...
[2018-09-05T11:50:40,619][INFO ][o.e.n.Node ] [eme3ye6] closed

請切換到root,修改 /etc/security/limits.conf文件

[root@dev-2 elasticsearch-6.3.0]# vim /etc/security/limits.conf
#@student - maxlogins 4
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096
# End of file
[root@dev-2 elasticsearch-6.3.0]#

修改/etc/sysctl.conf文件

[root@dev-2 etc]# vim /etc/sysctl.conf
vm.max_map_count=655360
[root@dev-2 etc]# sysctl -p
vm.max_map_count = 655360

再切換到dev用戶啓動elasticsearch

[dev@dev-2 elasticsearch-6.3.0]$ nohup elasticsearch &
[dev@dev-2 elasticsearch-6.3.0]$ cat nohup.out
....
[2018-09-05T12:15:27,438][INFO ][o.e.x.s.t.n.SecurityNetty4HttpServerTransport] [eme3ye6]
publish_address {47.98.143.72:9200}, bound_addresses {47.98.143.72:9200}
[2018-09-05T12:15:27,438][INFO ][o.e.n.Node ] [eme3ye6] started
[2018-09-05T12:15:27,949][WARN ][o.e.x.s.a.s.m.NativeRoleMappingStore] [eme3ye6] Failed to clear cache
for realms [[]]
[2018-07-04T10:17:37,982][INFO ][o.e.l.LicenseService ] [eme3ye6] license [36a89374-568c-4f0d-83ce-
37475ca3015c] mode [basic] - valid
[2018-07-04T10:17:37,996][INFO ][o.e.g.GatewayService ] [eme3ye6] recovered [0] indices into
cluster_state
....
[dev@dev-2 elasticsearch-6.3.0]$ jps
21874 Jps
4969 Elasticsearch
[dev@dev-2 elasticsearch-6.3.0]# netstat -lnp|grep 9200
tcp        0      0 0.0.0.0:9200            0.0.0.0:*               LISTEN      4969/java   
[dev@dev-2 elasticsearch-6.3.0]# curl 47.98.143.72:9200
{
  "name" : "jDY6CwJ",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "LhuAiJqpRVqKOxIUZ0Xa3Q",
  "version" : {
    "number" : "6.3.0",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "424e937",
    "build_date" : "2018-06-11T23:38:03.357887Z",
    "build_snapshot" : false,
    "lucene_version" : "7.3.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

4 中文分詞

4.1 安裝IK分詞器

**Github下載和Elasticsearch相同版本的IK分詞器: **
IK分詞器下載地址
plugins/ 文件夾下建立ik 文件夾,並把壓縮包內容解壓到 ik 文件夾.

[root@dev-2 plugins]# pwd
/opt/soft/elasticsearch/elasticsearch-6.3.0/plugins
[dev@dev-2 plugins]$ mkdir ik
[dev@dev-2 plugins]$ cd ik
[dev@dev-2 ik]$ unzip elasticsearch-analysis-ik-6.3.0.zip -d .
[dev@dev-2 ik]$ ls
commons-codec-1.9.jar config httpclient-4.5.2.jar plugindescriptor.properties
commons-logging-1.2.jar elasticsearch-analysis-ik-6.3.0.jar httpcore-4.4.4.jar

重新啓動ElasticSear

[dev@dev-2 elasticsearch-6.3.0]$ nohup bin/elasticsearch &
[1] 17149
[dev@dev-2 elasticsearch-6.3.0]$ jps
17236 Jps
17149 Elasticsearch
[dev@dev-2 elasticsearch-6.3.0]$ cat nohup.out |grep ik
[2018-09-05T13:42:57,317][INFO ][o.e.p.PluginsService ] [eme3ye6] loaded plugin [analysis-ik]
[2018-09-05T13:43:21,229][INFO ][o.w.a.d.Monitor ] try load config from
/opt/soft/elasticsearch/elasticsearch-6.3.0/config/analysis-ik/IKAnalyzer.cfg.xml
[2018-09-05T13:43:49,372][INFO ][o.w.a.d.Monitor ] try load config from
/opt/soft/elasticsearch/elasticsearch-
6.3.0/plugins/ik/config/https://blog.csdn.net/z90818/article/details/78644293.cfg.xml
[dev@dev-2 elasticsearch-6.3.0]$

4.2 具體使用

Q: 如何使用IK分詞器對自己需要存放的數據進行分詞和查詢?
A: 在創建index和type的時候,就指定type裏面的字段是否需要分詞.就像我們在mysql數據庫先搭建表結構一樣
例如: 指定Index爲 testindex,type爲 testtype裏面的字段 name使用 ik_max_word 分詞存儲和搜索.

curl -XPOST http://localhost:9200/testindex/testtype/_mapping -H 'Content-Type:application/json' -d'
{
	"properties": {
		    "name": {
		             "type": "text",
		             "analyzer": "ik_max_word",
		            "search_analyzer": "ik_max_word"
		    }
	 }
}'

詳細例子可見: ik-github例子

5 應用

5.1 簡單的搜索引擎(實現對電影節目的全文檢索)

5.1.1概述

注:該系統所用的數據由於數據量較大,則由本人事先從數據庫讀取數據存到ElasticSearch,下面會做詳細描述以便後期檢索需要
該檢索系統使用Spring Boot結合Jest +mysql快速實現對阿里雲ElasticSearch的全文檢索功能。
主要使用組件:
Jest:一種rest訪問es的客戶端
elasticsearch:實現全文檢索
thymeleaf:web前端模版框架

5.1.2 項目目錄結構

在這裏插入圖片描述

5.1.3 maven配置及yml配置文件

a) maven配置

<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>

  <groupId>cheng.demo.springcloud</groupId>
  <artifactId>jest-elasticsearch</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>jest-elasticsearch</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  
  <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.6.RELEASE</version>
	</parent>
	
	<dependencies>
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
			<groupId>io.searchbox</groupId>
			<artifactId>jest</artifactId>
		</dependency>
		<dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>persistence-api</artifactId>
            <version>1.0</version>
        </dependency>
        <!-- 添加mybatis支持 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.2.0</version>
        </dependency>
        <!--Mapper插件 -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>3.3.9</version>
        </dependency>
        <!-- generator -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.5</version>
        </dependency>
        <!-- 添加mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>
        <!--添加spring JDBC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.11</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.2</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- 前端框架模板 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>4.0.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

b) yml配置文件

server:
  port: 7081

spring:
  elasticsearch:
    jest:
      uris:
      - http://47.98.143.72:9200
      read-timeout: 5000
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://10.10.2.233:3306/irs_cms
    username: root
    password: xxxxxx
  thymeleaf:
    cache: false

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.cheng.elasticsearch.entity

5.1.4 Entity實體類BusVod(電影資源)
@Document(indexName = BusVod.INDEX, type = BusVod.ORDER_TYPE,  shards = 6, replicas = 2, refreshInterval = "-1")
public class BusVod implements Serializable{
    private static final long serialVersionUID = -763638353551774166L;
    //建立索引
    public static final String INDEX = "movie-test";
    //類型
    public static final String ORDER_TYPE = "movie-type";

    private String name;
    private String director;
    private String actor;
    private String releaseTime;
    private String provider;
    private String vodDesc;
    private String screenWriter;
    // 電影描述,可以通過ik 分詞器進行分詞
    @Field(type = FieldType.String, searchAnalyzer = "ik", analyzer = "ik")
    private String summaryLong;
    public BusVod(String name, String director, String actor, String releaseTime, String provider, String vodDesc, String screenWriter, String summaryLong) {
       //省略
    }

    public BusVod() {
    }
    //getter setter 省略...
}

5.1.5 mapper資源(從數據庫取所有的電影資源數據)

a) java類(BusVodMapper.java)

@Repository
public interface BusVodMapper extends Mapper<BusVod> {
	//從數據庫查詢所有的電影資源數據
    List<BusVod> findList();
}

b) mybatis-xml文件(BusVodMapper.xml)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.cheng.elasticsearch.mapper.BusVodMapper">
	<resultMap id="resMap" type="com.cheng.elasticsearch.entity.BusVod">
		<result column="name" property="name" jdbcType="VARCHAR" />
		<result column="director" property="director" jdbcType="VARCHAR" />
		<result column="actor" property="actor" jdbcType="VARCHAR" />
		<result column="release_time" property="releaseTime" jdbcType="VARCHAR" />
		<result column="provider" property="provider" jdbcType="VARCHAR" />
		<result column="vod_desc" property="vodDesc" jdbcType="VARCHAR" />
		<result column="screen_writer" property="screenWriter" jdbcType="VARCHAR"/>
		<result column="summary_long" property="summaryLong" jdbcType="VARCHAR"/>
	</resultMap>

	<sql id="column">
		bus_vod.name				name,
		bus_vod.director			director,
		bus_vod.actor				actor,
		bus_vod.release_time 		release_time,
		bus_vod.provider			provider,
		bus_vod.vod_desc 			vod_desc,
	    bus_vod.screen_writer		screen_writer,
	    bus_vod.summary_long		summary_long
	</sql>

	<select id="findList" resultMap="resMap">
		select
		<include refid="column"></include>
		from bus_vod
	</select>

</mapper>
5.1.6 Service配置

a) 接口

public interface BusVodService {
	//從數據庫導入數據到elasticsearch
    List<BusVod> findList();
    void saveBusvod(List<BusVod> busVodList);
	//根據輸入的值搜索複合的內容
    List<BusVod> searchEntity(String keyword);
}

b) 實現類

@Service
public class BusVodServiceImpl implements BusVodService {
	private static final Logger LOGGER = LoggerFactory.getLogger(BusVodServiceImpl.class);

    @Autowired
    private JestClient jestClient;

    @Autowired
    private BusVodMapper mapper;

	@Override
    public List<BusVod> findList() {
        List<BusVod> busVods = mapper.findList();
        return busVods;
    }

    /**
     * 批量保存內容到ES
     */
    @Override
    public void saveBusvod(List<BusVod> busVodList) {
        Bulk.Builder bulk = new Bulk.Builder();
       for(BusVod busVod : busVodList) {
            Index index = new Index.Builder(busVod).index(BusVod.INDEX).type(BusVod.ORDER_TYPE).build();
            bulk.addAction(index);
        }
        try {
            jestClient.execute(bulk.build());
            LOGGER.info("ES 插入完成");
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.error(e.getMessage());
        }
    }
	
	/**
     * 通過一個關鍵字搜索對應的電影資源
     * @param keyword
     * @return
     */
    @Override
    public List<BusVod> searchEntity(String keyword) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (keyword != null) {
            boolQueryBuilder.should(QueryBuilders.commonTermsQuery("actor",keyword));
            boolQueryBuilder.should(QueryBuilders.commonTermsQuery("director", keyword));
            boolQueryBuilder.should(QueryBuilders.commonTermsQuery("name", keyword));
            boolQueryBuilder.should(QueryBuilders.commonTermsQuery("provider", keyword));
            boolQueryBuilder.should(QueryBuilders.commonTermsQuery("releaseTime", keyword));
            boolQueryBuilder.should(QueryBuilders.commonTermsQuery("screenWriter", keyword));
            boolQueryBuilder.should(QueryBuilders.commonTermsQuery("summaryLong", keyword));
            boolQueryBuilder.should(QueryBuilders.commonTermsQuery("vodDesc", keyword));
        }
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("summaryLong");//高亮summaryLong
        highlightBuilder.field("name");
        highlightBuilder.preTags("<font color='red'>").postTags("</font>");//高亮標籤
        highlightBuilder.fragmentSize(500);//高亮內容長度
 
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.highlight(highlightBuilder);
        searchSourceBuilder.query(boolQueryBuilder).size(10); //需要檢索的數據條數,這裏可以設計爲分頁
        Search search = new Search.Builder(searchSourceBuilder.toString())
                .addIndex(BusVod.INDEX).addType(BusVod.ORDER_TYPE).build();
        try {
            SearchResult result = jestClient.execute(search);
            System.out.println("本次查詢共查到:"+result.getTotal()+"部電影!");
            List<SearchResult.Hit<BusVod,Void>> hits = result.getHits(BusVod.class);
            System.out.println(hits.size());
            List<BusVod> busVodlists = new ArrayList<BusVod>();

            for (SearchResult.Hit<BusVod, Void> hit : hits) {
                BusVod source = hit.source;
                //獲取高亮後的內容
                Map<String, List<String>> highlight = hit.highlight;
                if (highlight!=null){
                    List<String> summaryLong = highlight.get("summaryLong");//高亮後的summaryLong
                    if(summaryLong!=null){
                        source.setSummaryLong(summaryLong.get(0));
                    }
                    List<String> name = highlight.get("name");//高亮後的name
                    if(name!=null){
                        source.setName(name.get(0));
                    }
                }
                BusVod busVod = new BusVod();
                busVod.setName(source.getName());
                busVod.setSummaryLong(source.getSummaryLong());
                busVod.setActor(source.getActor());
                busVod.setDirector(source.getDirector());
                busVod.setScreenWriter(source.getScreenWriter());
                busVod.setVodDesc(source.getVodDesc());
                busVod.setReleaseTime(source.getReleaseTime());
                busVod.setProvider(source.getProvider());
                busVodlists.add(busVod);
            }
                return busVodlists;     
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }
    
}
5.1.7 Controller類

調用Service接口,數據返回到頁面(VodController)

@Controller
@RequestMapping("/bus/vod")
public class VodController {
    @Autowired
    private BusVodService busVodService;

	//利用fiddler工具或者postman工具調用該方法可實現數據庫數據導入到elasticsearch中
    @RequestMapping("/list")
    public String getList(HttpServletRequest request) {
        List<BusVod> busVods = busVodService.findList();
        List<BusVod> addList = new ArrayList<BusVod>();
        for(BusVod  busVod : busVods){
            BusVod newEntity = new BusVod(busVod.getName(),busVod.getDirector(),busVod.getActor(),
                    busVod.getReleaseTime(), busVod.getProvider(),busVod.getVodDesc(), busVod.getScreenWriter(), busVod.getSummaryLong());
            addList.add(newEntity);
        }
        busVodService.saveBusvod(addList);
        return "";
    }

	//實現全文檢索功能
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public String query(@RequestParam(name = "keyword", required = false) String keyword,
                                ModelMap map ) {
        List<BusVod>  busVodList=  busVodService.searchEntity(keyword);
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
        map.addAttribute("movieLists",busVodList);
        return  "moviesList";
    }
}
5.1.8 前端頁面實現

前端頁面使用的是thymeleaf模板(moviesList.html)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>電影資源檢索</title>
    <link rel='stylesheet' href='/webjars/bootstrap/css/bootstrap.min.css'/>
    <script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/bootstrap/js/bootstrap.min.js"></script>
</head>
<body  style="width: 1000px; margin-left:  200px;">
<form action="/bus/vod/query" class="px-5 py-3" >
    <div class="input-group">
        <input name="keyword" type="text" class="form-control" placeholder="請輸入搜索內容" aria-label="請輸入搜索內容" aria-describedby="basic-addon2"/>
        <div class="input-group-append">
            <button class="btn btn-outline-secondary" type="submit">搜索</button>
        </div>
    </div>
</form>
<ul class="list-group">
    <li th:each="movie : ${movieLists}" class="list-group-item">
        <div class="row">
	        <!-- 這裏可添加url鏈接,還未實現 -->
            <a th:href="${movie.getName()}">
                <h4 scope="row" th:utext="${movie.name}" ></h4>
            </a>
            <h6 scope="row" th:text="${movie.getActor()}" class="align-bottom" ></h6>
        </div>
        <div class="row">
            <h6 scope="row" th:utext="${movie.summaryLong}"/>
        </div>
    </li>
    <hr/>
</ul>
</body>
</html>

5.2 實現結果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章