SpringBoot + Thymeleaf 练手小项目 --------- 豆瓣网站模拟


一、项目介绍

本项目的具体功能是模拟豆瓣网站查阅电影、演员信息的实现,具体功能如下。

我们首先打开网站首页,电影库中有以下几部电影(电影信息存储在数据库中),我们随便点击电影的图片或下方的文字,会跳转到电影的具体信息界面。
在这里插入图片描述
电影的详细介绍界面如下。
在这里插入图片描述
在此界面我们可以点击主演或导演的名字或者下方图片的超链接,同样可以跳转到人物的详细介绍界面
在这里插入图片描述
人物的详细介绍界面如图。
在这里插入图片描述
我们点击右上方的豆豆瓣按钮可以跳转到主页。
在这里插入图片描述

主页如图。
在这里插入图片描述
在主页我们输入字符点击查询按钮做模糊查询,将返回包含这个字符的所有电影,比如我们输入叶。
在这里插入图片描述
如下所有含叶的电影都被展示出来了。
在这里插入图片描述

点击某个电影下方查看更多按钮,可以查看此电影的详细信息。
在这里插入图片描述
电影详细信息如图。
在这里插入图片描述

项目目的 :熟悉 SpringBoot 和 Thymeleaf 的使用

二、资源准备

1. 准备数据库表

我们新建 qdu 数据库,在此数据库中创建 movie 电影表、worker 演员表、两者的关系表movieworker

在 Navicat 中执行下方的 sql 脚本

-- 创建工作人员表,存储导演和演员信息
create table worker (
	WorkerId char(7),
	WName varchar(25),
	WAnotherName nvarchar(50),
	WGender char(2),
	WHoroscope varchar(10),
	WDescription text,
	WImage varchar(30)
);

-- 插入导演信息
insert worker values
('1022652','格蕾塔·葛韦格','Greta Gerwig','女','狮子座','格蕾塔·葛韦格出生于1983年,毕业于纽约伯纳德学院。葛韦格原本打算成为一个编剧,她因为和美国独立电影呢喃核运动的联系而名声鹊起。2010年的《格林伯格》让她的知名度大涨,在那之后她出演了一系列主流影片诸如《不求回报》,《亚瑟》等等。2012年她出演了伍迪·艾伦的《爱在罗马》,获得更多影迷赏识指日可待。','p1513870447.95.webp'),
('1340565','禹民镐',N'우민호','男','狮子座','获奖人:2016年 第37届韩国电影青龙奖 最佳影片 局内人','p271.webp'),
('1048021','萨姆·门德斯','Sam Mendes','男','狮子座','萨姆·门德斯原名塞缪尔·安德烈·门德斯,生于1965年8月1日。他是家中的独子,父亲是特立尼达人,母亲拥有波兰和法国血统。门德斯的双亲在他5岁时离婚了,他跟随出版刊物的母亲瓦勒莉来到伦敦,随后又搬到牛津。他童年惟一的玩伴就是板球。','p2581888792.webp'),
('1036390','托德·菲利普斯','Todd Phillips','男','射手','托德·菲利普斯,好莱坞著名导演,以性喜剧《宿醉》成名。','p12855.webp'),
('1274472','叶伟信','Wilson Yip','男','天秤座','自1985年在新艺城电影公司开始其电影幕後工作生涯,最初是当信差,后来再经郑则士转做场记,继而再做上去当副导演。曾参与超过二十部电影的拍摄工作,一连做了10年的副导演,直到1995年才有机会独立执导电影。首次担任电影导演的副导演,拍摄作品《夜半一点钟》。并凭1996年拍摄的《旺角风云》获得第三届香港电影评论学会推荐电影,同时获得最佳编剧奖。长期为许多名导打下手也令叶伟信积累了不少拍片经验,对其日后的发展产生了极大影响。','p1524904218.27.webp');

-- 插入演员信息
insert worker values
('1378921','佛罗伦斯·珀','Florence Pugh','女','摩羯座','Florence Pugh was born on January 3, 1996 in Oxford, Oxfordshire, England. She is an actress, known for The Falling (2014), Marcella (2016) and Lady Macbeth (2016).','p1517034183.97.webp'),
('1053624','艾玛·沃特森','Emma Watson','女','白羊座','凭借《哈利波特》系列红遍全球;拥有古典优雅气质与现代时尚美感;年仅十九岁便成为二十一世纪最卖座女星;深受时尚巨头老佛爷与女魔头的宠爱;却在事业顶峰期以straight A的成绩踏入美国常春藤盟校布朗的校园;同时致力于平等,热衷环保与公益。美貌与智慧并存,就是她——艾玛·沃特森。','p1512020567.12.webp'),
('1022004','西尔莎·罗南','Saoirse Ronan','女','白羊座','西尔莎·罗南1994年出生在美国纽约,父母是爱尔兰人,后在拥有五万人口的小镇Carlow长大,名字Saoirse在爱尔兰语中则是“自由”的意思。她的父亲是曾经出演过《与魔鬼同行》、《王牌罪犯》及众多爱尔兰影片的保罗·罗南,在当地享有很高的知名度,回忆起多年前在《与魔鬼同行》的片场,合作的布拉德·皮特就曾经轻轻抱起4岁的小西尔莎。','p9684.webp'),
('1032148','乔治·麦凯','George MacKay','男','双鱼座','英国演员。他曾出演电影《男孩们回来了》,《我的生存之道》,《爱在阳光灿烂时》,《险中之人》,《神奇队长》,《奥菲莉亚》和《1917》。','p1494063833.48.webp'),
('1341989','迪恩·查尔斯·查普曼','Dean-Charles Chapman','男','处女座','英国演员。他最著名的作品是在《跳出我天地音乐剧》中饰演Billy,以及在《权力的游戏》中饰演托曼国王。','p1433819011.4.webp'),
('1047979','华金·菲尼克斯','Joaquin Phoenix','男','天蝎座','杰昆·菲尼克斯,是90年代最出名的童星瑞凡·菲尼克斯的亲弟弟,三十出头的他戏龄已经高达二十几年。杰昆出道的第一部片子叫《spacecamp》,后来又在《Russkies》 (1987)和《Parenthood 》(1989)中演出,他表演相当不错。','p241.webp'),
('1054445','罗伯特·德尼罗','Robert De Niro','男','狮子座',' 奥斯卡影帝罗伯特·德尼罗塑造过的所有角色中,最著名的当属《教父2》中年轻的唐·科莱昂和《愤怒的公牛》中的拳击手杰克·拉莫塔,他最经典的一句台词则是《出租汽车司机》中“你在跟我讲话?”凭借马丁·斯科塞斯1973年影片《穷街陋巷》一举成名的德尼罗,既可以扮演心狠手辣的黑帮分子,又能在《午夜狂奔》、《摇尾狗》、《老大靠边闪》和《拜见岳父岳母》等喜剧中尽现搞笑天份。','p47221.webp'),
('1116943','马克·马龙','Marc Maron','男','天秤座','Marc David Maron (/ˈmærən/ MAIR-ən; born September 27, 1963) is an American stand-up comedian, podcaster, writer and actor.','p1404968819.83.webp'),
('1370059','莎姬·贝兹','Zazie Beetz','女','双子座','Zazie Beetz is an actress. Born in Berlin, Germany to a German Father and African-American mother, she raised in Manhattan speaking both German and English at home. She performed in community theaters and local stages from a young age. She is known for Atlanta (2016), Applesauce (2015, and Finding Her (2017).','p1489116013.79.webp'),
('1025194','甄子丹','Donnie Yen','男','狮子座','甄子丹出生在广东广州,祖籍广东台山,2岁来到香港直到11岁移民美国波士顿。母亲麦宝婵是世界闻名的武术家和太极高手,创办了驰名国际的中国武术研究所。从甄子丹会走路起,麦宝婵就开始教他练武,为他打扎好优良的传统武术根基。','p10695.webp'),
('1105108','李秉宪',N'이병헌','男','巨蟹座','李秉宪,韩国男艺人,被誉为韩国最完美的男人。在电视剧方面,他把所有能拿的奖都拿遍了。电影方面,第一部电影《是谁让我发疯》,使他拿到了大钟奖新人男演员奖,但之后的几部电影反映平平,但秉宪并没有因此而灰心,更加紧努力于他的演艺事业。','p1383705218.17.webp'),
('1321576','李圣旻',N'이성민','男','双子座','1985年,李圣旻作为话剧演员出道 。2006年,出演吕均洞执导的黑帮电影《丝绸鞋》,这是李圣旻首次担纲主演的电影作品,他在片中饰演性格直爽、脾气火爆的黑帮成员成哲。2018年,凭借谍战片《北风》获得第27届釜日电影奖最佳男演员奖、第55届大钟奖最佳男主角奖。','p1583915737.65.webp'),
('1055887','洪金宝','Sammo','男','摩羯座','洪金宝于香港出生,十岁时便拜入于占元门下学习京剧,是七小福中的大师兄,艺名为“元龙”。洪金宝早于十二岁时已开始涉足电影,当时是以艺名“朱元龙”之名义当童星,出演的电影有《爱的教育》、《大小黄天霸》、《岳飞出世》等等。','p838.webp'),
('1018257','樊少皇','Siu-Wong Fan','男','双子座','樊少皇自三岁开始,便随邵氏著名武打演员的父亲樊梅生接触电影圈,他由童星演起,第一部参与作品是电影《法网难逃》,之后他以童星身分续拍电影《再见妈咪》及《听不到的说话》。直到十三岁,他跟随父亲到徐州工作,在当地生活了三年,父亲不但请私人教师教他读书,学习日文和吉他,更让已立志成为武打演员的他,正式学习武术,为进军娱乐圈铺路。','p1384965537.89.webp'),
('1009368','迈克·泰森','Mike Tyson','男','巨蟹座','没有介绍','p27423.webp'),
('1318005','张晋','Max Zhang','男','金牛座','张晋(1974年5月19日-),中国大陆男演员。中国武英级运动员,9岁习武,11岁进入四川省武术队,先后取得全国武术比赛陈式太极拳和太极剑冠军,枪术、剑术冠军,八卦掌、醉剑、对练亚军,在第七、八届全国运动会上获得金牌和银牌。1998年退役后加入著名导演袁和平的“袁家班”做替身和武术指导。曾为不少演员做替身,并担任他们的武术教练。2000年从幕后走向台前,拍摄了《水月洞天》《灵镜传奇》《红七军》《流浪汉世界杯》《爱情维修站》等多部影视作品。','p1436716618.28.webp'),
('1314321','吴樾','Yue Wu','男','金牛座','吴樾是国内唯一一位多次获得国内外武术比赛的冠军,被授予国家武术最高级别(武英级), 又以全国表演专业第一名的成绩考入中央戏剧学院,毕业后以表演专业第一名的成绩成为中国国家话剧院的专业演员。','p45924.webp'),
('1201063','吴建豪','Vanness Wu','男','狮子座','吴建豪,美籍华裔的台湾艺人,同时是经典偶像组合F4的成员之一。2001年,吴建豪在拍摄偶像剧《流星花园》后与其他三位主角组成F4,急速窜红亚洲各地。2004年,吴建豪拜师少林寺学艺,接受资深武僧专业训练,成为少林俗家弟子,法号延来。2006年,与韩国人气歌手安七炫组成亚洲首支跨国组合Kangta&Vanness。','p1366445006.32.webp');


-- 创建电影表
create table movie (
	MovieId varchar(10) primary key,
	MChineseName varchar(50) not null,  
	MOriginalName nvarchar(50), 
	MRating float,
	MType varchar(30),
	MReleaseDate varchar(60),
	MLanguage varchar(30),
	MCountry varchar(30),
	MLength int,
	MDigest text,
	MCover varchar(30)
);

-- 插入电影信息到movie表
insert movie values
('26348103','小妇人','Little Women',8.1,'爱情','2020(中国大陆) / 2019-12-25(美国)','英语','美国',135,'电影改编自世界名著《小妇人》,由奥斯卡提名最佳导演格雷塔.葛伟格执导,直击当代女性困惑。作为2020年颁奖季热门,影片聚齐两代神仙阵容“世纪大同框”——金球奖最佳女主“伯德小姐”西尔莎·罗南与屡获提名的超人气演员“甜茶”再度携手诠释错过的真爱,“赫敏”艾玛·沃森与“黑寡妇师妹”弗洛伦斯·皮尤联袂呈现手足情深,三获奥斯卡的老戏骨梅尔·斯特里普与四次金球奖得主劳拉·邓恩倾力加盟。导演葛伟格用细腻又颇具新意的叙事手法,为这部经典文学名作带来充满活力的现代风格。','p2575325937.webp'),
('30252495','1917',NULL,8.5,'战争','2020(中国大陆) / 2019-12-25(美国) / 2020-01-10(英国)','英语 / 法语 / 德语','英国 / 美国',119,'1917年,第一次世界大战进入最激烈之际,两个年仅16岁的英国士兵接到的命令,需立即赶往死亡前线,向那里的将军传达一个“立刻停止进攻”讯息。 时间只有八小时,武器弹药有限,无人知晓前方敌况:死亡寂静之地、布满尸体的铁丝网、突入其来的敌军、随时毙命的危险境况…… 这一次两个少年为救1600个人的性命,不完成,毋宁死!','p2587146023.webp'),
('30241298','南山的部长们',N'남산의 부장들',8.1,'爱情','2020-01-22(韩国)','韩语','韩国',114,'电影"南山的部长们"以韩国中央情报部(KCIA)的部长们(副总理级)与他们所主导的政治阴谋为素材, 原作为揭露韩国政治内幕的同名小说,以历代中央情报部的部长金载圭和金炯旭的故事为中心重新创作而成。1979年下半年,YH贸易女工笼城事件、新民党总裁金泳三的国会议员除名事件以及10月的釜马事件先后爆发,政局不定。中央情报部金载圭和强硬派总统警卫室室长发生摩擦,朴正熙对他逐渐反感和不信任。1979年10月26日,在汉城钟路区宫井洞中央情报部官邸中,朴正熙和总统警卫室室长在宴会酒席中被金载圭用手枪枪杀。','p2581888792.webp'),
('27119724','小丑','Joker',8.7,'惊悚 / 犯罪','2019-08-31(威尼斯电影节) / 2019-10-04(美国)','英语','美国 / 加拿大',122,'湿冷无望的哥谭市,卑微的亚瑟·弗兰克(华金·菲尼克斯 Joaquin Phoenix 饰)依靠扮演小丑赚取营生。与之相依为命的母亲患有精神疾病,而亚瑟深记母亲的教诲,无论遭受怎样的挫折都笑对人生,却因此让自己背负着莫大的压力,濒临崩溃。他梦想成为一名脱口秀演员,怎奈生活一次次将失望狠狠地砸在他的头上。不仅如此,他因意外丢掉了工作,偶然瞥见母亲的秘密,又使他心中燃起对那个与之地位悬殊却从未谋面的父亲的殷切渴望。命运习惯了事与愿违,空荡荡的地铁内,悲伤的小丑在无法自已的癫狂笑声中大开杀戒……','p2567198874.webp'),
('3578981','叶问2:宗师传奇','Ip Man2',7.3,'动作 / 传记 / 历史','2010-04-27(中国大陆)','粤语 / 汉语普通话 / 英语','中国大陆 / 中国香港',114,'电影"南山的部长们"以韩国中央情报部(KCIA)的部长们(副总理级)与他们所主导的政治阴谋为素材, 原作为揭露韩国政治内幕的同名小说,以历代中央情报部的部长金载圭和金炯旭的故事为中心重新创作而成。1979年下半年,YH贸易女工笼城事件、新民党总裁金泳三的国会议员除名事件以及10月的釜马事件先后爆发,政局不定。中央情报部金载圭和强硬派总统警卫室室长发生摩擦,朴正熙对他逐渐反感和不信任。1979年10月26日,在汉城钟路区宫井洞中央情报部官邸中,朴正熙和总统警卫室室长在宴会酒席中被金载圭用手枪枪杀。','p1556481862.webp'),
('11598977','叶问3','Ip Man3',6.5,'剧情 / 动作 / 传记 / 历史','2015-12-24(中国香港) / 2016-03-04(中国大陆)','粤语 / 汉语普通话 / 英语','中国大陆 / 中国香港',105,'已在香港立足开馆的叶问(甄子丹 饰)与太太张永成(熊黛林 饰)平静地生活,不料小儿子叶正就读的小学发生了流氓滋扰事件,参与闹事的正是田傲山(梁家仁 饰)昔日的徒弟马鲸笙(谭耀文 饰)。于是叶问带领徒弟徐力(张继聪 饰)等人保护了黄老师(吴千语 饰)、校长(刘以达 饰)以及众小学生,并结识了同样是永春传人的车夫张天志(张晋 饰),二人惺惺相惜。可是事件却没有止步于此,马鲸笙的老板(迈克.泰森 饰)才是幕后黑手,探长肥波(郑则仕 饰)也卷入了其中,与此同时,张天志向叶问发起了谁是“正宗永春”的挑战,叶问该如何应战?','p2322954776.webp'),
('26885074','叶问4:完结篇','Ip Man4',7.0,'动作 / 传记 / 历史','2019-12-20(中国大陆)','粤语 / 英语','中国香港',107,'因故来到美国唐人街的叶问,意外卷入一场当地军方势力与华人武馆的纠纷,面对日益猖狂的民族歧视与压迫,叶问挺身而出,在美国海军陆战队军营拼死一战,以正宗咏春,向世界证明了中国功夫。','p2577437186.webp');

create table movieworker (
	movieId varchar(10) not null,
	workerId char(7) not null,
	Number int
);

-- 插入数据到电影工作人员表
insert movieworker values
('26348103','1022652',0),
('26348103','1022004',1),
('26348103','1053624',2),
('26348103','1378921',3),
('30252495','1048021',0),
('30252495','1032148',1),
('30252495','1341989',2),
('30241298','1340565',0),
('30241298','1105108',1),
('30241298','1321576',2),
('27119724','1036390',0),
('27119724','1047979',1),
('27119724','1054445',2),
('27119724','1116943',3),
('27119724','1370059',4),
('3578981','1274472',0),
('3578981','1025194',1),
('3578981','1055887',2),
('3578981','1018257',3),
('11598977','1274472',0),
('11598977','1025194',1),
('11598977','1009368',2),
('11598977','1318005',3),
('26885074','1274472',0),
('26885074','1025194',1),
('26885074','1314321',2),
('26885074','1201063',3);

2. 准备image、css、js等静态资源文件

图片名跟 moive 表中字段 MCover 字段值保持一致
在这里插入图片描述
css、js目录中主要包含 bootstrap 文件

在这里插入图片描述
在这里插入图片描述
字体文件在fonts目录中

在这里插入图片描述

3. 项目结构

大体的项目结构如图
在这里插入图片描述

三、开发步骤

1. 新建项目

使用 SpringBoot 框架,建立项目名为 douban_like 的 SpringBoot 项目

在这里插入图片描述
在这里插入图片描述

2. pom.xml

加入 MyBatis 与 MySql 的起步依赖

<!--mybatis 起步依赖: mybatis 框架需要的依赖全部加入好-->
<dependency>
   <groupId>org.mybatis.spring.boot</groupId>
   <artifactId>mybatis-spring-boot-starter</artifactId>
   <version>2.1.4</version>
</dependency>

<!--mysql 驱动-->
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <scope>runtime</scope>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>

3. 实体类 model

我们创建电影表 movie 、演员表 worker 在 Java 程序中的映射对象,即创建它们俩的实体类,实体类属性名与表中字段名保持一致。

package com.fancy.douban_like.model;

import java.util.List;


public class Movie {

    private String MovieId; //电影编号
    private String MChineseName; //电影中文名
    private String MOriginalName; //电影原名
    private float MRating; //电影评分
    private String MType; //电影类型
    private String MReleaseDate; //电影上映日期
    private String MLanguage; //电影语言
    private String MCountry; //电影制作国家
    private int MLength; //电影时长
    private String MDigest; //电影剧情介绍
    private String MCover; //电影封面图片
    private List<Worker> workerList; //电影工作人员列表-包括导演和演员

    public Movie() {
    }

    public Movie(String movieId, String MChineseName, float MRating, String MCover) {
        MovieId = movieId;
        this.MChineseName = MChineseName;
        this.MRating = MRating;
        this.MCover = MCover;
    }

    public Movie(String movieId, String mchineseName, float MRating, String MDigest, String MCover) {
        MovieId = movieId;
        MChineseName = mchineseName;
        this.MRating = MRating;
        this.MDigest = MDigest;
        this.MCover = MCover;
    }

    public Movie(String movieId, String mchineseName, String MOriginalName, float MRating, String MType, String MReleaseDate, String MLanguage, String MCountry, int MLength, String MDigest, String MCover, List<Worker> workerList) {
        MovieId = movieId;
        MChineseName = mchineseName;
        this.MOriginalName = MOriginalName;
        this.MRating = MRating;
        this.MType = MType;
        this.MReleaseDate = MReleaseDate;
        this.MLanguage = MLanguage;
        this.MCountry = MCountry;
        this.MLength = MLength;
        this.MDigest = MDigest;
        this.MCover = MCover;
        this.workerList = workerList;
    }

    public String getMovieId() {
        return MovieId;
    }

    public void setMovieId(String movieId) {
        MovieId = movieId;
    }

    public String getMChineseName() {
        return MChineseName;
    }

    public void setMChineseName(String MChineseName) {
        this.MChineseName = MChineseName;
    }

    public String getMOriginalName() {
        return MOriginalName;
    }

    public void setMOriginalName(String MOriginalName) {
        this.MOriginalName = MOriginalName;
    }

    public float getMRating() {
        return MRating;
    }

    public void setMRating(float MRating) {
        this.MRating = MRating;
    }

    public String getMType() {
        return MType;
    }

