02. JakartaEE11+Thymeleaf实现图书管理模块

一、数据建模

1、物理建模

在mysql数据库中创建此表,常规情况下,如果多个单词组成,则使用_分隔;

复制代码
-- 图书表(增加库存字段)
CREATE TABLE books (
                       id INT PRIMARY KEY AUTO_INCREMENT,
                       title VARCHAR(100) NOT NULL,
                       author VARCHAR(50) NOT NULL,
                       isbn VARCHAR(20) UNIQUE,
                       stock INT DEFAULT 1,
                       publish_date DATE,
                       category VARCHAR(50)
);
​

2、逻辑建模

在java中创建实体类

复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Books {
    private Integer id; // 主键id
    private String title; // 书名
    private String author; // 作者
    private String isbn;  // ISBN
    private Integer stock; // 库存
    private Date publishDate;  //对应与数据表publish_date  // 出版日期
    private String category; // 类别
​
}

二、整体架构

三、搭建环境

1、搭建持久层环境

(1)在 pom.xml文件 添加相应的依赖包

复制代码
  <dependencies>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- Thymeleaf依赖 -->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>3.1.2.RELEASE</version>
        </dependency>
​
        <!-- lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.42</version>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.16</version>
        </dependency>
​
            <!-- Apache Commons DbUtils -->
            <dependency>
                <groupId>commons-dbutils</groupId>
                <artifactId>commons-dbutils</artifactId>
                <version>1.7</version> 
            </dependency>
​
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
​
    </dependencies>
​

(2)资源配置文件:druid.properties

复制代码
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/library
username=root
password=123456
​
initialSize=5
maxActive=10
minIdle=5

(3)JDBCUtil工具类

