在开始之前,依然要先想好我们需要完成的目标。
添加Customer功能的流程是:
- 用户点击新增的添加客户按钮
- 跳转到一个空白表单供用户填写
- 表单验证,如果失败需要提示错误信息,成功则返回列表页面
- 列表页面展示更新后的数据
因此我们采取如下的开发步骤:
- 修改列表页的JSP,添加一个新增Customer按钮与对应链接
- 编写控制器方法和表单页面,对应新增Customer按钮的链接
- 点击表单的提交按钮后处理表单数据
- 根据处理结果展示错误提示信息或者跳转列表页
其实会了Django之后再来看这个,就是逐个编写对应功能的MVC了,没有太复杂的业务架构,重在具体代码编写:
创建添加Customer的按钮
在table标签之前放一个input元素或者button元素都可以,也可以用A标签加上按钮样式。这里采用Input标签加一个简单的JS脚本的方式:
<input type="button" value="Add Customer" onclick="window.location.href='showFormForAdd';return false;" class="add-button">
很显然,接下来要先为这个A标签做一个控制器方法展示表单:
@GetMapping(value = "showFormForAdd") public String showFormForAdd(Model model) { model.addAttribute("customer", new Customer()); return "customer-form"; }
简单的方法,接受GET请求,创建一个空白的Customer对象返回给customer-form.jsp,接下来就是编写提交表单的页面和对应的表单处理控制器了,而这个控制器就会延伸使用到Service层以及DAO层去新增数据。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <html> <head> <title>Add Customer</title> <link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/add-customer-style.css"> </head> <body> <div id="wrapper"> <div id="header"> <h2 style="text-align: center;">Add a new Customer</h2> </div> </div> <div id="container"> <div id="content"> <form:form action="saveCustomer" modelAttribute="customer" method="post"> First Name: <form:input path="firstName" required="required"/> <form:errors path="firstName" cssClass="error"/> <br><br> Last Name: <form:input path="lastName" required="required"/> <form:errors path="lastName" cssClass="error"/> <br><br> Email: <form:input path="email" type="email" required="required"/> <form:errors path="email" cssClass="error"/> <br><br> <input type="submit" value="Save"> </form:form> <br><br> <p><a href="${pageContext.request.contextPath}/customer/list">Back to List</a></p> </div> </div> </body> </html>
这个页面使用了表单标签,表单内配置好字段及错误信息,这些在表单验证的地方都做过了。还添加了一个链接用于返回列表页。
需要注意的是表单的action属性如果不加前边的斜杠,表示当前路径的相对路径,这里也可以写成action="/customer/saveCustomer"
。
还有一点是,<form:input>
这个标签会自动生成对应的实际input标签,path这个路径就是这个标签特有的,而不是HTML的规范。至于HTML的规范比如type="email"
和required="required"
可以自行添加在标签上,这些普通的标签是会直接反映到HTML页面上的。而path标签的作用实际上就是自动产生了这个input标签的name属性。
然后来编写处理表单POST请求的控制器方法:
@PostMapping(value = "/saveCustomer") public String saveCustomer(@ModelAttribute("customer") Customer customer) { System.out.println(customer); return "redirect:/customer/list"; }
为了简化,先搭出架构,这里暂时没有引入校验和保存。按照我们的逻辑,如果操作成功,则应该返回更新数据后的列表页。这里我们使用了之前学到的绑定属性参数的方法,然后打印一下Customer对象看看是不是成功获取数据。
之后return的字符串有讲究:在路径前加上redirect:
,表示重定向到对应路径,视图解析器不会把这个当成视图名称。
然后测试一下,发现正常获取了Customer对象及数据,也正常返回了列表页,那么后边就通过Service层和DAO层将其存入数据库。
先修改刚才的控制器方法,去掉控制台打印,改成调用Service对象的方法,当然,这个方法此时还没有编写,后边还需要编写:
@PostMapping(value = "/saveCustomer") public String saveCustomer(@ModelAttribute("customer") Customer customer) { return "redirect:/customer/list"; }
在CustomerService接口里增加该方法,并且修改实现类:
//接口 public interface CustomerService { List<Customer> getCustomers(); void saveCustomer(Customer customer); } //实现类实现抽象方法 @Override @Transactional public void saveCustomer(Customer customer) { customerDAO.saveCustomer(customer); }
可见这个套路是一样的,接下来更新DAO的接口和实现类:
//DAO接口 public interface CustomerDAO { List<Customer> getCustomers(); void saveCustomer(Customer customer); } //DAO实现类实现抽象方法 @Override public void saveCustomer(Customer customer) { Session session = sessionFactory.getCurrentSession(); session.save(customer); }
现在再启动项目试验一下可以正常添加了,由于都是字符串,必填字段和电子邮件格式我们都通过input标签的属性交给浏览器去验证,所以暂时先不用管表单验证。
最后还有两个小豆知识:
- 在Intellij里,经常会提示查询语句或者Entity Class中字符串形式的表名无法解析,其实需要在DateSources让其自动检测一下Hibernate和c3pO的连接,然后配置好数据连接并实际连接一下更新数据表结构,就可以认出来数据表的字符串了,打错的时候也会有提示,很好用。
- 现在我们的搜索都没有按照顺序排序,其实是没有意义的,在DAO层的查询中可以改成
"From Customer customer ORDER BY customer.lastName"
即可。