    public void setMType(String MType) {
        this.MType = MType;
    }

    public String getMReleaseDate() {
        return MReleaseDate;
    }

    public void setMReleaseDate(String MReleaseDate) {
        this.MReleaseDate = MReleaseDate;
    }

    public String getMLanguage() {
        return MLanguage;
    }

    public void setMLanguage(String MLanguage) {
        this.MLanguage = MLanguage;
    }

    public String getMCountry() {
        return MCountry;
    }

    public void setMCountry(String MCountry) {
        this.MCountry = MCountry;
    }

    public int getMLength() {
        return MLength;
    }

    public void setMLength(int MLength) {
        this.MLength = MLength;
    }

    public String getMDigest() {
        return MDigest;
    }

    public void setMDigest(String MDigest) {
        this.MDigest = MDigest;
    }

    public String getMCover() {
        return MCover;
    }

    public void setMCover(String MCover) {
        this.MCover = MCover;
    }

    public List<Worker> getWorkerList() {
        return workerList;
    }

    public void setWorkerList(List<Worker> workerList) {
        this.workerList = workerList;
    }
}
package com.fancy.douban_like.model;

public class Worker {

    private String WorkerId; //编号
    private String WName; //姓名
    private String WAnotherName; //其他名
    private String WGender; //性别
    private String WHoroscope; //星座
    private String WDescription; //介绍
    private String WImage; //头像图片

    public Worker() {
    }

    public Worker(String workerId, String WName, String WImage) {
        WorkerId = workerId;
        this.WName = WName;
        this.WImage = WImage;
    }

    public Worker(String workerId, String WName, String WAnotherName, String WGender, String WHoroscope, String WDescription, String WImage) {
        WorkerId = workerId;
        this.WName = WName;
        this.WAnotherName = WAnotherName;
        this.WGender = WGender;
        this.WHoroscope = WHoroscope;
        this.WDescription = WDescription;
        this.WImage = WImage;
    }

    public String getWorkerId() {
        return WorkerId;
    }

    public void setWorkerId(String workerId) {
        WorkerId = workerId;
    }

    public String getWName() {
        return WName;
    }

    public void setWName(String WName) {
        this.WName = WName;
    }

    public String getWAnotherName() {
        return WAnotherName;
    }

    public void setWAnotherName(String WAnotherName) {
        this.WAnotherName = WAnotherName;
    }

    public String getWGender() {
        return WGender;
    }

    public void setWGender(String WGender) {
        this.WGender = WGender;
    }

    public String getWHoroscope() {
        return WHoroscope;
    }

    public void setWHoroscope(String WHoroscope) {
        this.WHoroscope = WHoroscope;
    }

    public String getWDescription() {
        return WDescription;
    }

    public void setWDescription(String WDescription) {
        this.WDescription = WDescription;
    }

    public String getWImage() {
        return WImage;
    }

    public void setWImage(String WImage) {
        this.WImage = WImage;
    }
}

关系表则不用映射到Java程序中 。

4. Mapper 类

持久层 Mapper 类对象主要作用是从数据库中取出数据或者对前台数据进行持久化操作,其方法通常封装在service方法中,不直接进行暴露。

对于 movie 表,我们需要的方法有 :

  • 根据 name 对电影进行模糊查询
  • 根据 ID 查询电影
  • 查询所有电影

对应的 MovieMapper 接口:

package com.fancy.douban_like.mapper;

import com.fancy.douban_like.model.Movie;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface MovieMapper {

    Movie getMovieInfoById(String movieId);
    List<Movie> getAllMovies();
    List<Movie> getMoviesByKeyWords(String movieName);

}

mapper 映射文件

<?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.fancy.douban_like.mapper.MovieMapper">
    <select id="getMovieInfoById" parameterType="java.lang.String" resultType="com.fancy.douban_like.model.Movie" >
         select * from movie where MovieId = #{movieId};
    </select>

    <select id="getAllMovies" resultType="com.fancy.douban_like.model.Movie">
         select MovieId, MChineseName, MRating, MCover from movie;
    </select>

    <select id="getMoviesByKeyWords" parameterType="java.lang.String" resultType="com.fancy.douban_like.model.Movie">
        select MovieId, MChineseName, MRating, MDigest, MCover  from Movie where MChineseName like "%" #{movieName} "%";
    </select>
</mapper>

对于 worker 表,我们需要的方法有:

  • 根据电影 ID 查询该电影中的所有演员
  • 根据演员 ID 查询某个演员

对应的 WorkerMapper 接口:

package com.fancy.douban_like.mapper;

import com.fancy.douban_like.model.Worker;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface WorkerMapper {

    List<Worker> getWorkersByMovieId(String movieId);
    Worker getOneWorkerByWorkerId(String workerId);

}

mapper 映射文件

<?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.fancy.douban_like.mapper.WorkerMapper">
    <select id="getWorkersByMovieId" parameterType="java.lang.String" resultType="com.fancy.douban_like.model.Worker">
        select a.WorkerId,a.WName,a.WImage from Worker a, MovieWorker b  where a.WorkerId=b.WorkerId and b.MovieId=#{movieId};
    </select>

    <select id="getOneWorkerByWorkerId" parameterType="java.lang.String" resultType="com.fancy.douban_like.model.Worker">
        select * from Worker where WorkerId=#{workerId};
    </select>
</mapper>

5. service 类

在业务层我们所要做的就是将 mapper 接口的持久层方法封装到业务层方法中,并向控制器提供相关方法。

MovieService 接口:

package com.fancy.douban_like.service;

import com.fancy.douban_like.model.Movie;

import java.util.List;

public interface MovieService {

    List<Movie> findAllMovies();
    List<Movie> findMoviesByKeyWords(String keyword);
    Movie findMovieById(String movieId);

}

MovieServiceImpl 实现类:

package com.fancy.douban_like.service.impl;

import com.fancy.douban_like.mapper.MovieMapper;
import com.fancy.douban_like.mapper.WorkerMapper;
import com.fancy.douban_like.model.Movie;
import com.fancy.douban_like.service.MovieService;
import org.springframework.stereotype.Service;

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

@Service
public class MovieServiceImpl implements MovieService {

    @Resource
    MovieMapper movieMapper;

    @Resource
    WorkerMapper workerMapper;

    @Override
    public List<Movie> findAllMovies() {
       return movieMapper.getAllMovies();
    }

    @Override
    public List<Movie> findMoviesByKeyWords(String keyword) {
        return movieMapper.getMoviesByKeyWords(keyword);
    }

    @Override
    public Movie findMovieById(String movieId) {
        Movie movie = movieMapper.getMovieInfoById(movieId);
        movie.setWorkerList(workerMapper.getWorkersByMovieId(movieId));
        return movie;
    }
}

WorkerService 接口 :

package com.fancy.douban_like.service;

import com.fancy.douban_like.model.Worker;

public interface WorkerService {
    Worker findWorkerById(String workerId);
}

WorkerService 接口实现类:

package com.fancy.douban_like.service.impl;

import com.fancy.douban_like.mapper.WorkerMapper;
import com.fancy.douban_like.model.Worker;
import com.fancy.douban_like.service.WorkerService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class WorkerServiceImpl implements WorkerService {

    @Resource
    WorkerMapper workerMapper;


    @Override
    public Worker findWorkerById(String workerId) {
        return workerMapper.getOneWorkerByWorkerId(workerId);
    }
}

6. 首页 index.html 开发

在首页中我们需要展示所有电影,并且点击电影图片和文字做相应的跳转。

首先我们需要在控制器定义方法,当访问index首页时,从数据库中返回相关数据。

① MovieController

package com.fancy.douban_like.controller;

import com.fancy.douban_like.model.Movie;
import com.fancy.douban_like.service.MovieService;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

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

@RestController
public class MovieController {

    @Resource
    MovieService movieService;

    @GetMapping("/index")
    public ModelAndView getAllMovies() {
        List<Movie> movies = movieService.findAllMovies();
        ModelAndView mv = new ModelAndView();
        mv.addObject("movies", movies);
        mv.setViewName("index");
        return mv;
    }
}

② index.html

在 index.html 界面中, 我们将取出后台返回的数据。由于后台返回数据是个集合,我们需要对集合进行遍历,使用 Thymeleaf th:each 对集合进行遍历。对于图片的显示,我们用 th:src="@{/images/imgName(imgName=${movie.MCover})}",进行动态化拼接,使得所有电影信息都对应正确的显示出来。

然后我们需要完成的是,点击电影图片或文字,跳转到电影的详情页,这里我们同样是用Thymeleaf th:href 写入动态参数,且请求路径用的是 Restful 接口架构风格, 调用根据id查询电影信息的方法,代码如下

<a class="item" th:href="@{'/movie/id/' + ${movie.MovieId}}" target="_blank"></a>

完整的 index.html 代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>豆豆瓣-你心上的电影是哪一个</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"/>
    <link rel="stylesheet" th:href="@{/css/style.css}"/>
    <link rel="shortcut icon" href="https://img3.doubanio.com/favicon.ico" type="image/x-icon">
</head>
<body>
    <div class="container">
        <br>
        <a href="index" class="home btn-warning">豆豆瓣</a>

        <hr>
        <div class="text-center">
            <h2>豆豆瓣-你的电影库</h2>
            <br>
            <!--提交该表单,发送请求给GetNamedMovieServlet,根据电影名称关键字模糊查询所有电影-->
            <form class="form-inline" action="movie/name">
                <input type="text" id="input_wd" name="keyword" class="form-control" placeholder="输入电影关键字">
                <input type="submit" id="searchBtn1" class="btn btn-info" value="提交"></input>
            </form>
        </div>

        <br>
        <br>
        <br>
        <h3>最近热门电影</h3>
        <hr>
        <div class="movieGallery" th:each="movie:${movies}">

        <!--每个电影打印下面的一个a元素即可,显示该电影的所有信息,这里显示了一个固定的电影7次-->
        <!--点击超链接发送请求给GetMovieInfoServlet,根据电影编号查询单个电影的所有信息-->

            <a class="item" th:href="@{'/movie/id/' + ${movie.MovieId}}" target="_blank">
                <div>
                    <img th:src="@{/images/{imgName}(imgName=${movie.MCover})}" alt="${movie.MChineseName}}">
                </div>
                <p th:text="${movie.MChineseName} + ' ' + ${movie.MRating}">电影名 评分</p>

            </a>
        </div>
   </div>
</body>
</html>

7. 电影详情页 movie_info.html 开发

我们在 index.html 中点击电影的超链接会跳转到电影的详情页,我们需要在 Controller 中定义方法接收 movieId 进行查询,并将详细信息返回到 movie_info.html 界面。

① MovieController

我们在 MovieController 中定义 queryMovieById 方法,调用 MovieService 中 findMovieById 方法,从数据库中取出数据,返回前台。

@GetMapping("/movie/id/{movieId}")
public ModelAndView queryMovieById(@PathVariable String movieId) {
    Movie movie = movieService.findMovieById(movieId);
    ModelAndView mv = new ModelAndView();
    mv.addObject("movie", movie);
    mv.setViewName("movie_info");
    return mv;
}

