本系统仅使用servlet以及thymeleaf技术,记录设计时的逻辑思路,暂时还未考虑servlet优化以及Filter和Listener,也并未将所有源码粘贴展示。
1. 首页展示效果
1.1 将数据库中的库存记录显示到前台
获取IndexServlet传过来的fruitList列表,如果没有,则显示为库存为空:
<tr th:if="${#lists.isEmpty(session.fruitList)}">
<td colspan="4">对不起,库存为空!</td>
</tr>
如果有则通过session域获取,通过thymeleaf语法在前端进行展示:
<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 th:text="${fruit.price}">5</td>
<td th:text="${fruit.fcount}">20</td>
<td><img src="imgs/del.jpg" class="delImg" th:onclick="|delFruit(${fruit.fid})|"/></td>
</tr>
接下来就需要IndexServlet能够获取到数据库的值,那么先对FruitDAO接口定义一个能够查询数据库的方法:
//获取指定页码的库存列表信息,每页显示5条
List<Fruit> getFruitList(String keyword,Integer pageNo);
这里的FruitDAO继承了BaseDAO,BaseDAO是定义了一些数据库的通用增删改查操作以及数据库的连接方法。
接下来在FruitDAOImpl实现类中实现该方法的具体操作:
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);
}
这里使用到的keyword是在后述中的实现查询操作的时候修改了,这里可以先忽视。
在IndexServlet中创建FruitDAOImpl实现类的对象,调用该方法得到一个fruitList,然后将该对象保存到session作用域中:
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
//保存到session作用域
HttpSession session = request.getSession() ;
session.setAttribute("fruitList",fruitList);
这样前台就能拿到作用域中的fruitList进行展示
1.2 翻页功能的实现
由于上述代码仅仅实现了在首页展示数据库中5条记录的功能,并未实现翻页功能,所以在这节实现翻页功能的设计与实现。
同样的,先设计前端的翻页按钮:
<div style="width: 60%; margin-left: 20%; text-align: center; padding-top: 4px;">
<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}"/>
<span th:text="${session.pageNo}"></span>
<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>
然后设计点击按钮执行的js方法:
function page(pageNo){
window.location.href="index?pageNo="+pageNo;
}
点击按钮将会执行page()方法,同样是在session作用域中取到pageNo,然后直接使用pageNo + 1或pageNo - 1进行上一页下一页的实现操作,需要能在session中获取到pageNo,那么就需要对IndexServlet进行相应的设计:
Integer pageNo = 1;
String pageNoStr = request.getParameter("pageNo");
if (StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);
}
//放入session会话域
session.setAttribute("pageNo",pageNo);
首页只需要调用page(1),尾页则需要知道库存中有多少库存记录,通过库存记录数来计算得到库存的总页数到底是多少,所以就需要对FruitDAO和FruitDAOImpl添加相应的方法:
//查询数据库中的条目数
int fruitCount(String keyword);
public int fruitCount(String keyword) {
return ((Long)super.executeComplexQuery("select count(*) from t_fruit where fname like ? or remark like ?","%"+keyword+"%","%"+keyword+"%")[0]).intValue();
}
然后在IndexServlet中计算pageCount并将其保存到session作用域:
int fruitCount = fruitDAO.fruitCount(keyword);
int pageCount = (fruitCount + 5 - 1) / 5;
session.setAttribute("pageCount",pageCount);
那么实现尾页的操作就只需要调用page(${session.pageCount})方法
这样设计其实存在一个弊端,在页码为1时上一页的功能仍然可用,此时就会出现负数页的情况,解决这个问题的很简单,只需要在按钮之后设置一个禁用属性即可:上述的:
<!--这里是需要使用thymeleaf获取session中的pageNo值,所以使用th-->
th:disabled="${session.pageNo==1}
2. 编辑水果库存功能的实现
前端edit.html的form表单的设计:
<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="*{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>
这里将表单提交到updateServlet处理:
//1.设置编码
req.setCharacterEncoding("UTF-8");
//2.获取参数
String fidStr = req.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = req.getParameter("fname");
String priceStr = req.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = req.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = req.getParameter("remark");
//3.执行更新
fruitDAO.updateFruit(new Fruit(fid,fname,price,fcount,remark));
这里就需要调用fruitDAO中的updateFruit方法,所以在FruitDAO及其实现类中添加相应的方法:
//修改指定的库存记录
void updateFruit(Fruit fruit);
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());
}
然后资源跳转,回到index
resp.sendRedirect("index");
此处需要重定向,目的是重新给IndexServlet发请求,重新获取fruitList,然后覆盖到session中。这样主页上显示的数据才是最新的
3. 新增水果库存的实现
add.html的form表单:
<form action="add.do" method="post">
<table id="tbl_fruit">
<tr>
<th class="w20">名称:</th>
<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>
提交后将表单交给AddServlet处理,此时AddServlet获取传来的值,调用DAO的添加库存方法,所以需要向FruitDAO及其实现类添加相应的方法:
//添加一条记录
void addFruit(Fruit fruit);
public void addFruit(Fruit fruit) {
String sql = "insert into t_fruit values(0,?,?,?,?)";
super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark());
}
然后在AddServlet中获取表单的指,将其添加到数据库中:
//1.设置编码
req.setCharacterEncoding("UTF-8");
//2.获取客户端传过来的值
String fname = req.getParameter("fname");
Integer price = Integer.parseInt(req.getParameter("price"));
int fcount = Integer.parseInt(req.getParameter("fcount"));
String remark = req.getParameter("remark");
//3.添加进入数据库
fruitDAO.addFruit(new Fruit(0,fname,price,fcount,remark));
然后重定向刷新index
resp.sendRedirect("index");
4 删除库存功能的实现
在index的del图标上添加鼠标点击的js方法:
<td><img src="imgs/del.jpg" class="delImg" th:onclick="|delFruit(${fruit.fid})|"/></td>
function delFruit(fid){
if (confirm("是否确认删除?")){
window.location.href='del.do?fid=' + fid;
}
}
传入到DelServlet处理
String fidStr = req.getParameter("fid");
if (StringUtil.isNotEmpty(fidStr)){
Integer fid = Integer.parseInt(fidStr);
fruitDAO.delFruit(fid);
这里就需要在FruitDAO及其实现类添加删除方法:
//删除指定id的记录
void delFruit(Integer fid);
public void delFruit(Integer fid) {
String sql = "delete from t_fruit where fid = ?";
super.executeUpdate(sql,fid);
}
最后重定向到首页index刷新数据:
resp.sendRedirect("index");
5. 查询功能的实现
index.html相关表单设计
<div style="border:0px solid red; width: 60%; margin-left: 20%; text-align: right; float: left;">
<form th:action="@{/index}" method="post" style="float: left;">
<input type="hidden" name="oper" value="search">
请输入查询的关键字:<input type="text" name="keyword"/>
<input type="submit" th:value="查询" class="btn">
</form>
<a th:href="@{/add.html}" style="border:0px solid blue; margin-bottom: 4px" >添加新库存记录</a>
</div>
将查询的关键字和隐藏域的oper的值传入到IndexServlet中,就查询逻辑设计IndexServlet的逻辑:
request.setCharacterEncoding("UTF-8");
Integer pageNo = 1;
HttpSession session = request.getSession() ;
String oper = request.getParameter("oper");
String keyword = null;
//如果oper是null说明不是通过表单的查询按钮进来的
//如果oper不是null则说明是通过表单的查询按钮进来的
if (StringUtil.isNotEmpty(oper) && "search".equals(oper)){
//说明是点击表单查询提交过来的请求
//此时pageNo应该还原为1.keyword应该从请求参数中获取
pageNo = 1;
keyword = request.getParameter("keyword");
if (StringUtil.isEmpty(keyword)){
keyword = "";
}
session.setAttribute("keyword",keyword);
}else {
//否则就是点击例如上一页、下一页或者直接在地址栏输入网址
//此时keyword应该从session作用域中获取
String pageNoStr = request.getParameter("pageNo");
if (StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);
}
Object keywordObj = session.getAttribute("keyword");
if (keywordObj != null){
keyword = (String) keywordObj;
}else {
keyword = "";
}
}
6. 实现效果
主页:
改:
增:
删:
查: