Web实战-Tomcat-Servlet-Thymeleaf -JDBC-MySQL浏览器页面显示数据库数据(水果库存系统)
Posted .29.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Web实战-Tomcat-Servlet-Thymeleaf -JDBC-MySQL浏览器页面显示数据库数据(水果库存系统)相关的知识,希望对你有一定的参考价值。
🥇作者 .29. 的✔博客主页✔
🥇记录JavaWeb学习的专栏:Web专栏
🥇拼搏起来吧,未来会给你开出一个无法拒绝的条件…您的
点赞
,收藏
以及关注
是对作者最大的鼓励喔 ~~
客户端展示库存数据
前言
在JDBC专栏中,我们完成了水果库存系统功能的设计,实现以及优化,但总得来看,整个项目都是后端的代码,系统的页面也不过是通过控制台模拟而来的,而非真正的前端页面。
而在这篇文章开始,我们会对水果库存系统的客户端页面功能进行设计与实现,让库存系统可以在网页中使用。
项目中我们主要依赖Tomcat
部署,使用Servlet
组件,过程中通过JDBC
连接mysql数据库
获取数据,将数据在浏览器页面中展现出来。
一、Thymeleaf - 视图模板技术
在开始,我们需要先了解一下Thymeleaf - 视图模板技术
的使用,这是我们在使用Servlet
时需要用到的技术。
接下来,我们通过 Thymeleaf 的使用来了解视图模板技术到底是什么。
1.导入 jar 包
需要使用Thymeleaf - 视图模板技术,第一步就是导入相关的 jar 包了,具体的步骤大家应该都了解,不了解的可以按照这篇文章的思路来尝试导入jar包:druid数据库连接池的使用。
Thymeleaf - 视图模板技术 jar包资源:
链接:https://pan.baidu.com/s/1NOucl2A8nEAIzg-rT4GqGg
提取码:leaf
2.创建Servlet类,设置相关属性与方法。
我们需要创建一个Servlet类,我将这个类命名为ViewBaseServlet
,说其是Servlet类是因为它需要继承HttpServlet类
。
ViewBaseServlet类
中设置的是与使用Thymeleaf 技术相关的属性以及方法,我们可以通过代码来了解其功能:
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ViewBaseServlet extends HttpServlet
private TemplateEngine templateEngine;
@Override
public void init() throws ServletException
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①html是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
3.添加web.xml文件配置
web.xml
文件是一个Web项目的配置文件.
上文提到的ViewBaseServlet类
代码中,有两个重要的部分:
- 添加前缀 view-prefix
- 添加后缀 view-suffix
除了ViewBaseServlet类
,我们还需要在web.xml文件中添加前缀与后缀相关的参数配置,参数配置代码如下:
<context-param>
<param-name>view-prefix</param-name>
<param-value>/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>
参数配置中,我们将前缀设置为/
;同时后缀设置为.html
。
这样的用意在于我们可以使用Thymeleaf - 视图模板技术,通过逻辑视图名称获取到对应的物理视图名称,例如:
//假设此时的视图名称是index
//那么Thymeleaf会将这个 逻辑视图名称 对应到 物理视图名称上去
//逻辑视图名称 : index
//物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是 / + index + .html
我们先了解一下使用的效果即可,至于为什么这样,我们会在后续代码的实现中体现出来。
4.使用Thymeleaf技术相关的HTML标签属性
在HTML中,需要使用Thymeleaf技术,我们需要在html标签
中添加相关属性:
<html xmlns:th="http://www.thymeleaf.org">
</html>
而后,当我们使用Thymeleaf技术的属性时,都需要在属性前添加th:
的标志。
二、浏览器页面实现
1.获取数据库数据
我们需要在浏览器页面中显示数据库中存放的数据,那么我们就首先要连接数据库来获取数据,这时候正好就需要连接数据库执行更新或查询操作的功能。
相关功能的通用方法,已经在我们的JDBC专栏:JDBC实战系列文章中完成,直接拿过来使用即可:
BaseDAO类代码
:
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author .29.
* @create 2022-09-25 19:30
*/
public abstract class BaseDAO<T>
public final String DRIVER = "com.mysql.cj.jdbc.Driver";
public final String URL = "jdbc:mysql://localhost:3306/fruitdb?useSSL=false&useUnicode=true&CharacterEncoding=utf-8";
public final String USER = "root" ;
public final String PSW = "" ;
protected Connection connection;
protected PreparedStatement pstm;
protected ResultSet rs;
//获取T的对象
private Class entityClass;
//构造方法
public BaseDAO()
//getClass() 获取Class对象,我们当前创建的是FruitDAOImpl对象,new FruitDAOImpl();
//那么子类的构造方法内部首先调用父类(BaseDAO)的空参构造器,
//因此此处的getCalss()会被执行,但是获取的是子类FruitDAOImpl的Class
//所以getGenericSuperclass()获取的是BaeDAO的class
Type genericType = getClass().getGenericSuperclass();//获取泛型父类类型
//强转成 ParameterizedType 参数化类型
//getActualTypeArguments 获取实际的类型参数
Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
//只传入了一个参数,数组首位就是我们需要获取的<T>的真实类型
Type actualType = actualTypeArguments[0];
//actualType.getTypeName();获取类型名
try
entityClass = Class.forName(actualType.getTypeName());
catch (ClassNotFoundException e)
e.printStackTrace();
//将加载驱动,连接数据库的操作包装成方法,减少代码复用率
protected Connection conn()
try
//加载驱动
Class.forName(DRIVER);
//数据库管理器,连接数据库
connection = DriverManager.getConnection(URL, USER, PSW);
return connection;
catch (ClassNotFoundException e)
e.printStackTrace();
catch (SQLException e)
e.printStackTrace();
return null;
//将关闭资源的操作包装成方法
protected void close(ResultSet rs,PreparedStatement pstm,Connection connection)
try
if(rs != null)
rs.close();
if(pstm != null)
pstm.close();
if(connection != null)
connection.close();
catch (SQLException e)
e.printStackTrace();
//执行更新,返回影响行数的方法(如果是插入操作,返回自增列主键值)
protected int executeUpdate(String sql,Object... params)//... params不确定数量的参数
boolean insertFlag = false;
insertFlag = sql.trim().toUpperCase().startsWith("INSERT");
try
connection = conn();
//(sql语句不通用,靠参数传递进来)
//sql语句
//String sql = "update t_fruit set fcount = ? where fname like ?";
//预处理对象
if(insertFlag)
pstm = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
else
pstm = connection.prepareStatement(sql);
setParams(pstm,params);
int count = pstm.executeUpdate();
rs = pstm.getGeneratedKeys();
if(rs.next())
return ((Long)rs.getLong(1)).intValue();
//参数填充也不通用,也靠参数传递进来
if(params != null && params.length > 0)
for(int i = 0;i < params.length;++i)
pstm.setObject(i+1,params[i]);
return count;//执行更新,返回影响行数
catch (SQLException e)
e.printStackTrace();
finally
close( rs, pstm, connection);
return 0;//影响行数为0
//通过反射技术,给obj对象的property属性赋propertyValue值
protected void setValue(Object obj, String property,Object propertyValue)
Class clazz = obj.getClass();
try
//获取property这个字符串对应的属性名,比如fid去找obj对象中对应的fid属性值
Field field = clazz.getDeclaredField(property);
if(field != null)
field.setAccessible(true);//强制访问(即使private属性也能访问),防止属性为private
field.set(obj,propertyValue);
catch (NoSuchFieldException e)
e.printStackTrace();
catch (IllegalAccessException e)
e.printStackTrace();
//执行复杂查询,返回... 例如统计结果
protected Object[] executeComplexQuery(String sql,Object... params)
try
//加载驱动,连接数据库的方法
connection = conn();
//预处理对象
pstm = connection.prepareStatement(sql);
//执行查询,返回结果集
rs = pstm.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集信息的数据(有哪些列,什么类型。。。)
ResultSetMetaData rsmd = rs.getMetaData();
//获取元数据列数
int columnCount = rsmd.getColumnCount();
Object[] columnValueArr = new Object[columnCount];
if(rs.next())
for(int i = 0;i < columnCount;++i)
Object columValue = rs.getObject(i + 1);
columnValueArr[i] = columValue;
return columnValueArr;
catch (SQLException e)
e.printStackTrace();
finally
close( rs, pstm, connection);
return null;
//执行查询,返回单个实体对象
protected T load(String sql,Object... params)
try
//加载驱动,连接数据库的方法
connection = conn();
//预处理对象
pstm = connection.prepareStatement(sql);
setParams(pstm,params);
//执行查询,返回结果集
rs = pstm.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集信息的数据(有哪些列,什么类型。。。)
ResultSetMetaData rsmd = rs.getMetaData();
//获取元数据列数
int columnCount = rsmd.getColumnCount();
if(rs.next())
T entity = (T) entityClass.newInstance();
for(int i = 0;i < columnCount;++i)
String columnName = rsmd.getColumnName(i + 1);
Object columValue = rs.getObject(i + 1);
setValue(entity,columnName,columValue);
return entity;
catch (SQLException e)
e.printStackTrace();
catch (InstantiationException e)
e.printStackTrace();
catch (IllegalAccessException e)
e.printStackTrace();
finally
close( rs, pstm, connection);
return null;
//给预处理参数设置参数
protected void setParams(PreparedStatement psmt,Object... params) throws SQLException
if(params != null && params.length > 0 )
for(int i = 0;i < params.length;++i)
psmt.setObject(i+1,params[i]);
// 执行查询,返回结果集并输出
protected List<T> executeQuery(String sql,Object... params)
List<T> list = new ArrayList<>();
try
//加载驱动,连接数据库的方法
connection = conn();
//预处理对象
pstm = connection.prepareStatement(sql);
setParams(pstm,params);
//执行查询,返回结果集
rs = pstm.executeQuery();
//通过rs可以获取结果集的元数据
//元数据:描述结果集信息的数据(有哪些列,什么类型。。。)
ResultSetMetaData rsmd = rs.getMetaData();
//获取元数据列数
int columnCount = rsmd.getColumnCount();
while(rs.next())
T entity = (T) entityClass.newInstance();
for(int i = 0;i < columnCount;++i)
String columnName <以上是关于Web实战-Tomcat-Servlet-Thymeleaf -JDBC-MySQL浏览器页面显示数据库数据(水果库存系统)的主要内容,如果未能解决你的问题,请参考以下文章
《Flask Web开发:基于Python的Web应用开发实战》pdf 免费下载
Flask Web开发:基于Python的Web应用开发实战PDF