SpringBoot + Thymeleaf + Bootstrap5 整合 MyBatis-Plus 和 MySQL 实现动态分类标签渲染教程
教程概述
继上一篇文章SpringBoot整合Thymeleaf与Bootstrap5:快速构建导航栏并抽取公共代码,教学了SpringBoot整合Thymeleaf与Bootstrap5快速的实现了一个网站的导航栏,当然,只是一个展示。本教程接上一个项目,实现整合MyBatisPlus、MySQL,将标签分类信息存放于数据库,完成网站中分类标签的动态模板渲染。
原文链接:SpringBoot+Thymeleaf+Bootstrap5整合MyBatisPlus、MySQL实现分类标签的动态渲染
最后的结果如下图
教程步骤
1.导入MyBatisPlus、MySQL依赖
2.创建Mapper的目录
3.配置MySQL、MyBatisPlus
4.创建标签页面,并在主页引入
5.创建数据库、建表,构造数据
6.创建视图对象
7.创建响应数据工具类
8.修改主页的控制器
9.查看主页
10.修改标签页动态获取分类标签
步骤1:导入MyBatisPlus、MySQL依赖
导入如下依赖,这里还需要额外导入jackson-core的依赖,因为本教程的分类子标签是以json形式存放于数据库中,需要使用该库对其进行转换
<!-- MyBatis-Plus Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jackson-core-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
如图
步骤2:创建Mapper的目录
在resources目录下,我这里创建了一个名为dao的目录,用于存放mapper的xml文件,如图
步骤3:配置MySQL、MyBatisPlus
在application.yaml中,添加上mysql的配置与mybatisplus的配置,接上次的thymeleaf完整如下,注意将mysql信息配置正确
spring:
thymeleaf:
cache: false
encoding: UTF-8
prefix: classpath:/templates/
suffix: .html
mode: HTML5
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/you_resource?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 159357
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:/dao/*.xml
如图
步骤4:创建标签页面
虽然称为标签页面,但是是用于给主页引入的,之所以抽取出来,是考虑到后续其他页面可能会使用,甚至表的结构也会修改,当然现在不考虑那么多,怎么块怎么来。
在templates/front/commons下创建NavTab.html,内容如下
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<div class="container mt-4 bg-white p-4 rounded">
<ul class="nav nav-pills flex-wrap list-unstyled mb-4">
<!-- 独立的一行,显示 A分类 -->
<li class="nav-item w-100 border-bottom pb-2 mb-3">
<span class="nav-link p-0 m-0 d-flex align-items-center text-dark">
<i class="bi bi-align-bottom me-2" style="font-size: 1.25rem;"></i>
<span>源码分类</span>
</span>
</li>
<!-- 其他分类项 -->
<li class="nav-item border-end pe-3 me-3">
<a class="text-dark text-decoration-none" href="#">分类1</a>
</li>
<li class="nav-item border-end pe-3 me-3">
<a class="text-dark text-decoration-none" href="#">分类2</a>
</li>
<li class="nav-item border-end pe-3 me-3">
<a class="text-dark text-decoration-none" href="#">分类3</a>
</li>
</ul>
<ul class="nav nav-pills flex-wrap list-unstyled mb-4">
<!-- 独立的一行,显示 A分类 -->
<li class="nav-item w-100 border-bottom pb-2 mb-3">
<span class="nav-link p-0 m-0 d-flex align-items-center text-dark">
<i class="bi bi-align-bottom me-2" style="font-size: 1.25rem;"></i>
<span>网创分类</span>
</span>
</li>
<!-- 其他分类项 -->
<li class="nav-item border-end pe-3 me-3">
<a class="text-dark text-decoration-none" href="#">分类1</a>
</li>
<li class="nav-item border-end pe-3 me-3">
<a class="text-dark text-decoration-none" href="#">分类2</a>
</li>
<li class="nav-item border-end pe-3 me-3">
<a class="text-dark text-decoration-none" href="#">分类3</a>
</li>
</ul>
</div>
</body>
</html>
在主页,也就是index.html中引入该文件
主要是这一句
<div th:replace="~{front/commons/NavTab.html}"></div>
完整代码为
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>test</title>
<link rel="stylesheet" href="/bootstrap-5.3.0/css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
</head>
<body>
<nav th:replace="~{front/commons/HeadNav.html}"></nav>
<!-- 使用Bootstrap的容器 -->
<div class="container-fluid p-0">
<!-- Banner容器 -->
<div class="position-relative">
<!-- 图片 -->
<img src="/front/banner1.webp" class="img-fluid w-100" alt="Banner Image" style="height: 200px; object-fit: cover;">
<!-- 文字覆盖 -->
<div class="position-absolute top-50 start-50 translate-middle text-center text-white">
<h1 class="fw-bold">欢迎来到我们的站点</h1>
<p class="lead">这是一个精美的横幅展示区域</p>
</div>
</div>
</div>
<div th:replace="~{front/commons/NavTab.html}"></div>
<!--卡片-->
<div class="row row-cols-1 row-cols-md-3 g-4">
</div>
<script src="/bootstrap-5.3.0/js/bootstrap.js"></script>
</body>
</html>
此时你还无法启动项目,因为我们配置了数据库信息并且还没有创建对应的数据库,启动是不会成功的
步骤5:创建数据库、建表,构造数据
我这里使用的MySQL数据库版本为5.7,当然用8.0更好,数据库名和yaml中配置的名称一样,名为you_resource,创建一张表,名为nav_tab,我这里直接将该表的SQL脚本放出来,直接使用即可
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for nav_tab
-- ----------------------------
DROP TABLE IF EXISTS `nav_tab`;
CREATE TABLE `nav_tab` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'NavTab分类id',
`item_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '分类名称',
`item_info` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '子分类项信息',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`create_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人名称',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`update_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人名称',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'NavTab分类表\r\nid为大分类的id\r\nitem_name为大分类的名称\r\nitem_info为小分类项的信息,以json形式,包括了分类项的名称、超链接地址' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of nav_tab
-- ----------------------------
INSERT INTO `nav_tab` VALUES (1, '源码分类', '[{\"item_name\": \"java源码\", \"item_href\": \"http://www.youluoyuan.com\"}, {\"item_name\": \"html源码\", \"item_href\": \"http://www.baidu.com\"}, {\"item_name\": \"PHP源码\", \"item_href\": \"http://www.baidu.com\"}]', '2024-12-27 16:56:27', '1', '2024-12-30 15:21:20', '1');
INSERT INTO `nav_tab` VALUES (2, '网创分类', '[{\"item_name\": \"自媒体\", \"item_href\": \"http://www.youluoyuan.com\"}, {\"item_name\": \"短视频\", \"item_href\": \"http://www.baidu.com\"}]', '2024-12-30 13:29:28', NULL, '2024-12-30 13:29:56', NULL);
SET FOREIGN_KEY_CHECKS = 1;
库、表创建导入完成后,你应当看到这样的内容,如图
步骤6:创建视图对象
因为分类标签表中子标签是以json格式存储,直接使用实体返回的信息无法直接应用于模板,因此在domain中创建vo目录,建立模板所需的视图对象,如图,在vo中创建NavTabVo,内容如下,如果你的lombok无法使用,可自行将注解替换生成get和set方法(我这里就是)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NavTabVo {
private Long id;
private String itemName;
private List<HashMap<String,String>> itemInfo;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private Date createTime;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private Date updateTime;
private String createUser;
private String updateUser;
}
步骤7:创建响应数据工具类
在后端处理数据,不一定总是成功的,因此有必要创建一个统一的响应数据格式,创建utils目录,添加名为Res的java文件,内容如下
public class Res<T> {
private int code; // 0 表示失败,1 表示成功
private String msg; // 错误或成功消息
private T data; // 返回的数据
private Res(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> Res<T> success(T data) {
return new Res<T>(1,"成功",data);
}
public static <T> Res<T> error(String msg) {
return new Res<T>(0,msg,null);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Res() {
}
}
步骤8:修改主页的控制器
上篇文章创建的主页控制器,只是用于处理跳转到主页,现在需要增加分类标签数据的响应
来到FrontPageCon,修改我们的FrontIndex接口,这里就要用到jackson和上面创建的Res了,以及对数据库的操作
接口内容如下
FrontPageCon.java
@Controller
@RequestMapping("/")
public class FrontPageCon {
@Resource
NavTabService navTabService;
@GetMapping("/")
public String FrontIndex(Model model) {
List<NavTab> navTabs = navTabService.getNavTabs();
List<NavTabVo> navTabVos = new ArrayList<>();
for (NavTab navTab : navTabs) {
NavTabVo navTabVo = new NavTabVo();
navTabVo.setId(navTab.getId());
navTabVo.setItemName(navTab.getItemName());
navTabVo.setCreateTime(navTab.getCreateTime());
navTabVo.setUpdateTime(navTab.getUpdateTime());
navTabVo.setCreateUser(navTab.getCreateUser());
navTabVo.setUpdateUser(navTab.getUpdateUser());
String itemInfoStr = navTab.getItemInfo();
try {
ObjectMapper objectMapper = new ObjectMapper();
// 将 JSON 字符串转换为 List<Map<String, String>>
TypeReference<List<HashMap<String, String>>> typeReference = new TypeReference<List<HashMap<String, String>>>() {};
List<HashMap<String, String>> itemInfo = objectMapper.readValue(itemInfoStr,typeReference);
navTabVo.setItemInfo(itemInfo);
} catch (Exception e) {
model.addAttribute("res", Res.error(e.getMessage()));
return "front/index";
}
navTabVos.add(navTabVo);
}
model.addAttribute("res", Res.success(navTabVos));
return "front/index";
}
}
NavTabService我就不说了(直接点击生成的东西),这里放出对应实现类内容
NavTabServiceImpl.java
@Service("navTabService")
public class NavTabServiceImpl extends ServiceImpl<NavTabDao,NavTab> implements NavTabService {
@Resource
NavTabDao navTabDao;
@Override
public List<NavTab> getNavTabs() {
return navTabDao.getNavTabs();
}
}
NavTabDao.java,这里我没有使用注解的方式,因此还是有必要贴出
@Mapper
public interface NavTabDao extends BaseMapper<NavTab> {
List<NavTab> getNavTabs();
}
到了dao这里,然后在resources的dao下创建NavTabDao.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.you_resource.dao.NavTabDao">
<!--List<NavTab> getNavTabs();-->
<select id="getNavTabs" resultType="com.you_resource.domain.pojo.NavTab">
select * from nav_tab
</select>
</mapper>
步骤9:查看主页
在前面的步骤,后端的相关内容都做好了,但是标签页还没有做处理,可以先启动项目看看显示是否正常,如果没问题,将会是如图的效果 (中间那个彩色的区域是一张图,你可以替换为其他图片)
步骤10:修改标签页动态获取分类标签
将NavTab.html内容替换为如下
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<div class="container mt-4 bg-white p-4 rounded">
<div th:if="${res.getCode() == 0}">
<strong>错误:</strong> <span th:text="${res.getMsg()}"></span>
</div>
<ul th:each="navTabVo : ${res.getData()}" class="nav nav-pills flex-wrap list-unstyled mb-4">
<!-- 独立的一行,显示 A分类 -->
<li class="nav-item w-100 border-bottom pb-2 mb-3">
<span class="nav-link p-0 m-0 d-flex align-items-center text-dark">
<i class="bi bi-align-bottom me-2" style="font-size: 1.25rem;"></i>
<span th:text="${navTabVo.getItemName()}"></span>
</span>
</li>
<!-- 其他分类项 -->
<li class="nav-item border-end pe-3 me-3" th:each="itemInfo : ${navTabVo.getItemInfo()}" >
<a class="text-dark text-decoration-none" th:href="${itemInfo['item_href']}" th:text="${itemInfo['item_name']}"></a>
</li>
</ul>
</div>
</body>
</html>
可以看到,内容省略了不少,且这里利用thymeleaf的语法动态的获取的接口中响应的数据,再次启动项目,结果如下
补充说明
补充1.代码中的错误处理解释
我们提到了响应的数据问题,如果后台处理数据错误时总不可能直接给用户展示个例如500的错误吧,比如如下图
接口中在可能出现错误的地方,也就是json解析那块,做了异常捕获,并响应对应的错误信息,我们可以在try中故意制造个除以0的错误看看效果,如图,还算不错
补充2.解读thymeleaf的模板渲染
首先是响应的数据中,可以看到是一个Res包含了code、msg、data的,且data是一个List<NavTabVo>对象,且每个NavTabVo中有个比较特殊的属性itemInfo,其类型是List<HashMap<String,String>>,这也是为了应付json数据转化为java对象,因为模板只可解析java对象
在NavTab.html中,以下代码做了对后端出异常的处理显示
<div th:if="${res.getCode() == 0}">
<strong>错误:</strong> <span th:text="${res.getMsg()}"></span>
</div>
ul中,${res.getData()}对应着Res对象的getData方法,这里使用了javabean模式的获取数据的方法,th:each表示遍历获取每个对象并命名为navTabVo,后面的th:text="${navTabVo.getItemName()}"是一样的,使用java对象的get方法。
再往下,可以看到a标签中的获取数据方式与上面不同,是这样的 th:href="${itemInfo['item_href']}" th:text="${itemInfo['item_name']}" ,因为这里的每个itemInfo在接口中可以看到是一个个Map,因此需用此方式来获取对应的值
<ul th:each="navTabVo : ${res.getData()}" class="nav nav-pills flex-wrap list-unstyled mb-4">
<!-- 独立的一行,显示 A分类 -->
<li class="nav-item w-100 border-bottom pb-2 mb-3">
<span class="nav-link p-0 m-0 d-flex align-items-center text-dark">
<i class="bi bi-align-bottom me-2" style="font-size: 1.25rem;"></i>
<span th:text="${navTabVo.getItemName()}"></span>
</span>
</li>
<!-- 其他分类项 -->
<li class="nav-item border-end pe-3 me-3" th:each="itemInfo : ${navTabVo.getItemInfo()}" >
<a class="text-dark text-decoration-none" th:href="${itemInfo['item_href']}" th:text="${itemInfo['item_name']}"></a>
</li>
</ul>
结语
以上是幽络源的SpringBoot+Thymeleaf+Bootstrap5整合MyBatisPlus、MySQL实现分类标签的动态渲染的java教程,如感兴趣、有不懂之处可加群交流。