本文主要是围绕Thymeleaf,需要有一定的Springboot和mybatis的基础
1.首先了解一下Thymeleaf
1.1 简介
简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。
从代码层次上讲:Thymeleaf是一个java类库,他是一个xml/xhtml/html5的模板引擎,可以作为mvc的web应用的view层。
1.2 基本语法
表达式
表达式名字 | 语法 | 用途 |
变量取值 | ${...} | 获取请求域、session域、对象等值 |
选择变量 | *{...} | 获取上下文对象值 |
消息 | #{...} | 获取国际化等值 |
链接 | @{...} | 生成链接 |
片段表达式 | ~{...} | jsp:include 作用,引入公共页面片段 |
th属性
html有的属性,Thymeleaf基本都有,而常用的属性大概有七八个。其中th属性执行的优先级从1~8,数字越低优先级越高。
1. th:text :设置当前元素的文本内容,相同功能的还有th:utext,两者的区别在于前者不会转义html标签,后者会。优先级不高:order=7
2. th:value:设置当前元素的value值,类似修改指定属性的还有th:src,th:href。优先级不高:order=6
3. th:each:遍历循环元素,和th:text或th:value一起使用。注意该属性修饰的标签位置。优先级很高:order=2
4. th:if:条件判断,类似的还有th:unless,th:switch,th:case。优先级较高:order=3
5. th:insert:代码块引入,类似的还有th:replace,th:include,三者的区别较大,若使用不恰当会破坏html结构,常用于公共代码块提取的场景。优先级最高:order=1
6. th:fragment:定义代码块,方便被th:insert引用。优先级最低:order=8
7. th:object:声明变量,一般和*{}一起配合使用,达到偷懒的效果。优先级一般:order=4
8. th:attr:修改任意属性,实际开发中用的较少,因为有丰富的其他th属性帮忙,类似的还有th:attrappend,th:attrprepend。优先级一般:order=5
注意使用Thymeleaf一定要加上命名空间 xmlns:th="http://www.thymeleaf.org"
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
关系运算符
gt:great than(大于)
ge:great equal(大于等于)
eq:equal(等于)
lt:less than(小于)
le:less equal(小于等于)
ne:not equal(不等于)
1.3springBoot中使用Thymeleaf
导入starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在这个路径下可以找到Thymeleaf在springboot中的自动装配类
@AutoConfiguration(
after = {WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class}
)
@EnableConfigurationProperties({ThymeleafProperties.class})
@ConditionalOnClass({TemplateMode.class, SpringTemplateEngine.class})
@Import({ReactiveTemplateEngineConfiguration.class, DefaultTemplateEngineConfiguration.class})
public class ThymeleafAutoConfiguration {
}
可以看到这个配置类是和ThymeleafProperties进行了配置绑定
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
private Integer templateResolverOrder;
private String[] viewNames;
private String[] excludedViewNames;
private boolean enableSpringElCompiler;
private boolean renderHiddenMarkersBeforeCheckboxes;
private boolean enabled;
private final ThymeleafProperties.Servlet servlet;
private final ThymeleafProperties.Reactive reactive;
}
可以看到springboot已经默认给我们配置视图解析器的前缀和后缀,如果我们想自定义配置,则可以在application.properties文件中以spring.Thymeleaf为前缀来修改配置
2.springboot+mybatis+Thymeleaf小练习
上面已经对Thymeleaf有了基本的了解,接下来就写一个小练习
效果如下图所示,在展示页面可以通过球队名称和球员名称进行模糊查询并且分页
删除采用异步方法,删除后直接从table移除当前行
在添加页面所属球队的select框通过后台数据动态绑定,添加成功后返回首页展示页面
2.1新建一个springboot项目,并且引入如下依赖(我的boot版本为2.7.5大家可以根据自己版本进行调整)
<!-- thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- springbootweb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--springboot配置提醒-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Lombok简化实体类开发 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- SpringBoot测试单元 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.4</version>
</dependency>
2.2 建立数据库,我这里的数据库名叫nbaplayers,相对应字段在下面实体类有注释
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for clubs
-- ----------------------------
DROP TABLE IF EXISTS `clubs`;
CREATE TABLE `clubs` (
`cid` int(0) NOT NULL AUTO_INCREMENT,
`cname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`city` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`cid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of clubs
-- ----------------------------
INSERT INTO `clubs` VALUES (1, '太阳', '纽约');
INSERT INTO `clubs` VALUES (2, '湖人', '宾夕法尼亚');
INSERT INTO `clubs` VALUES (3, '勇士', '洛杉矶');
-- ----------------------------
-- Table structure for players
-- ----------------------------
DROP TABLE IF EXISTS `players`;
CREATE TABLE `players` (
`pid` int(0) NOT NULL AUTO_INCREMENT,
`pname` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`birthday` date NULL DEFAULT NULL,
`height` int(0) NULL DEFAULT NULL,
`weight` int(0) NULL DEFAULT NULL,
`position` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`cid` int(0) NULL DEFAULT NULL,
PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of players
-- ----------------------------
INSERT INTO `players` VALUES (1, '大卫', '1997-02-05', 182, 182, '小前锋', 1);
INSERT INTO `players` VALUES (2, '彼得', '1982-05-02', 193, 156, '大前锋', 1);
INSERT INTO `players` VALUES (5, '柯南', '1978-05-06', 188, 162, '大前锋', 3);
INSERT INTO `players` VALUES (6, '博客', '1986-08-06', 185, 175, '小前锋', 3);
INSERT INTO `players` VALUES (9, '猪', '2022-11-08', 23, 123, '2', 2);
INSERT INTO `players` VALUES (11, '彼得', '2022-11-14', 123, 23, '1', 1);
INSERT INTO `players` VALUES (12, '彼得', '2022-12-15', 100, 25, '1', 2);
INSERT INTO `players` VALUES (14, '彭志', '2001-03-25', 140, 280, '中锋', 1);
SET FOREIGN_KEY_CHECKS = 1;
2.3设置配置文件
spring:
#配置数据源
datasource:
url: jdbc:mysql://localhost:3306/nbaplayers?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT-8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
#配置mybatis规则
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
#自动将下划线命名映射为驼峰命名
map-underscore-to-camel-case: true
#开启延迟加载
lazy-loading-enabled: true
#类型别名
type-aliases-package: com.example.day_01_thymleaf.pojo
#配置分页插件
pagehelper:
helperDialect: mysql
reasonable: true
params: count=countSql
整体项目结构如下
相关实体类
//球员类
public class Players {
//球员id
private Integer pid;
//球员名称
private String pname;
//球员生日
private String birthday;
//球员身高
private Integer height;
//球员体重
private Integer weight;
//球员定位
private String position;
//球队id
private Integer cid;
//所属球队名称
private String cname;
}
@Data
//球队类
public class Clubs {
//球队id
private Integer cid;
//球队名称
private String cname;
//所属城市
private String city;
}
然后建立相关的service mapper
相关前端页面
main页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script th:src="@{/js/jquery.js}"></script>
<style>
a{
margin-left: 50px;
}
h2{
color: pink;
font-family: 楷体;
}
</style>
</head>
<body>
<h1 style="margin: auto">NBA篮球管理</h1>
<form th:action="@{/plays}" method="get">
球员姓名 : <input type="text" name="pname" th:value="${session.pname}">
球队名称 : <select name="typeId" >
<option value="" >请选择</option>
<option th:each="club : ${session.clubs}" th:text="${club.cname}" th:value="${club.cid}" th:selected="${session.typeId} eq ${club.cid}"></option>
</select>
<input type="submit" value="搜索">
</form>
<a th:href="@{/add}">新增球员</a>
<table>
<tr>
<th>球队编号</th>
<th>球员名称</th>
<th>出生时间</th>
<th>球员身高</th>
<th>球员体重</th>
<th>球员位置</th>
<th>所属球队</th>
<th>相关操作</th>
</tr>
<tr th:each="p : ${session.page.list}">
<td th:text="${p.pid}" th:id="${p.pid}"></td>
<td th:text="${p.pname}"></td>
<td th:text="${p.birthday}"></td>
<td th:text="${p.height}"></td>
<td th:text="${p.weight}"></td>
<td th:text="${p.position}"></td>
<td th:text="${p.cname}"></td>
<td>
<button th:pid="${p.pid}" th:onclick="deletePlayer(this.getAttribute('pid'))">删除</button>
</td>
</tr>
</table>
<a th:href="@{/plays?pageNum=1}" th:if="${session.page.hasPreviousPage}">首页</a>
<a th:href="'/plays?pageNum='+${session.page.prePage}" th:if="${session.page.hasPreviousPage}">上一页</a>
<a th:href="'/plays?pageNum='+${session.page.nextPage}" th:if="${session.page.hasNextPage}">下一页</a>
<a th:href="'/plays?pageNum='+${session.page.pages}" th:if="${session.page.hasNextPage}">尾页</a>
<h2>第 <span th:text="${session.page.pageNum}"></span>页</h2>
<h2>共 <span th:text="${session.page.total}"></span>条记录 </h2>
</body>
<script>
function deletePlayer(id){
$.ajax({
url:"./delete/"+id, //请求的url地址
dataType:"text", //返回格式为json
type:"GET", //请求方式
success:function(req){
if(req=="true"){
alert("删除成功")
$("#"+id).parent().remove()
}
},
});
}
</script>
</html>
add页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 style="text-align: center">添加球员信息</h1>
<form th:action="@{/plays}" method="post" style="margin: auto;background-color: #9ddf9d;text-align: center;font-size: 25px">
球员姓名: <input type="text" name="pname"> <br>
出生时间: <input type="text" name="birthday">(yyyy-MM-dd) <br>
球员身高: <input type="text" name="height"> <br>
球员体重: <input type="text" name="weight"> <br>
球员位置: <input name="position" type="radio" value="控球后卫"> 控球后卫
<input name="position" type="radio" value="得分后卫"> 得分后卫
<input name="position" type="radio" value="小前锋"> 小前锋
<input name="position" type="radio" value="大前锋"> 大前锋
<input name="position" type="radio" value="中锋"> 中锋
<br>
所属球队 : <select name="cid" >
<option value="" >请选择</option>
<option th:each="club : ${session.clubs}" th:text="${club.cname}" th:value="${club.cid}"></option>
</select>
相关操作 :<input type="submit" value="新增"> <input type="button" onclick="window.history.back()" value="返回">
</form>
</body>
</html>
2.4编写Controller
因为我们使用了模板引擎,所以我们编写一个请求方法,将 / 请求重定向到plays请求上去,默认加载相关数据,因为在展示页和添加页面都需要球队数据,所以我们把球队数据存入session中,其中删除方法为异步调用,所以一定要加上@ResponseBody注解,才能给前端返回数据。否则返回的是一个逻辑视图名称。
@Slf4j
@Controller
public class MainController {
@Autowired
private PlayersService playersService;
@Autowired
private ClubsService clubsService;
/**
* 启动项目默认访问main页面
* @return
*/
@GetMapping("/")
public String index(){
return "redirect:/plays";
}
@GetMapping("plays")
public String getAllPlays(@RequestParam(required = false) Integer pageNum,
@RequestParam(required = false) String typeId,
@RequestParam(required = false)String pname,
HttpSession session){
//判断执行的是分页还是查询
//如果查询则分页为空
Object clubs = session.getAttribute("clubs");
if(clubs==null){
session.setAttribute("clubs",clubsService.getAllClubs());
}
if(pageNum==null){
pageNum=1;
session.setAttribute("typeId",typeId);
session.setAttribute("pname",pname);
}else {
//如果分页不为空则执行的是分页
typeId=(String) session.getAttribute("typeId");
pname=(String) session.getAttribute("pname");
}
PageInfo<Players> page = playersService.getAllPlays(pageNum, typeId, pname);
session.setAttribute("page",page);
return "main";
}
/**
* 根据id删除
* @param id
* @return
*/
@GetMapping("/delete/{id}")
@ResponseBody
public String deletePlays(@PathVariable("id")Integer id){
if(playersService.deleteByID(id)>0){
return "true";
}else {
return "false";
}
}
/**
* 添加球员
* @param players
* @return
*/
@PostMapping("plays")
public String addPlays(Players players){
return playersService.addPlays(players);
}
/**
* 跳转到新增页面
* @return
*/
@GetMapping("/add")
public String toAdd(){
return "add";
}
}