② movie_list.html

这次后台返回的是单一对象,而不是列表,我们直接用 ${movie.属性名}接收即可,文本的话用
th:text 进行替换,图片的话同样是使用 th:src="@{}" 接收动态参数进行路径拼接。

同时,我们点击演员名字时,返回演员的相关信息,实现方式与上面相同,只不过这次返回的是 workerId,详见如下代码。

<!DOCTYPE html>
<html xmlns:display="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>电影信息</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"/>
    <link rel="stylesheet" th:href="@{/css/style.css}"/>
    <link rel="shortcut icon" href="https://img3.doubanio.com/favicon.ico" type="image/x-icon">
</head>
<body>


   <!--  获取电影的详细信息
         Movie类中的有个属性workerList,是个List<Worker>列表,用于存储电影的所有员工
         在查询一个电影的信息的时候,已经帮你们查询了该电影的所有员工,放入了workerList属性中
         其中列表中的第一个工作人员是导演,其他为演员,因为数据库对每个工作人员编了顺序,0为导演,1为最主要主演,2为次主要主演,以此类推
         你需要获取列表中的第一个工作人员,也就是导演,然后在对应位置显示导演信息-->


    <div class="container">

        <br>
        <a href="/douban/index" class="home btn-warning">豆豆瓣</a>

        <!--以下是显示了一个固定电影的信息,替换成查询到的电影的动态信息即可-->

        <hr>
        <h2 class="text-warning bg-warning padding10" th:if="${movie.MOriginalName == null}" th:text="'电影介绍:  ' + '  ' + ${movie.MChineseName} + '  '"></h2>
        <h2 class="text-warning bg-warning padding10" th:if="${movie.MOriginalName != null}" th:text="'电影介绍:  ' + '  ' + ${movie.MChineseName} + '  ' + ${movie.MOriginalName}"></h2>
        <hr>
        <div class="content">
            <div class="leftDiv">
                <img class="img-thumbnail" th:src="@{/images/{imgName}(imgName=${movie.MCover})}" alt="${movie.MChineseName}}">
            </div>
            <div class="rightDiv">
                <!--这里显示导演的超链接-->
                <!--点击这里的超链接,查询导演具体信息,并在worker_info.jsp页面显示导演详细信息-->
                <br>导演: <a target="_blank" th:href="@{'/worker/id/' + ${movie.workerList[0].WorkerId}}" th:text="${movie.workerList[0].WName}">导演名字</a>
                <br>主演:

<!--                这里遍历列表中第二个开始的工作人员,也就是显示所有演员的信息,然后在对应位置显示演员信息-->
<!--                i=1说明从列表中第二个Worker对象开始遍历,索引从0开始,这里显示了一个固定的演员信息4次-->

                <!--点击这里的超链接,查询演员具体信息,并在worker_info.jsp页面显示导演详细信息-->
                <span th:each="worker, workerStat:${movie.workerList}">
                    <a target="_blank" th:href="@{'/worker/id/' + ${worker.WorkerId}}" th:if="${workerStat.index != 0}" th:text="${worker.WName}">演员名字</a> /
                </span>

                <!--这里显示电影的其他信息-->
                <div th:text="'类型 : ' + ${movie.MType} ">类型</div>
                <div th:text="'制片国家/地区 : ' + ${movie.MCountry}">制品国家地区</div>
                <div th:text="'语言 : ' + ${movie.MLanguage}">语言</div>
                <div th:text="'上映日期 : ' + ${movie.MReleaseDate}">上映日期</div>
                <div th:text="'片场 : ' + ${movie.MLength}">片长</div>
                <div th:text="'豆瓣评分 : ' + ${movie.MRating}">豆瓣评分</div>
                <br><br>
            </div>
            <div class="bottomDiv">
                <h3 class="padding10 text-success bg-success" th:text="${movie.MChineseName} + ' 剧情简介 · · · · · ·'">电影名称 剧情简介 · · · · · ·</h3>
                <hr>
                <p th:text="${movie.MDigest}"></p><a href="#">查看更多</a>
                <hr>
            </div>
        </div>

        <h3 class="text-danger bg-danger padding10" th:text="${movie.MChineseName} + ' 工作人员'">电影名称 工作人员</h3>
        <hr>
        <div class="workerGallery" th:each="worker, workerStat:${movie.workerList}">

<!--          这里重新遍历了一下电影的员工列表,包括导演,用图片超链接的形式再显示一次导演和演员信息-->

            <!--点击这里的超链接,可以查看每个员工(导演或演员)的详细信息-->
            <a class="item" target="_blank" th:href="@{'/worker/id/' + ${worker.WorkerId}}">
                <div class="cover-wp">
                    <img th:src="@{/images/{imgName}(imgName=${worker.WImage})}" alt="${worker.WName}" data-x="3000" data-y="4454">
                </div>
                <p th:text="${worker.WName}"></p>
            </a>
        </div>

    </div>

</body>
</html>

8. 人物详情页 worker_info.html 开发

① WorkerController

在 WorkerController 中,我们调用 WorkerService 类中的 findWorkerById 方法,传入前台参数 workerId,从数据库中返回数据给 worker_info.html 界面。

package com.fancy.douban_like.controller;


import com.fancy.douban_like.mapper.WorkerMapper;
import com.fancy.douban_like.model.Worker;
import com.fancy.douban_like.service.WorkerService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;

@RestController
public class WorkerController {

   @Resource
   WorkerService workerService;


