3 记账项目
3.1 需求分析
3.1.1 记账管理
其中,查询条件中类型为下拉列表,可选值有“不限,支出,收入,转账,借出,借入,还入,还出”。日期输入框需要输入“yyyy-MM-dd”格式的日期字符串,点击“搜索”按钮提交表单,提交时如果输入项不符合输入要求,则显示相应提示信息。列表中根据记账类别在金额前添加相应的“+,-”符号,如果类别为“支出,借出,还出”时在金额前添加“-”。
如果类别为“收入,借入,还入”时在金额前添加“+”。如果根据输入项进行查询后没有找到账单数据,则给出提示信息,如图所示:
点击“记账”按钮后,进入记账页面。
3.1.2 记账
类型属性是单选框, 标题输入框最长输入不能超过25 字符, 日期输入框需要输入“yyyy-MM-dd”格式的日期字符串,金额输入框必须为大于0 的数,说明输入框中最大输入长度为250 字符。
点击“重置”按钮则恢复初始值
点击“保存”按钮执行保存功能。提交数据至Controller 前必须使用JS 验证。如果各属性输入值不符合要求则需提示用户。
点击“返回”按钮放弃当前记账操作。并返回首页。
功能补充:在“说明”列后加一列,叫“操作”。用户可以删除或修改数据。
分页显示:上一页 下一页 首页 尾页 当前页码 总页数 总条数
3.1.3 参考数据库
create table bills( id int primary key auto_increment, title varchar(50), billtime date, typeid int, price double, explains varchar(50) ); create table billtype( id int primary key auto_increment, bname varchar(5) );
3.2 环境搭建
先创建基本的目录
然后按照上面的步骤配置pom.xml、spring.xml、mybatis.xml、generatorConfig.xml,接着使用mybatis的自动生成功能自动生成
3.3 查询数据
配置web.xml中的请求分发器,以及处理post乱码
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>Archetype Created Web Application</display-name> <!--1 配置前端控制器--> <servlet> <servlet-name>aa</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>aa</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!--2 处理post乱码--> <filter> <filter-name>bb</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>bb</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
3.3.1 展示下拉列表和基本信息
dao:
BillsMapper
package com.wangjiawei.dao; import com.wangjiawei.bean.Bills; import java.util.List; public interface BillsMapper { /** * 查询所有账单 * @return */ public List<Bills> getBills(); int deleteByPrimaryKey(Integer id); int insert(Bills record); int insertSelective(Bills record); Bills selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Bills record); int updateByPrimaryKey(Bills record); }
在BillsMapper.xml中添加对应实现:
<resultMap id="BaseResultMap" type="com.wangjiawei.bean.Bills"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="title" jdbcType="VARCHAR" property="title" /> <result column="billtime" jdbcType="DATE" property="billtime" /> <result column="typeid" jdbcType="INTEGER" property="typeid" /> <result column="price" jdbcType="DOUBLE" property="price" /> <result column="explains" jdbcType="VARCHAR" property="explains" /> </resultMap> <select id="getBills" resultMap="BaseResultMap"> select * from bills </select>
BillTypeMapper
package com.wangjiawei.dao; import com.wangjiawei.bean.Billtype; import java.util.List; public interface BilltypeMapper { /** * 查询所有账单类型 * @return */ public List<Billtype> getTypes(); int deleteByPrimaryKey(Integer id); int insert(Billtype record); int insertSelective(Billtype record); Billtype selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Billtype record); int updateByPrimaryKey(Billtype record); }
在BillTypeMapper.xml中添加对应实现:
<resultMap id="BaseResultMap" type="com.wangjiawei.bean.Billtype"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="bname" jdbcType="VARCHAR" property="bname" /> </resultMap> <select id="getTypes" resultMap="BaseResultMap"> select * from billtype </select>
service层:
BillsService接口:
public interface BillsService { /** * 查询所有账单 * @return */ public List<Bills> getBills(); }
BillsServiceImpl实现类:
@Service public class BillsServiceImpl implements BillsService { @Resource private BillsMapper billsMapper; /** * 查询所有账单 * * @return */ @Override public List<Bills> getBills() { return billsMapper.getBills(); } }
BillTypesService接口:
public interface BillTypesService { /** * 查询所有账单类型 * @return */ public List<Billtype> getTypes(); }
BillTypesService实现类:
@Service public class BillTypesServiceImpl implements BillTypesService { @Resource private BilltypeMapper billtypeMapper; /** * 查询所有账单类型 * * @return */ @Override public List<Billtype> getTypes() { return billtypeMapper.getTypes(); } }
controller层:
BillsController
@Controller public class BillsController { @Resource private BillTypesService typesService; @Resource private BillsService billsService; @RequestMapping("/gettypes") public String gettypes(ModelMap map){ // 1 查询所有账单类型 List<Billtype> types = typesService.getTypes(); // 2 查询所有的账单 List<Bills> bills = billsService.getBills(); // 保存数据给前台 map.addAttribute("types", types); map.addAttribute("bills", bills); return "show"; } }
相应的前端界面:
index.jsp
<html> <body> <script type="application/javascript"> location.href = "/gettypes"; </script> </body> </html>
show.jsp
<%-- Created by IntelliJ IDEA. User: 12291 Date: 2020/10/30 Time: 10:26 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head> <title>Title</title> </head> <body> <h1>记账管理</h1> <p> <form> 类型: <select> <option value="-1">不限</option> <c:forEach items="${types}" var="tp"> <option value="${tp.id}">${tp.bname}</option> </c:forEach> </select> 时间: 从<input type="text" name="begin">到<input type="text" name="end"> <input type="submit" value="搜索"> </form> <input type="button" value="记账"> </p> <table border="1" width="500"> <tr> <td>标题</td> <td>记账时间</td> <td>类别</td> <td>金额</td> <td>说明</td> <td>操作</td> </tr> <c:forEach items="${bills}" var="bill"> <tr> <td>${bill.title}</td> <td><fmt:formatDate value="${bill.billtime}" pattern="yyyy-MM-dd"/></td> <td>类别</td> <td>${bill.price}</td> <td>${bill.explains}</td> <td>删除 修改</td> </tr> </c:forEach> </table> </body> </html>
3.3.2 前端页面【类别】部分的显示
这里需要两表联查:
select * from bills b, billtype t where b.typeid = t.id;
先添加实体类的一对多关系:
Bills类中添加属性
private Billtype billtype;
BillType中添加属性
private List<Bills> billsList;
相应的要修改mapper.xml:
BillsMapper.xml(要展示类别只用修改这一个就行了)
<resultMap id="rs1" type="bills"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="title" jdbcType="VARCHAR" property="title" /> <result column="billtime" jdbcType="DATE" property="billtime" /> <result column="typeid" jdbcType="INTEGER" property="typeid" /> <result column="price" jdbcType="DOUBLE" property="price" /> <result column="explains" jdbcType="VARCHAR" property="explains" /> <association property="billtype" javaType="com.wangjiawei.bean.Billtype"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="bname" jdbcType="VARCHAR" property="bname" /> </association> </resultMap> <select id="getBills" resultMap="rs1"> select * from bills b, billtype t where b.typeid = t.id; </select>
修改前端show.jsp:
3.3.3 前端页面【金额】部分的显示
支出,借出时金额应该有一个负号
只用在前端界面中使用choose when即可
<c:forEach items="${bills}" var="bill"> <tr> <td>${bill.title}</td> <td><fmt:formatDate value="${bill.billtime}" pattern="yyyy-MM-dd"/></td> <td>${bill.billtype.bname}</td> <td> <c:choose> <c:when test="${bill.billtype.bname =='支出' || bill.billtype.bname =='借出' || bill.billtype.bname =='还出'}"> -${bill.price} </c:when> <c:when test="${bill.billtype.bname =='收入' || bill.billtype.bname =='借入' || bill.billtype.bname =='还入'}"> +${bill.price} </c:when> <c:otherwise> ${bill.price} </c:otherwise> </c:choose> </td> <td>${bill.explains}</td> <td>删除 修改</td> </tr> </c:forEach>
3.3.4 通过时间和类型模糊查询
实际上,查询所有的语句和模糊查询的语句是同一种语句,只不过模糊查询的语句是在查询所有语句的基础上进行一些拼接,这两种查询可以调用同一个接口。
前端页面:
为搜索这一部分添加一个form
<form action="/getAllBills" method="post"> 类型: <select name="typeid"> <option value="-1">不限</option> <c:forEach items="${types}" var="tp"> <option value="${tp.id}" ${tid==tp.id?'selected':''}>${tp.bname}</option> </c:forEach> </select> 时间: 从<input type="text" name="begin" value="${begintime}">到<input type="text" name="end" value="${endtime}"> <input type="submit" value="搜索"> </form> <input type="button" value="记账">
controller层:
添加查询所有的方法 getAllBills。
其中添加了回显操作,由于回显操作也要根据type的id进行匹配展示(见前端界面),所以这里也要查询一下所有的type。
/** * 查询所有账单 * @return */ @RequestMapping("/getAllBills") public String getBills(Integer typeid, String begin, String end, ModelMap map){ List<Bills> bills = billsService.getBills(typeid, begin, end); map.addAttribute("bills", bills); // 数据回显 // 将模糊查询的条件值再返回给前台 map.addAttribute("tid", typeid); map.addAttribute("begintime", begin); map.addAttribute("endtime", end); List<Billtype> types = typesService.getTypes(); map.addAttribute("types", types); return "show"; }
service层:
dao接口:
public List<Bills> getBills(int typeid, String begin, String end);
dao实现类:
/** * 查询所有账单 * * @return */ @Override public List<Bills> getBills(int typeid, String begin, String end) { Map params = new HashMap(); params.put("tid", typeid); params.put("begin", begin); params.put("end", end); return billsMapper.getBills(params); }
dao:
修改BillsMapper的getBills方法
public List<Bills> getBills(Map map);
相应的配置文件BillsMapper.xml
<resultMap id="rs1" type="bills"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="title" jdbcType="VARCHAR" property="title" /> <result column="billtime" jdbcType="DATE" property="billtime" /> <result column="typeid" jdbcType="INTEGER" property="typeid" /> <result column="price" jdbcType="DOUBLE" property="price" /> <result column="explains" jdbcType="VARCHAR" property="explains" /> <association property="billtype" javaType="com.wangjiawei.bean.Billtype"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="bname" jdbcType="VARCHAR" property="bname" /> </association> </resultMap> <select id="getBills" resultMap="rs1"> select * from bills b, billtype t where b.typeid = t.id <if test="tid != -1"> and t.id=#{tid} </if> <if test="begin!=null and begin!=''"> and b.billtime>=#{begin} </if> <if test="end!=null and end!=''"> and b.billtime <![CDATA[ <= ]]> #{end} </if> </select>
3.3.5 分页展示
使用工具类PageHelper进行分页操作,在service层进行这个操作。
service:
分页查询需要接收新的参数,当前页数:index和每页显示条数size
接口
public PageInfo<Bills> getBills(int typeid, String begin, String end, int index, int size);
实现类:
@Service public class BillsServiceImpl implements BillsService { @Resource private BillsMapper billsMapper; /** * 查询所有账单 * * @return */ @Override public PageInfo<Bills> getBills(int typeid, String begin, String end, int index, int size) { Map params = new HashMap(); params.put("tid", typeid); params.put("begin", begin); params.put("end", end); // 1 指定分页数据 PageHelper.startPage(index, size); // 2 查询数据 List<Bills> bills = billsMapper.getBills(params); // 3 创建分页工具类 PageInfo<Bills> info = new PageInfo<>(bills); return info; } }
controller层:
controller中修改查询操作的调用,传入一个size和index,并且返回给前端的数据变成PageInfo对象
@RequestMapping("/gettypes") public String gettypes(ModelMap map){ // 1 查询所有账单类型 List<Billtype> types = typesService.getTypes(); // 2 查询所有的账单 PageInfo<Bills> info = billsService.getBills(-1, null, null, 1, PageUtil.PAGESIZE); // 保存数据给前台 map.addAttribute("types", types); map.addAttribute("info", info); return "show"; }
@RequestMapping("/getAllBills") public String getBills(@RequestParam(defaultValue = "1") int index, @RequestParam(defaultValue = "-1") Integer typeid, String begin, String end, ModelMap map){ PageInfo<Bills> info = billsService.getBills(typeid, begin, end, index, PageUtil.PAGESIZE); map.addAttribute("info", info); // 数据回显 // 将模糊查询的条件值再返回给前台 map.addAttribute("tid", typeid); map.addAttribute("begintime", begin); map.addAttribute("endtime", end); List<Billtype> types = typesService.getTypes(); map.addAttribute("types", types); return "show"; }
其中index被定义为一个常量3
public interface PageUtil { public int PAGESIZE = 3; } 1 2 3 前端页面: 后台返回给前端的数据变成PageInfo对象; 要控制点击上一页,下一页时显示的范围; 分页查询的时候记得要带上模糊查询的参数; <%-- Created by IntelliJ IDEA. User: 12291 Date: 2020/10/30 Time: 10:26 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head> <title>Title</title> </head> <body> <h1>记账管理</h1> <p> <form action="/getAllBills" method="post"> 类型: <select name="typeid"> <option value="-1">不限</option> <c:forEach items="${types}" var="tp"> <option value="${tp.id}" ${tid==tp.id?'selected':''}>${tp.bname}</option> </c:forEach> </select> 时间: 从<input type="text" name="begin" value="${begintime}">到<input type="text" name="end" value="${endtime}"> <input type="submit" value="搜索"> </form> <input type="button" value="记账"> </p> <table border="1" width="500"> <tr> <td>标题</td> <td>记账时间</td> <td>类别</td> <td>金额</td> <td>说明</td> <td>操作</td> </tr> <c:if test="${info.list.size() > 0}"> <c:forEach items="${info.list}" var="bill"> <tr> <td>${bill.title}</td> <td><fmt:formatDate value="${bill.billtime}" pattern="yyyy-MM-dd"/></td> <td>${bill.billtype.bname}</td> <td> <c:choose> <c:when test="${bill.billtype.bname =='支出' || bill.billtype.bname =='借出' || bill.billtype.bname =='还出'}"> -${bill.price} </c:when> <c:when test="${bill.billtype.bname =='收入' || bill.billtype.bname =='借入' || bill.billtype.bname =='还入'}"> +${bill.price} </c:when> <c:otherwise> ${bill.price} </c:otherwise> </c:choose> </td> <td>${bill.explains}</td> <td>删除 修改</td> </tr> </c:forEach> </c:if> <c:if test="${info.list.size() == 0}"> <tr> <td colspan="6"> <h3>没有找到任何数据</h3> </td> </tr> </c:if> <tr> <td colspan="6"> <a href="/getAllBills?typeid=${tid}&begin=${begintime}&end=${endtime}">首页</a> <a href="/getAllBills?index=${info.prePage==0?1:info.prePage}&typeid=${tid}&begin=${begintime}&end=${endtime}">上一页</a> <a href="/getAllBills?index=${info.nextPage==0?info.pages:info.nextPage}&typeid=${tid}&begin=${begintime}&end=${endtime}">下一页</a> <a href="/getAllBills?index=${info.pages}&typeid=${tid}&begin=${begintime}&end=${endtime}">尾页</a> 总页数:${info.pages} 总条数:${info.total} </td> </tr> </table> </body> </html>
3.4 记账功能(新增数据)
前端页面:
注意,页面中的name要和后台Bill类的属性名一致
<%-- Created by IntelliJ IDEA. User: 12291 Date: 2020/10/30 Time: 16:52 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Title</title> </head> <body> <h1>记账</h1> <form action="/insertBill" method="post"> <p>类型: <c:forEach items="${types}" var="ty"> <input type="radio" value="${ty.id}" name="typeid">${ty.bname} </c:forEach> </p> <p>标题:<input type="text" style="width: 500px" name="title"></p> <p>日期:<input type="text" name="billtime">金额:<input type="text" name="price"></p> <p>说明:<textarea cols="50" rows="4" name="explains"></textarea></p> <input type="reset" value="重置"> <input type="submit" value="保存"> </form> </body> </html>
dao层的代码是自动生成的,所以已经有了insert方法,直接从service层开始写:
BillsService中添加:
public int insert(Bills record);
实现类,注意要开启事务:
@Override @Transactional public int insert(Bills record) { return billsMapper.insert(record); }
controller:
@RequestMapping("/insertBill") public String add(Bills bills){ int insert = billsService.insert(bills); if (insert > 0){ // 回到主页面 return "redirect:/gettypes"; } // 回到新增页面 return "redirect:/getBillType"; }
测试的时候需要注意,由于采用的是默认springMVC日期输入格式,所以需要的是2020/2/2这种以斜杠分割的方式。
3.5 更新账单
更新账单其实分了两个操作,一个是查询账单,一个是修改账单
3.5.1 查询账单
show.jsp中为修改操作添加a标签
<a href="/findById?bid=${bill.id}">修改</a>
controller定义对应方法:
@RequestMapping("/findById") public String findById(int bid, ModelMap map){ Bills bills = billsService.selectByPrimaryKey(bid); List<Billtype> types = typesService.getTypes(); map.addAttribute("bills", bills); map.addAttribute("types", types); return "update"; }
service层:
接口:
Bills selectByPrimaryKey(Integer id);
实现类:
@Override public Bills selectByPrimaryKey(Integer id) { return billsMapper.selectByPrimaryKey(id); }
dao是自动生成的:
<sql id="Base_Column_List"> id, title, billtime, typeid, price, explains </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from bills where id = #{id,jdbcType=INTEGER} </select>
前端界面:
controller会返回一个update.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Title</title> </head> <body> <h1>更新</h1> <form action="/updateBill" method="post"> <input type="hidden" name="id" value="${bills.id}"> <p>类型: <c:forEach items="${types}" var="ty"> <input type="radio" value="${ty.id}" ${ty.id==bills.typeid?"checked":""} name="typeid">${ty.bname} </c:forEach> </p> <p>标题:<input type="text" style="width: 500px" name="title" value="${bills.title}"></p> <p>日期:<input type="text" name="billtime" value="<fmt:formatDate value="${bills.billtime}" pattern="yyyy/MM/dd"/>">金额:<input type="text" name="price" value="${bills.price}"></p> <p>说明:<textarea cols="50" rows="4" name="explains">${bills.explains}</textarea></p> <input type="reset" value="重置"> <input type="submit" value="保存"> </form> </body> </html>
3.5.2 修改操作
controller:
@RequestMapping("/updateBill") public String updateBill(Bills bills){ int i = billsService.updateByPrimaryKey(bills); if (i > 0){ return "redirect:/gettypes"; } return "redirect:/findById?bid=" + bills.getId(); }
修改成功返回show页面,修改失败返回update页面。
service层:
@Override public int updateByPrimaryKey(Bills record) { return billsMapper.updateByPrimaryKey(record); }
dao也是自动生成的:
<update id="updateByPrimaryKey" parameterType="com.wangjiawei.bean.Bills"> update bills set title = #{title,jdbcType=VARCHAR}, billtime = #{billtime,jdbcType=DATE}, typeid = #{typeid,jdbcType=INTEGER}, price = #{price,jdbcType=DOUBLE}, explains = #{explains,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER} </update>
3.5.3 删除操作
show.jsp中为删除添加一个按钮
<a href="/deleteById?bid=${bill.id}">删除</a>
controller层:
添加删除方法
@RequestMapping("/deleteById") public void delete(int bid, HttpServletResponse response){ int i = billsService.deleteByPrimaryKey(bid); response.setContentType("text/html;charset=utf-8"); try { PrintWriter writer = response.getWriter(); if (i > 0){ writer.print("<script>alert('删除成功');location.href='/gettypes'</script>"); return; } writer.print("<script>alert('删除失败');location.href='/gettypes'</script>"); } catch (IOException e) { e.printStackTrace(); } }
service层:
接口
public int deleteByPrimaryKey(Integer id);
实现类
@Override @Transactional public int deleteByPrimaryKey(Integer id) { return billsMapper.deleteByPrimaryKey(id); }