系列文章目录
- (尚硅谷)JavaWeb新版教程03-Tomcat-Servlet,介绍了 Tomcat 和 Servlet 的基本内容,实现了 Tomcat 的部署运行和访问。
- 实现浏览器 - Servlet - 数据库交互操作,这里实现了在浏览器上添加数据,能够直接添加到数据库中的操作。
- (尚硅谷)JavaWeb新版教程04-Thymeleaf-保存作用域 ,介绍了 Thymeleaf 技术,实现了将数据库真实的数据显示到浏览器界面上。
文章目录
需求
我们要实现的功能:
- 将每一个水果变成一个超链接,并且点击这个链接可以跳转到对应的编辑页面对这一栏进行编辑,edit 页面有这个水果的所有信息 – edit
- 对水果的信息 edit 之后,点击修改按钮之后,数据库中的数据修改了,返回到 index 界面上的数据也更改了 – update
- 点击删除小图标,删除对应的那一行水果,页面上跳出来是否确认删除的动态确认框,确认之后,删除对应行数据 – del
- 表格右上角显示可以添加新库存记录的超链接,点击之后跳转到一个新的添加界面(一个表单),点击添加按钮添加数据之后重新回到 index 界面 – add
- index 页面上分页显示,每页显示5个水果,并且在表格下方设置首页、上一页、下一页、尾页的按钮,点击对应按钮可以跳转到对应页面 – index
- 在表格上方添加一个按照任意关键字检索的一个功能,将水果名称和备注中带有这个关键字的水果查出来并分页显示 – index
备注:
- 下面主要讲解后台实现,对于具体的 CSS 美观设置不做介绍;
- 同时对文中 Thymeleaf 基本语法可以查看参考文献:第八章 Thymeleaf;
- 配置 servlet 映射均用注解的方式。
1、水果编辑功能
1.1 edit 功能实现
- 首先呢,想要点击水果的名字能进行界面跳转,那么我们需要先将这一行字变成一个超链接,即在这一行通过设置 <a> 标签的 href 的属性设置对应的链接跳转地址为 edit.do。
<td><a th:text="${fruit.fname}" th:href="@{/edit.do(fid=${fruit.fid})}">苹果</a></td>
- 那么我们就需要一个 edit.html 静态界面和一个 EditServlet 组件来处理 edit.do 请求。
- edit.html 界面上要显示 名称、单价、库存、备注,同时还有修改按钮,所以是展示的页面需要用一个表格来显示。
edit.html 的表格实现如下:
<p class="center f30">编辑库存信息</p>
<table id="tbl_fruit" th:object="${fruit}">
<!-- 隐藏域 : 功能类似于文本框 , 它的值会随着表单的发送也会发送给服务器,但是界面上用户看不到 -->
<input type="hidden" name="fid" th:value="*{fid}"/>
<tr>
<th class="w20">名称:</th>
<td><input type="text" name="fname" th:value="*{fname}"/></td>
</tr>
<tr>
<th class="w20">单价:</th>
<td><input type="text" name="price" th:value="*{price}"/></td>
</tr>
<tr>
<th class="w20">库存:</th>
<td><input type="text" name="fcount" th:value="*{fcount}"/></td>
</tr>
<tr>
<th class="w20">备注:</th>
<td><input type="text" name="remark" th:value="*{remark}"/></td>
</tr>
<tr>
<th colspan="2">
<input type="submit" value="修改" />
</th>
</tr>
</table>
解释:
- 这里在整个表格的最开始的 table 标签写上 th:object=“${fruit}” ,那么在下面每一行使用 th:value=“*{fcount}” 时,表示这一行的数据显示的是从数据库上请求的 fruit.fcount 的数据;
- 我们点击某一个水果怎么知道是哪个水果呢,通过每个水果的唯一主键 fid 来确认,但是这个 fid 用户是不需要的,所以设置一个隐藏属性,即将文本框 <input> 标签的 type 属性设置为 hidden 的就可以了;
- 隐藏域 : 功能类似于文本框 , 它的值会随着表单的发送也会发送给服务器,但是界面上用户看不到。
EditServlet 组件实现:
@WebServlet("/edit.do")
public class EditServlet extends ViewBaseServlet {
//新建一个fruitDAO的实现类对象,用来调用DAO方法,多态的体现
private FruitDAO fruitDAO = new FruitDAOImpl();
@Override
public void doGet(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
//获取fid属性
String fidStr = request.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
//从后台获取数据
Fruit fruit = fruitDAO.getFruitByFid(fid);
//放到请求的保存作用域中
request.setAttribute("fruit",fruit);
//将这个页面渲染之后显示出来
super.processTemplate("edit",request,response);
}
}
}
解释:
- 没有设置请求方式,默认是 get 方式,所以重写 doGet 方法;
- 从后台获取数据需要用到 fruitDAO 中的方法,新建一个 fruitDAO 的实现类对象,用来根据 fid 获取特定的水果库存信息,多态的体现。
- 这里需要通过 fid 获取水果信息,所以需要在 FruitDAO 接口中定义 getFruitByFid 方法,并且在 FruitDAOImpl 中调用 BaseDAO 的方法来实现这个方法。
FruitDAO 接口实现:
//根据fid获取特定的水果库存信息
Fruit getFruitByFid(Integer fid);
FruitDAOImpl 实现类实现这个方法:
@Override
public Fruit getFruitByFid(Integer fid) {
return super.load("select * from t_fruit where fid = ? ", fid);
}
解释:
- 这里的 super 调用的是 BaseDAO 父类中的方法。
1.2 update 功能实现
- 上面一步 edit 界面编辑好之后,还有一个修改按钮,既然点击修改按钮要能把数据提交给数据库,我们就需要将 edit 设置成一个 form 表单,将 form 标签设置在 table 标签外面,这样表格中修改的文本框也可以一起提交。
- 那么我们就需要点击 “修改” 按钮之后触发 update.do 行为,我们需要创建一个 UpdateServlet 组件来处理这个请求。
edit .html 界面添加:
<form th:action="@{/update.do}" method="post" th:object="${fruit}">
解释:
- 其中的 th:action=“@{/update.do}” 表示跳转到 update.do 界面
- 请求方式要写成 post 方法
UpdateServlet 组件实现:
@WebServlet("/update.do")
public class UpdateServlet extends ViewBaseServlet {
private FruitDAO fruitDAO = new FruitDAOImpl();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.设置编码
request.setCharacterEncoding("utf-8");
//2.获取表单参数
String fidStr = request.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
int price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
//3.执行更新
fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));
//4.资源跳转
//super.processTemplate("index",request,response);
//request.getRequestDispatcher("index.html").forward(request,response);
//此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
response.sendRedirect("index");
}
}
解释:
- 上面表单的请求的方法是 post 方法,所以我们要重写 doPost 方法 ;
- 因为要向数据库添加数据,可能有中文数据,首先要设置编码集,否则添加的中文字符就是乱码;
- 此处资源跳转需要用重定向,目的是重新给 IndexServlet 发请求,重新获取 furitList,然后覆盖到 session 中,这样 index.html 页面上显示的 session 中的数据才是最新的;
- 重新请求一次,数据库才会把新数据发给客户端,才会获取新数据放到保存作用域中,否则直接内部转发这个界面存放的仍然是未修改前的数据。
- 需要更新数据库中的某 fid 水果的四项基本信息,我们需要在 FruitDAO 接口中定义 updateFruit 方法,并且在 FruitDAOImpl 中调用 BaseDAO 的方法来实现这个方法。
FruitDAO 接口实现:
//修改指定的库存记录
void updateFruit(Fruit fruit);
FruitDAOImpl 实现类实现:
@Override
public void updateFruit(Fruit fruit) {
String sql = "update t_fruit set fname = ? , price = ? , fcount = ? , remark = ? where fid = ? ";
super.executeUpdate(sql, fruit.getFname(), fruit.getPrice(), fruit.getFcount(), fruit.getRemark(), fruit.getFid());
}
2、删除功能
- 我们点击小图标,想要把这一行数据删掉,先要跳出一个是否确认删除的确认框(js 的内容),我们新建一个 index.js 文件中放置这个功能函数
index.js 功能实现:
function delFruit(fid){
if(confirm('是否确认删除?')){
window.location.href='del.do?fid='+fid;
}
}
解释:
- 这个 del.do?fid= 链接地址就是在我们的绝对地址基础上后面加上这一串,比如我们点击第一个水果,那么我们浏览器上显示的地址就会变成
http://localhost:8080/del.do?fid=1
,从而跳到对应的页面。
- 然后我们在 index.html 的删除图标那一列调用这个函数
index.html 界面添加:
<td><img src="imgs/del.jpg" class="delImg" th:onclick="|delFruit(${fruit.fid})|"/></td>
解释:
- 里面的 th:οnclick=“|delFruit(${fruit.fid})|” ,双竖线括起来的代表一个函数,而不是一个字符串,$ 符代表一个请求的参数,这里当作 delFruit 这个函数的一个形参传进去。
- 我们需要删除数据库中的某 fid 水果,我们需要在 FruitDAO 接口中定义 delFruit 方法,并且在 FruitDAOImpl 中调用 BaseDAO 的方法来实现这个方法。
FruitDAO 接口实现:
//根据fid删除指定的库存记录
void delFruit(Integer fid);
FruitDAOImpl 实现类实现:
@Override
public void delFruit(Integer fid) {
super.executeUpdate("delete from t_fruit where fid = ? ", fid);
}
3、添加新库存记录
- 我们首先想要在表格右上角显示可以添加新库存记录的超链接,那么我们就需要在 index.html 中表格上方添加一个 div 块,独占一行,然后在 div 块中添加一个超链接标签 <a>。
<a th:href="@{/add.html}" style="border:0px solid blue;margin-bottom:4px;">添加新库存记录</a>
- 点击超链接之后跳转到一个新的添加界面(这里需要设置成一个表单),那我们要创建一个 add.html 文件
add.html 文件实现:
<div id="div_container">
<div id="div_fruit_list">
<p class="center f30">新增库存信息</p>
<form action="add.do" method="post">
<table id="tbl_fruit">
<tr>
<th class="w20">名称:</th>
<!-- <td><input type="text" name="fname" th:value="${fruit.fname}"/></td> -->
<td><input type="text" name="fname" /></td>
</tr>
<tr>
<th class="w20">单价:</th>
<td><input type="text" name="price" /></td>
</tr>
<tr>
<th class="w20">库存:</th>
<td><input type="text" name="fcount" /></td>
</tr>
<tr>
<th class="w20">备注:</th>
<td><input type="text" name="remark" /></td>
</tr>
<tr>
<th colspan="2">
<input type="submit" value="添加" />
</th>
</tr>
</table>
</form>
</div>
</div>
解释:
- form 表单,在点击 “添加” 按钮之后,我们要发送一个 add.do 请求给 AddServlet 组件;
- 并且由于表单携带数据,所以方法设置为 post 方法。
注意:
- 这里会有一个 bug 就是,在 add.html 页面写完东西之后点击添加没办法添加,还在这个静态页面,那是因为将 action=“add.do” 这个行为设置成 th:action=“@{/add.do}” 导致的。
- 由于index.html中的添加超链接的 action 是 th:href=“@{/add.html}”,加上 .html 后缀是不经过 Thymeleaf 渲染的,所以在 add.html 中的所有有关的 Thymeleaf 表达式全部失效,也就是 th:xxxxxx 表达式全部失效,那么后面设置的 action 是失效的。
- 解决方法就是将 所有有关 Thymeleaf 表达式全部删掉,替换成普通表单。
AddServlet 组件实现:
@WebServlet("/add.do")
public class AddServlet extends ViewBaseServlet {
private FruitDAO fruitDAO = new FruitDAOImpl();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String fname = request.getParameter("fname");
Integer price = Integer.parseInt(request.getParameter("price")) ;
Integer fcount = Integer.parseInt(request.getParameter("fcount"));
String remark = request.getParameter("remark");
Fruit fruit = new Fruit(0,fname , price , fcount , remark ) ;
fruitDAO.addFruit(fruit);//添加到后台
response.sendRedirect("index");//重新发送请求,从后台获取数据,跳转到index页面
}
}
解释:
- 上面 add.html 请求的方法是 post 方法,所以我们要重写 doPost 方法;
- 要写数据到数据库,所以要设置编码集;
- 因为有新数据更新,所以要用重定向方式,再给服务端重新发送请求,获取新数据。
- 需要将数据添加到数据库,所以我们需要在 FruitDAO 接口中定义该方法,并且在 FruitDAOImpl 实现这方法。
FruitDAO 接口实现:
//添加新库存记录
void addFruit(Fruit fruit);
FruitDAOImpl 实现类实现:
@Override
public void addFruit(Fruit fruit) {
String sql = "insert into t_fruit values(0,?,?,?,?)";
super.executeUpdate(sql, fruit.getFname(), fruit.getPrice(), fruit.getFcount(), fruit.getRemark());
}
4、分页显示
- 想要分页显示,每页显示5个水果,我们只需要修改对应的 sql 语句就可以了。
FruitDAO 接口实现:
//获取指定页码上的库存列表信息 , 每页显示5条
List<Fruit> getFruitList(Integer pageNo);
FruitDAOImpl 实现类实现:
@Override
public List<Fruit> getFruitList(Integer pageNo) {
return super.executeQuery("select * from t_fruit limit ? , 5", (pageNo - 1) * 5);
}
- 然后我们要在表格下方设置首页、上一页、下一页、尾页的按钮,点击对应按钮可以跳转到对应页面,首先在 index.html 表格下方设置四个按钮。
index.html 修改如下:
<div style="width:60%;margin-left:20%;border:0px solid red;padding-top:4px;" class="center">
<input type="button" value="首 页" class="btn"/>
<input type="button" value="上一页"/>
<input type="button" value="下一页" class="btn"/>
<input type="button" value="尾 页" class="btn"/>
</div>
- 然后设置对应的页面函数,让服务端知道你想去哪个页面需要设置 th:onclick 事件。
index.html 修改如下:
<div style="width:60%;margin-left:20%;border:0px solid red;padding-top:4px;" class="center">
<input type="button" value="首 页" class="btn" th:onclick="|page(1)|"/>
<input type="button" value="上一页" class="btn" th:onclick="|page(${session.pageNo-1})|"/>
<input type="button" value="下一页" class="btn" th:onclick="|page(${session.pageNo+1})|"/>
<input type="button" value="尾 页" class="btn"/>
</div>
解释:
- 对于传给 page 函数的参数,我们需要获取当前会话作用域中保存的当前在第几页是什么,需要请求 session.pageNo,这里会话作用域中的值的设置需要 IndexServlet 组件来实现。
其中的 page 函数需要 JavaScript 来实现:
function page(pageNo){
window.location.href="index?pageNo="+pageNo;
}
IndexServlet 组件实现:
//Servlet从3.0版本开始支持注解方式的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
@Override
public void doGet(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
//获取当前的会话session
HttpSession session = request.getSession() ;
//设置当前页默认值为1
Integer pageNo = 1 ;
String pageNoStr = request.getParameter("pageNo");
//如果从请求中读到了pageNo,就强转,如果没读到就默认为1
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);
}
//更新session中的pageNo值
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(pageNo);
session.setAttribute("fruitList",fruitList);
//此处的视图名称是 index
//那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
//逻辑视图名称 : index
//物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是: / index .html
super.processTemplate("index",request,response);
}
}
- 点击尾页按钮的时候,我们需要跳到最后一页,那我们就需要知道当前数据库数据每页显示 5 个数据的话,一共能分成几页,需要计算一个 pageCount 值,同时修改 index.html 中尾页的 th:onclick 事件。
index.html 修改如下:
<input type="button" value="尾 页" class="btn" th:onclick="|page(${session.pageCount})|"/>
解释:
- 我们还需要知道总共数据库中的数据需要多少页来显示,我们需要请求 session.pageCount 数据。
//Servlet从3.0版本开始支持注解方式的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
@Override
public void doGet(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
//获取当前的会话session
HttpSession session = request.getSession() ;
//设置当前页默认值为1
Integer pageNo = 1 ;
String pageNoStr = request.getParameter("pageNo");
//如果从请求中读到了pageNo,就强转,如果没读到就默认为1
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);
}
//更新session中的pageNo值
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(pageNo);
//更新session中的fruitList列表
session.setAttribute("fruitList",fruitList);
//总记录条数
int fruitCount = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (fruitCount-1)/5 + 1;
//更新session中的pageCount值
session.setAttribute("pageCount",pageCount);
super.processTemplate("index",request,response);
}
}
- 在第一页的时候,我们不能再让用户往前点击了,这个时候 “第一页” 和 “首页” 按钮应该失效不能用了,在最后一页的时候,“下一页” 和 “尾页” 按钮需要失效,所以我们需要设置 th:disabled 属性,表示这个按钮不能使用了。
index.html 修改如下:
<div style="width:60%;margin-left:20%;border:0px solid red;padding-top:4px;" class="center">
<input type="button" value="首 页" class="btn" th:onclick="|page(1)|" th:disabled="${session.pageNo==1}"/>
<input type="button" value="上一页" class="btn" th:onclick="|page(${session.pageNo-1})|" th:disabled="${session.pageNo==1}"/>
<input type="button" value="下一页" class="btn" th:onclick="|page(${session.pageNo+1})|" th:disabled="${session.pageNo==session.pageCount}"/>
<input type="button" value="尾 页" class="btn" th:onclick="|page(${session.pageCount})|" th:disabled="${session.pageNo==session.pageCount}"/>
</div>
解释:
- 当前的 pageNo 和当前的总共页数 pageCount 我们需要从会话作用域中去获取,所以用 session.pageNo 表示请求数据。
5、关键字查询功能
- 在表格上方添加一个按照任意关键字检索的一个功能,首先我们要有一个 div 块,其中前面提示 “请输入查询关键字”,后面有一个输入框能够让用户输入字符,后面还要有一个 “查询” 按钮。
index.html 实现:
<form th:action="@{/index}" method="post" style="float:left;width:60%;margin-left:20%;">
<input type="hidden" name="oper" value="search"/>
请输入关键字:<input type="text" name="keyword" th:value="${session.keyword}"/>
<input type="submit" value="查询" class="btn"/>
</form>
解释:
- 这个文本框中的信息要发送给服务端,所以要设置成 form 表单,同时还由 indexServlet 组件来处理,所以设置 action为 /index, 请求方法设置为 post 方法;
- 因为要区分当前查询的请求和其他正常显示的 index 界面不一样,所以我们可以设置一个隐藏属性 oper 属性,其他操作发出的请求是没有这个 oper属性的,也就是为空;
- 当点击下一页的时候,你输入的这个关键字还要显示在文本框一栏,所以需要从会话保存作用域中请求 session.keyword 参数。
- 我们需要根据这个获取到的关键字来限定从数据库获取到的数据,所以我们需要修改我们的 getFruitList 方法,其中包含文本框的关键字,同时 IndexServlet 组件处理这个请求。
FruitDAO 接口实现:
//获取指定页码上的库存列表信息 , 每页显示5条
List<Fruit> getFruitList(String keyword , Integer pageNo);
FruitDAOImpl 实现类实现:
@Override
public List<Fruit> getFruitList(String keyword, Integer pageNo) {
return super.executeQuery("select * from t_fruit where fname like ? or remark like ? limit ? , 5", "%" + keyword + "%", "%" + keyword + "%", (pageNo - 1) * 5);
}
- 获取 oper 参数,有就是查询操作,没有就是其他操作;
- 如果是查询操作,将此时的 pageNo 设置为 1 ,因为要从第一页开始显示,同时获取它的 keyword 参数值,并将它放置到会话作用域中
- 如果不是查询操作,但也有可能这个时候是我在查询界面,但是点击了上下页操作,所以在 oper 为空的时候也要处理一下 keyword 参数
IndexServlet 组件实现:
//Servlet从3.0版本开始支持注解方式的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码集
request.setCharacterEncoding("UTF-8");
doGet(req,resp);
}
@Override
public void doGet(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
//获取当前的会话session
HttpSession session = request.getSession() ;
//设置当前页默认值为1
Integer pageNo = 1 ;
//获取oper的值,判断查询表单是否由提交
String oper = request.getParameter("oper");
//如果oper!=null 说明 通过表单的查询按钮点击过来的
//如果oper是空的,说明 不是通过表单的查询按钮点击过来的
String keyword = null ;
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
//说明是点击表单查询发送过来的请求
//此时,pageNo应该还原为1 , keyword应该从请求参数中获取
pageNo = 1 ;
keyword = request.getParameter("keyword");
//如果keyword为null,需要设置为空字符串,否则查询的时候会拼接成 %null%,我们期望的是 %%
if(StringUtil.isEmpty(keyword)){
keyword = "" ;
}
//将keyword保存或者覆盖到session中
session.setAttribute("keyword",keyword);
}else{
//说明此处不是点击表单查询发送过来的请求(比如点击下面的上一页下一页或者直接在地址栏输入网址)
//此时keyword应该从session作用域获取
String pageNoStr = request.getParameter("pageNo");
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);//如果从请求中读到了pageNo,就强转,如果没读到就默认为1
}
//如果不是点击的查询按钮,那么查询是基于session中保存的现有keyword进行查询
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String)keywordObj ;
}else{
keyword = "" ;
}
}
//更新session中的pageNo值
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword , pageNo);
session.setAttribute("fruitList",fruitList);
//总记录条数
int fruitCount = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (fruitCount+5-1)/5 ;
/*
总记录条数 总页数
1 1
5 1
6 2
10 2
11 3
fruitCount (fruitCount+5-1)/5
*/
session.setAttribute("pageCount",pageCount);
//此处的视图名称是 index
//那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
//逻辑视图名称 : index
//物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是: / index .html
super.processTemplate("index",request,response);
}
}
解释:
- 因为请求方法为 post 方法,所以要重写 doPost 方法,但是实现逻辑还需要用到之前的那些方法,所以直接在 doPost 方法中调用 doGet 方法,实际实现逻辑添加到 doGet 方法中;
- 由于要与数据库交互,传给数据库一个中文字符,所以要设置编码集,防止中文乱码
6、代码示例
6.1 html 代码
index.html 代码
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/index.css">
<script language="JavaScript" src="js/index.js"></script>
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center f30">欢迎使用水果库存后台管理系统</p>
<div style="border:0px solid red;width:60%;margin-left:20%;text-align:right;">
<form th:action="@{/index}" method="post" style="float:left;width:60%;margin-left:20%;">
<input type="hidden" name="oper" value="search"/>
请输入关键字:<input type="text" name="keyword" th:value="${session.keyword}"/>
<input type="submit" value="查询" class="btn"/>
</form>
<a th:href="@{/add.html}" style="border:0px solid blue;margin-bottom:4px;">添加新库存记录</a>
</div>
<table id="tbl_fruit">
<tr>
<th class="w20">名称</th>
<th class="w20">单价</th>
<th class="w20">库存</th>
<th>操作</th>
</tr>
<tr th:if="${#lists.isEmpty(session.fruitList)}">
<td colspan="4">对不起,库存为空!</td>
</tr>
<tr th:unless="${#lists.isEmpty(session.fruitList)}" th:each="fruit : ${session.fruitList}">
<!-- <td><a th:text="${fruit.fname}" th:href="@{'/edit.do?fid='+${fruit.fid}}">苹果</a></td> -->
<td><a th:text="${fruit.fname}" th:href="@{/edit.do(fid=${fruit.fid})}">苹果</a></td>
<td th:text="${fruit.price}">5</td>
<td th:text="${fruit.fcount}">20</td>
<!-- <td><img src="imgs/del.jpg" class="delImg" th:οnclick="'delFruit('+${fruit.fid}+')'"/></td> -->
<td><img src="imgs/del.jpg" class="delImg" th:onclick="|delFruit(${fruit.fid})|"/></td>
</tr>
</table>
<div style="width:60%;margin-left:20%;border:0px solid red;padding-top:4px;" class="center">
<input type="button" value="首 页" class="btn" th:onclick="|page(1)|" th:disabled="${session.pageNo==1}"/>
<input type="button" value="上一页" class="btn" th:onclick="|page(${session.pageNo-1})|" th:disabled="${session.pageNo==1}"/>
<input type="button" value="下一页" class="btn" th:onclick="|page(${session.pageNo+1})|" th:disabled="${session.pageNo==session.pageCount}"/>
<input type="button" value="尾 页" class="btn" th:onclick="|page(${session.pageCount})|" th:disabled="${session.pageNo==session.pageCount}"/>
</div>
</div>
</div>
</body>
</html>
edit.html 代码
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/edit.css">
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center f30">编辑库存信息</p>
<form th:action="@{/update.do}" method="post" th:object="${fruit}">
<!-- 隐藏域 : 功能类似于文本框 , 它的值会随着表单的发送也会发送给服务器,但是界面上用户看不到 -->
<input type="hidden" name="fid" th:value="*{fid}"/>
<table id="tbl_fruit">
<tr>
<th class="w20">名称:</th>
<!-- <td><input type="text" name="fname" th:value="${fruit.fname}"/></td> -->
<td><input type="text" name="fname" th:value="*{fname}"/></td>
</tr>
<tr>
<th class="w20">单价:</th>
<td><input type="text" name="price" th:value="*{price}"/></td>
</tr>
<tr>
<th class="w20">库存:</th>
<td><input type="text" name="fcount" th:value="*{fcount}"/></td>
</tr>
<tr>
<th class="w20">备注:</th>
<td><input type="text" name="remark" th:value="*{remark}"/></td>
</tr>
<tr>
<th colspan="2">
<input type="submit" value="修改" />
</th>
</tr>
</table>
</form>
</div>
</div>
</body>
</html>
add.html 代码
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/add.css">
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center f30">新增库存信息</p>
<!--<form action="add.do" method="post">-->
<form action="add.do" method="post">
<table id="tbl_fruit">
<tr>
<th class="w20">名称:</th>
<!-- <td><input type="text" name="fname" th:value="${fruit.fname}"/></td> -->
<td><input type="text" name="fname" /></td>
</tr>
<tr>
<th class="w20">单价:</th>
<td><input type="text" name="price" /></td>
</tr>
<tr>
<th class="w20">库存:</th>
<td><input type="text" name="fcount" /></td>
</tr>
<tr>
<th class="w20">备注:</th>
<td><input type="text" name="remark" /></td>
</tr>
<tr>
<th colspan="2">
<input type="submit" value="添加" />
</th>
</tr>
</table>
</form>
</div>
</div>
</body>
</html>
6.2 CSS 代码
index.css 代码
*{
color: threeddarkshadow;
}
a{
text-decoration: none;
}
body{
margin:0;
padding:0;
background-color:#808080;
}
div{
position:relative;
float:left;
}
#div_container{
width:80%;
height:100%;
border:0px solid blue;
margin-left:10%;
float:left;
background-color: honeydew;
border-radius:8px;
}
#div_fruit_list{
width:100%;
border:0px solid red;
}
#tbl_fruit{
width:60%;
line-height:28px;
margin-top:16px;
margin-left:20%;
}
#tbl_fruit , #tbl_fruit tr , #tbl_fruit th , #tbl_fruit td{
border:1px solid gray;
border-collapse:collapse;
text-align:center;
font-size:16px;
font-family:"黑体";
font-weight:lighter;
}
.w20{
width:20%;
}
.delImg{
width:24px;
height:24px;
}
.btn{
border:1px solid lightgray;
width:80px;
height:24px;
}
.center{
text-align:center;
}
.f30{
font-size: 30px;
}
edit.css 代码
*{
color: threeddarkshadow;
}
body{
margin:0;
padding:0;
background-color:#808080;
}
div{
position:relative;
float:left;
}
#div_container{
width:80%;
height:100%;
border:0px solid blue;
margin-left:10%;
float:left;
background-color: honeydew;
border-radius:8px;
}
#div_fruit_list{
width:100%;
border:0px solid red;
}
#tbl_fruit{
width:60%;
line-height:28px;
margin-top:16px;
margin-left:20%;
}
#tbl_fruit , #tbl_fruit tr , #tbl_fruit th , #tbl_fruit td{
border:1px solid gray;
border-collapse:collapse;
text-align:center;
font-size:16px;
font-family:"黑体";
font-weight:lighter;
}
.w20{
width:20%;
}
.delImg{
width:24px;
height:24px;
}
.btn{
border:1px solid lightgray;
width:80px;
height:24px;
}
.center{
text-align:center;
}
.f30{
font-size: 30px;
}
add.css 代码
*{
color: threeddarkshadow;
}
body{
margin:0;
padding:0;
background-color:#808080;
}
div{
position:relative;
float:left;
}
#div_container{
width:80%;
height:100%;
border:0px solid blue;
margin-left:10%;
float:left;
background-color: honeydew;
border-radius:8px;
}
#div_fruit_list{
width:100%;
border:0px solid red;
}
#tbl_fruit{
width:60%;
line-height:28px;
margin-top:16px;
margin-left:20%;
}
#tbl_fruit , #tbl_fruit tr , #tbl_fruit th , #tbl_fruit td{
border:1px solid gray;
border-collapse:collapse;
text-align:center;
font-size:16px;
font-family:"黑体";
font-weight:lighter;
}
.w20{
width:20%;
}
.delImg{
width:24px;
height:24px;
}
.btn{
border:1px solid lightgray;
width:80px;
height:24px;
}
.center{
text-align:center;
}
.f30{
font-size: 30px;
}
6.3 JS 代码
index.js 代码:
function delFruit(fid){
if(confirm('是否确认删除?')){
window.location.href='del.do?fid='+fid;
}
}
function page(pageNo){
window.location.href="index?pageNo="+pageNo;
}
6.4 Servlet 组件实现
IndexServlet 组件
5、关键字查询中有完整代码
EditSevlet 组件
1.1 edit 功能实现中有完整代码
UpdateServlet 组件
1.2 update 功能实现中有完整代码
DelServet 组件
2、删除功能中有完整代码
AddServlet 组件
3、添加新库存记录中有完整代码
7、网页展示
初始页面:
点击下一页:
点击尾页:
点击添加新库存按钮:
点击添加按钮之后尾页显示:
点击某水果进入编辑界面:
修改阿克苏1单价和库存点击修改按钮后:
点击阿克苏2的删除按钮:
确认删除后的尾页:
在查询框内输入阿点击查询按钮后:
总结
以上就是水果库存后台管理系统的初步实现,后序还会使用 MVC 对水果系统中的功能进行封装和修改。