Spring 20 基础增删改查项目 – 修改和删除Customer

作者 柚爸

依然是思路最重要,这次是修改一个对象,很显然,必须要知道需要修改哪个对象,然后将对象展示在一个表单里供修改,修改完成后更新数据库,返回列表页。

经过思考可以发现,将数据保存入数据库,和我们编写的新增代码本质上一样,只是保存的内容不是新的东西,而是从数据库中取出的对象。所以首先要解决的问题就是如果知道修改哪一个。

通过项目一开始的界面介绍可以知道,为每个数据添加一个链接,在生成列表的时候确定好每个链接附带的id,通过id就可以知道要修改哪个对象了。

修改列表页添加操作列

为每一行数据添加一个操作链接,在其中附带上这行对应的数据库中的id就可以了,修改list-customers.jsp:

<table>
    <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Email</th>
        <th>Action</th>
    </tr>

    <c:forEach var="tempCustomer" items="${customers}">
        
        <!--创建一个updateLink变量,使用GET参数customerId=id-->
        <c:url var="updateLink" value="/customer/showFormForUpdate">
            <c:param name="customerId" value="${tempCustomer.id}"/>
        </c:url>

        <tr>
            <td> ${tempCustomer.firstName} </td>
            <td> ${tempCustomer.lastName} </td>
            <td> ${tempCustomer.email} </td>
            <td><a href="${updateLink}">Update</a> </td>
        </tr>
    </c:forEach>
</table>

在table标签内加了Action列,然后我们通过JSTL标签库做了一个GET请求的链接地址,然后将这个链接放到每一行的Action列中。

在项目运行后,页面中的实际链接是这样:

http://localhost:8080/customer/showFormForUpdate?customerId=1

可以看一下这条链接与JSTL标签里属性的对应关系。这样我们编写对应地址的控制器,就可以拿到id数据,从而展示和更新数据。

编写控制器和视图

很显然,要编写一个和GET请求地址对应的控制器,从请求中取得id值,还记得直接绑定request中的参数吗:

@GetMapping(value = "/customer/showFormForUpdate")
public String updateCustomer(@RequestParam("customerId") int customerId, Model model) {
    //获取customerId对应的customer对象
    Customer customer = customerService.getCustomer(customerId);

    //将customer对象设置到model上去
    model.addAttribute("customer", customer);

    return "customer-form";
}

这里直接将请求中的customerId对应的值绑定给控制器方法的参数,然后先取出对象,再将对象设置到model上,很显然,这里要为Service和DAO层再编写一系列抽象方法及实现了。

这里省略掉接口抽象方法,DAO和Service层如下:

//DAO
@Override
public Customer getCustomer(int customerId) {
    Session session = sessionFactory.getCurrentSession();
    return session.get(Customer.class, customerId);
}

//Service
@Override
@Transactional
public Customer getCustomer(int customerId) {
    return customerDAO.getCustomer(customerId);
}

更新customer方法和新增customer方法都使用了customer-form.jsp,不同之处是新增customer的时候传递的是一个新的对象,而更新的时候传递的是一个已知的对象。由于在之前我们知道表单标签在生成页面的时候调用getter方法,提交的时候调用setter方法,那么表单应该会显示出要修改的内容,提交后就修改成功。

启动项目后测试成功,发现可以修改。但是这里还有一点要注意,如果表单是直接通过链接生成的,或者服务中断,很有可能不知道到底是新的数据还是老的数据,所以一般的做法是在页面中放一个隐藏的input标签,用于记录id,如果有id就说明当前页面内的数据是一个老数据,如果没有id,就是一个新数据。

修改customer-form.jsp,在form:form标签内添加一行:

<form:hidden path="id"/>

之后还需要一处至关重要的地方,就是修改DAO层的saveCustomer方法:

@Override
public void saveCustomer(Customer customer) {
    Session session = sessionFactory.getCurrentSession();
    session.saveOrUpdate(customer);
}

.saveOrUpdate(customer)方法会自动判断这个数据对象的主键,如果存在就更新,不存在就新建。

这样我们通过一个隐藏字段,就彻底杜绝了可能出现的数据错误。在日常的Web开发中,这种做法是非常普遍的,可以避免页面加载不完全,或者反复提交等问题。

删除Customer

这个项目写到这份上也基本上知道删除该怎么做了,次序分别是在列表页添加删除按钮–编写控制器处理删除请求–到数据库中删除–返回列表页。

当然,一点就删除这个还是危险了点,有很多库可以实现一个对话框来判断,这里我们简单一些,就用一小段JS代码来确认。

创建删除链接

删除链接依然使用JSTL来生成:

<table>
    <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Email</th>
        <th>Action</th>
    </tr>

    <c:forEach var="tempCustomer" items="${customers}">

        <c:url var="updateLink" value="/customer/showFormForUpdate">
            <c:param name="customerId" value="${tempCustomer.id}"/>
        </c:url>

        <c:url var="deleteLink" value="/customer/delete">
            <c:param name="customerId" value="${tempCustomer.id}"/>
        </c:url>

        <tr>
            <td> ${tempCustomer.firstName} </td>
            <td> ${tempCustomer.lastName} </td>
            <td> ${tempCustomer.email} </td>
            <td><a href="${updateLink}">Update</a>|<a href="${deleteLink}" onclick="if(!(confirm('确定删除吗?' ))) return false">Delete</a></td>
        </tr>
    </c:forEach>
</table>

使用id生成了一个删除链接,传递给delete视图控制器。

编写控制器,Service和DAO层方法

先是控制器,套路已经很熟悉了

@GetMapping(value = "/delete")
public String deleteCustomer(@RequestParam("customerId") int customerId) {

    customerService.deleteCustomer(customerId);

    return "redirect:/customer/list";
}

之后是Service和DAO层,接口的代码就省略了。

//Service
@Override
@Transactional
public void deleteCustomer(int customerId) {
    customerDAO.deleteCustomer(customerId);
}

//DAO
@Override
public void deleteCustomer(int customerId) {
    Session session = sessionFactory.getCurrentSession();
    Customer customer = session.get(Customer.class, customerId);
    session.delete(customer);
}

实验一下,搞定啦。第一个增删改查项目顺利完工。

这个项目还有几个可以改进的地方:

  1. 为表单和Entity Class添加验证功能,在控制器类里加上预处理器
  2. 将域注入都改成依赖注入
  3. 按照Customer的名字进行模糊查询

这些改进的功能如何实现,其实都已经有了解决方法。现在继续学习Spring的另外一个重要的内容:AOC。