复制代码
package com.hnjt.util;
​
import com.alibaba.druid.pool.DruidDataSourceFactory;
​
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
​
public class JDBCUtil {
    //连接池
    private static DataSource ds;
    //本地线程对象
    //使用ThreadLocal就是为了一个线程在多次数据库操作过程中,使用的是同一个连接!
    private static ThreadLocal<Connection> th = new ThreadLocal<>();
​
    //静态代码块,初始化连接池
    static {
        //1.获取prop对象
        Properties prop = new Properties();
        //2.加载配置文件
        InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            prop.load(in);
            //3.获取数据源对象
            ds = DruidDataSourceFactory.createDataSource(prop);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    /**
     * 获取连接的方法
     *
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
        //从本地线程中获取连接对象
        Connection conn = th.get();
        if (conn == null) {
            //如果为空,则创建新的连接对象
            conn = ds.getConnection();
            //将连接对象放入本地线程中
            th.set(conn);
        }
        return conn;
    }
​
​
    /**
     * 关闭连接的方法
     */
    public static void close() {
        try {
            //获取连接
            Connection conn = getConnection();
            if (conn != null) {
                //移除本地线程中的连接对象
                th.remove();
                //处理事务:关闭事务,设置为自动提交
                conn.setAutoCommit(true);
                //关闭连接池
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(4)BaseDao

复制代码
package com.hnjt.util;
​
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
​
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
​
public class BaseDao<T> {
​
    private QueryRunner queryRunner = new QueryRunner();
​
    // 增删改
    public int update(String sql, Object... param) throws Exception {
        Connection connection = JDBCUtil.getConnection();
        try {
            return queryRunner.update(connection, sql, param);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtil.close();
        }
    }
​
    // 查询单个对象
    public T getBean(Class<T> clazz, String sql, Object... param) throws Exception {
        Connection connection = JDBCUtil.getConnection();
        try {
            return queryRunner.query(connection, sql, new BeanHandler<>(clazz), param);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtil.close();
        }
    }
​
    // 查询列表
    public List<T> getBeanList(Class<T> clazz, String sql, Object... param) throws Exception {
        Connection connection = JDBCUtil.getConnection();
        try {
            return queryRunner.query(connection, sql, new BeanListHandler<>(clazz), param);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtil.close();
        }
    }
}

2、搭建Thymeleaf环境

(1)添加web模块

src/main/webapp/WEB-INF/templates/index.html

复制代码
<!DOCTYPE html>
<!--引入thymeleaf包-->
<html lang="en" xmlns:th="http://www.thymeleaf/org">
<head>
    <meta charset="UTF-8">
    <base th:href="@{/}">
    <title>Title</title>
</head>
<body>
<h3>首页图书模块</h3>
<a th:href="@{/booksServlet(method='list')}">图书列表</a><p/>
</body>
</html>

src/main/webapp/WEB-INF/templates/list.html

复制代码
<!DOCTYPE html>
<!--引入thymeleaf包-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>图书列表</title>
    <base th:href="@{/}">
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
    <!--     jquery库文件-->
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
​
    <style>
        .table th{
            text-align: center;
        }
​
    </style>
</head>
<body>
<h2 class="text-center">图书信息管理页</h2>
<h2 class="text-center" th:if="${#lists.isEmpty(books)}">
    暂无图书信息!
</h2>
<div class="container">
    <p class="pp" ><a href="booksServlet?method=toAdd">添加图书</a></p>
    <table th:if="${not #lists.isEmpty(books)}" class="table table-bordered table-striped table-hover">
        <thead>
        <tr>
            <th>编号</th>
            <th>名称</th>
            <th>作者</th>
            <th>ISBN</th>
            <th>库存</th>
            <th>出版日期</th>
            <th>种类</th>
            <th colspan="2">操作</th>
        </tr>
        </thead>
        <tbody>
        <tr th:each="book:${books}">
            <td th:text="${book.id}"></td>
            <td th:text="${book.title}"></td>
            <td th:text="${book.author}"></td>
            <td th:text="${book.isbn}"></td>
            <td th:text="${book.stock}"></td>
            <td th:text="${book.publishDate}"></td>
            <td th:text="${book.category}"></td>
            <td><a class="btn btn-info btn-small" th:href="@{/booksServlet(method='toUpdate',id=${book.id})}">修改</a>
            </td>
            <td><a class="delBtn btn btn-danger btn-small"
                   th:href="@{/booksServlet(method='remove',id=${book.id})}">删除</a></td>
        </tr>
        </tbody>
    </table>
</div>
<script>
    // 删除按钮的点击事件
    $(".delBtn").click(function (e) {
        var flag= confirm("确定要删除吗?");
        // 如果用户点击取消,阻止表单提交
        if(!flag){
            e.preventDefault();
        }
    });
</script>
</body>
</body>
</html>

(2)拷贝CustomTemplateEngine类

CustomTemplateEngine和ModelServlet不需要配置映射路径,为了让模块Servlet继承的 CustomTemplateEngine:用来进行Thymeleaf视图解析的 ModelServlet:用来获取method请求参数,根据method的值,经过反射机制调用对应的请求方法。 最好把这两个servlet单独放在同一个包里

com.hnjt.thymeleaf.CustomTemplateEngine.java

复制代码
package com.hnjt.thymeleaf;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.WebApplicationTemplateResolver;
import org.thymeleaf.web.servlet.IServletWebExchange;
import org.thymeleaf.web.servlet.JakartaServletWebApplication;
import java.io.IOException;
/**
 * 对于一个JavaWeb应用而言,我们只需要配置一套模板引擎即可,所有的请求都通过该模板引擎来解析网页。
 * 单例模式。本类中还提供了一个对请求进行解析的方法,方便我们使用。
 */
public class CustomTemplateEngine {
    // 定义一个静态变量,用于保存模板引擎对象
    private static CustomTemplateEngine webApplication;
    // 模板引擎对象
    private TemplateEngine templateEngine;
    // 创建JakartaServletWebApplication对象
    private JakartaServletWebApplication application;
​
    /**
     * 私有化构造方法,防止外部直接创建对象
     * @param request
     */
    private CustomTemplateEngine(HttpServletRequest request) {
        System.out.println("设置Thymeleaf模板引擎");
        // 创建Thymeleaf的JakartaServletWebApplication对象
        application = JakartaServletWebApplication.buildApplication(request.getServletContext());
        // 创建模板解析器对象
        final WebApplicationTemplateResolver templateResolver = new WebApplicationTemplateResolver(application);
        // 设置Thymeleaf的模板模式为HTML,除此之外Thymeleaf还支持处理其他5种模板,它们分别是XML、TEXT、JAVASCRIPT、CSS、RAW
        templateResolver.setTemplateMode(TemplateMode.HTML);
        // 设置模板文件的前缀(即路径)
        templateResolver.setPrefix("/WEB-INF/templates/");
        // 设置模板文件的文件后缀
        templateResolver.setSuffix(".html");
        // 设置缓存时间
        templateResolver.setCacheTTLMs(Long.valueOf(3600000L));
        // 设置缓存是否可用,开发阶段我们需要将缓存关闭,即设置为false
        templateResolver.setCacheable(false);
        // 创建模板引擎对象
        templateEngine = new TemplateEngine();
        // 为模板引擎设置模板解析器
        templateEngine.setTemplateResolver(templateResolver);
    }
​
    /**
     * 获取WebApplication对象
     * @param request 请求对象
     * @return 返回CustomTemplateEngine对象
     */
    public static CustomTemplateEngine getInstance(HttpServletRequest request) {
        if (webApplication == null) {
            webApplication = new CustomTemplateEngine(request);
        }
        return webApplication;
    }
​
​
    /**
     * 处理模板文件
     * @param templateName 模板文件的名称
     * @param request 请求对象
     * @param response 响应对象
     * @throws IOException IO异常
     */
    public void processTemplate(String templateName, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 创建IServletWebExchange对象
        IServletWebExchange webExchange = application.buildExchange(request, response);
        // 创建WebContext对象
        WebContext context = new WebContext(webExchange, webExchange.getLocale());
        // 设置响应体内容类型和字符集
        response.setContentType("text/html;charset=UTF-8");
        // 处理模板数据
        templateEngine.process(templateName, context, response.getWriter());
    }
​
}

com.hnjt.thymeleaf.ModelServlet.java

复制代码
package com.hnjt.thymeleaf;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
​
import java.lang.reflect.Method;
​
public class ModelServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws jakarta.servlet.ServletException, java.io.IOException {
        doPost(req, resp);
    }
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws jakarta.servlet.ServletException, java.io.IOException {
        //获取请求的参数
        String method = req.getParameter("method");
        try {
            //获取Method对象
            Method methodObject = this.getClass().getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class);
​
            //暴力破解
            methodObject.setAccessible(true);
​
            //执行方法
            methodObject.invoke(this, req, resp);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

3、显示首页功能

IndexServlet类

复制代码
package com.hnjt.servlet;
​
import com.hnjt.thymeleaf.CustomTemplateEngine;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
​
import java.io.IOException;
/**
 * 访问首页
 */
@WebServlet("/index.html")   // 指定访问路径
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, IOException {
        // 获取模板引擎对象
        CustomTemplateEngine templateEngine = CustomTemplateEngine.getInstance(req);
​
        // 处理请求并响应结果
        templateEngine.processTemplate("index", req, resp);
    }
​
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
​

BooksServet类

复制代码
package com.hnjt.servlet;
​
import com.hnjt.dao.BooksDao;
import com.hnjt.dao.impl.BooksDaoImpl;
import com.hnjt.entity.Books;
import com.hnjt.thymeleaf.CustomTemplateEngine;
import com.hnjt.thymeleaf.ModelServlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
​
import java.io.IOException;
import java.util.List;
​
@Slf4j
@WebServlet("/booksServlet")
public class BooksServlet extends ModelServlet {
​
    private BooksDao booksService = new BooksDaoImpl();
​
    /**
     * 1、查询书籍信息B
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        CustomTemplateEngine engine = CustomTemplateEngine.getInstance(req);
        try {
            System.out.println("执行list方法...");
            // 查询书籍信息
            List<Books> books = booksService.queryBooks();
​
​
            // 将书籍信息保存到request域中
            req.setAttribute("books", books);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
​
        // 转发到list.html页面
        engine.processTemplate("list", req, resp);
​
    }
​
}
​
  • web.xml 配置首页
复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">
​
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

四、整体架构

相关推荐
胡图图不糊涂^_^2 小时前
网络原理笔记
java·网络·笔记·学习·tcp/ip·http·https
嘻嘻哈哈樱桃2 小时前
牛客经典101题题解集--哈希
java·数据结构·python·算法·leetcode·职场和发展·哈希算法
SamDeepThinking2 小时前
秒杀系统里的RocketMQ,不是发个消息那么简单
java·后端·架构
卷毛的技术笔记2 小时前
告别“盲猜式”排障:分布式链路追踪方案选型与Spring Boot 3实战
java·spring boot·分布式·后端·spring·面试·系统架构
XiYang-DING2 小时前
【Java EE】线程池
java·开发语言·java-ee
想唱rap2 小时前
TCP套接字编程
java·linux·网络·c++·tcp/ip·mysql·ubuntu
day day day ...2 小时前
Maven 项目中导入依赖的各种场景、方法、常见问题及解决办法
java·php·maven
xin_nai2 小时前
LeetCode热题100(Java)(4)子串
java·算法·leetcode