   @GetMapping("/worker/id/{workerId}")
   public ModelAndView queryWorkerById(@PathVariable String workerId) {
      Worker worker = workerService.findWorkerById(workerId);
      ModelAndView mv = new ModelAndView();
      mv.addObject("worker", worker);
      mv.setViewName("worker_info");
      return mv;
   }
}

② worker_info.html

此界面开发用到的 Thymeleaf 技术同上

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>工作人员信息</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"/>
    <link rel="stylesheet" th:href="@{/css/style.css}"/>
    <link rel="shortcut icon" href="https://img3.doubanio.com/favicon.ico" type="image/x-icon">
</head>
<body>

<div class="container">

    <br>
    <a href="/douban/index" class="home btn-warning">豆豆瓣</a>

    <hr>
    <h2 class="text-warning bg-warning padding10" th:text="'名人介绍 : ' + ${worker.WName}  + ' ' + ${worker.WAnotherName}">名人介绍: 导演或演员名字 导演或演员名字另一个名字</h2>
    <hr>
    <div class="content">
        <div class="leftDiv">
            <img class="img-thumbnail" th:src="@{/images/{imgName}(imgName=${worker.WImage})}" alt="${worker.WName}">
        </div>
        <div class="rightDiv">
            <br><br>
            <div th:text="'姓名 : ' + ${worker.WName}">姓名: 导演或演员名字</div>
            <div th:text="'又名 : ' + ${worker.WAnotherName}">又名: 导演或演员名字另一个名字</div>
            <div th:text="'性别 : ' + ${worker.WGender}">性别: 导演或演员性别</div>
            <div th:text="'星座 : ' + ${worker.WHoroscope}">星座: 导演或演员名字星座</div>
            <br><br>
        </div>
        <div class="bottomDiv">
            <h3 class="padding10 text-success bg-success" th:text="${worker.WName} + ' 个人简介 · · · · · ·'">导演或演员名字个人简介 · · · · · ·</h3>
            <hr>
            <p th:text="${worker.WDescription}"></p>
        </div>
    </div>
</div>

</body>
</html>

9. 电影查询列表页面 movie_list.html 开发

我们在首页表单的文本框中,输入name,点击提交,通过模糊查询将含有此name的所有电影给列出来。

① 模糊查询代码

在mapper映射文件中,我们已经给出

<select id="getMoviesByKeyWords" parameterType="java.lang.String" resultType="com.fancy.douban_like.model.Movie">
     select MovieId, MChineseName, MRating, MDigest, MCover  from Movie where MChineseName like "%" #{movieName} "%";
</select>

② MovieController

在控制器中增加方法 queryMoviesByName ,核心实现是调用 MovieService类中的 findMoviesByKeyWords 方法。

@GetMapping("/movie/name")
public ModelAndView queryMoviesByName(String keyword) {
    List<Movie> movies = movieService.findMoviesByKeyWords(keyword);
    ModelAndView mv = new ModelAndView();
    mv.addObject("movies", movies);
    mv.setViewName("movie_list");
    return mv;
}

③ movie_list.html

实现形式与上面几个界面基本相同,只有当点击 查看更多 按钮时,会携带参数 movieId 发生请求到电影详情页的相关controller。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>电影查询结果</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"/>
    <link rel="stylesheet" th:href="@{/css/style.css}"/>
    <link rel="shortcut icon" href="https://img3.doubanio.com/favicon.ico" type="image/x-icon">
    <script th:src="@{/js/jquery-3.4.1.min.js}"></script>
</head>
<body>
<div class="container">

    <br>
    <a href="/douban/index" class="home btn-warning">豆豆瓣</a>

    <hr>
    <h2 class="text-warning padding10 bg-warning text-center">电影查询结果</h2>
    <hr>

    <div class="movieGallery" th:each="movie:${movies}">

     <!--   获取查询到的电影的列表,遍历每个电影的信息,并在对应位置显示
        遍历电影列表,每个电影打印一个div显示该电影的基本信息,在对应位置显示电影的对应信息即可
        这里显示了一个固定的电影4次-->
        <div class="content">
            <div class="leftDiv">
                <!--点击这里的超链接可以显示电影的详细信息-->
                <a target="_blank" th:href="@{'/movie/id/' + ${movie.MovieId}}" >
                    <img target="_blank" class="img-thumbnail" th:src="@{/images/{imgName}(imgName=${movie.MCover})}" alt="${movie.MChineseName}}">
                </a>

            </div>
            <div class="rightDiv">
                电影名称:
                <!--点击这里的超链接可以显示电影的详细信息-->
                <a target="_blank" th:href="@{'/movie/id/' + ${movie.MovieId}}" class="bg-success text-success padding5" th:text="'      ' + ${movie.MChineseName}"></a>
                <br>
                <div th:text="'豆瓣评分 : ' + ${movie.MRating}">豆瓣评分</div>
                <h4 class="padding5 text-info bg-info" th:text="${movie.MChineseName} + ' ' + '剧情简介· · · · · ·'">剧情简介 · · · · · ·</h4>
                <p th:text="${movie.MDigest}">
                    电影简介
                    <!--点击这里的超链接可以显示电影的详细信息-->
                </p><a target="_blank"  th:href="@{'/movie/id/' + ${movie.MovieId}}">查看更多</a>
                <br>
            </div>
        </div>
        <hr class="clear">
    </div>

</div>
</body>
</html>

四、项目提取

此项目我放到了 GitHub 和 Gitee 上

GitHub 地址 :https://github.com/diving-into/douban_like

Gitee 地址 :https://gitee.com/fancyry/douban_like

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

在森林中麋了鹿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值