2008年5月27日火曜日

Springサンプルに削除機能を作成する

続いてProductの削除機能です。
今度はサクサク行きました。注意点はcheckboxからのデータ取得だけです。
spring:bindでDeleteProducts.product.idをbindするとcheckboxで選択した値が取れません(選択されたものだけでなく、全てとれてしまいます)。
そのため"targets"というint配列をBeanに追加し、jsp上でid="targets"、name="targets"というタグを書き込んでいます。

【手順概要】

ProductDAOにインターフェースdeleteProductを追加します。

JdbcProductDaoでdeleteProductを実装します。

JdbcProductDaoTestsにテストメソッドを追加します。

ProductManagerにdeleteProductインターフェースを追加します。

SimpleProductManagerでdeleteProductメソッドを実装します。

DeleteProducts Beanを追加します。

DelteProductValidatorを追加します。

DeleteProductFormControllerを追加します。

deleteProduct画面を追加します。

springapp-servlet.xml、メッセージ、リンクにエントリーを追加します。

【手順詳細】
ProductDAOにインターフェースdeleteProductを追加します。
public void deleteProduct(Product prod);

JdbcProductDaoでdeleteProductを実装します。
    @Override
public void deleteProduct(Product prod) {
logger.info("Delete product: " + prod.getDescription());
int count = getSimpleJdbcTemplate().update(
"delete from products where id = :id",
new MapSqlParameterSource().addValue("id", prod.getId()));
logger.info("Deleted: " + count);
}

JdbcProductDaoTestsにテストメソッドを追加します。
    public void testDeleteProduct() {
List<Product> products = new ArrayList<Product>();
Product p1 = createProduct(200, "dummy10", 100.10);
Product p2 = createProduct(201, "dummy11", 100.20);
products.add(p1);
products.add(p2);
for (Product p : products) {
productDao.insertProduct(p);
}
List<Product> insertedProducts = productDao.getProductList();
boolean checked1 = false;
boolean checked2 = false;
for (Product p : insertedProducts) {
if (p.getId() == p1.getId()) {
dumpProduct(p);
dumpProduct(p1);
assertEquals(true, p.equals(p1));
checked1 = true;
}
if (p.getId() == p2.getId()) {
dumpProduct(p);
dumpProduct(p2);
assertEquals(true, p.equals(p2));
checked2 = true;
}
}
if (!checked1 || !checked2) {
fail("Product not inserted.");
}
productDao.deleteProduct(p1);
productDao.deleteProduct(p2);
List<Product> afterDelete = productDao.getProductList();
for (Product p : afterDelete) {
if (p.getId() == p1.getId()) {
fail("Product p1 not deleted.");
}
if (p.getId() == p2.getId()) {
fail("Product p2 not deleted.");
}
}
}

ProductManagerにdeleteProductインターフェースを追加します。
 public void deleteProduct(Product product);

SimpleProductManagerでdeleteProductメソッドを実装します。
 @Override
public void deleteProduct(Product product) {
productDao.deleteProduct(product);
}

DeleteProducts Beanを追加します。
package springapp.service;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import springapp.domain.Product;

public class DeleteProducts {
/** Logger for this class and subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private List<Product> products = null;
int [] targets = null; // ポイント

public void setProducts(List<Product> products) {
this.products = products;
}

public List<Product> getProducts() {
return products;
}

public int[] getTargets() {
return targets;
}

public void setTargets(int[] targets) {
this.targets = targets;
StringBuffer log = new StringBuffer();
log.append("Targets set:");
for(int i = 0; i < targets.length; i++) {
log.append(targets[i]);
log.append(":");
}
logger.info(log);
}
}

DelteProductValidatorを追加します。
package springapp.service;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DeleteProductsValidator implements Validator {

/** Logger for this class and subclasses */
protected final Log logger = LogFactory.getLog(getClass());

public boolean supports(Class clazz) {
return DeleteProducts.class.equals(clazz);
}

public void validate(Object obj, Errors errors) {
DeleteProducts dp = (DeleteProducts) obj;
logger.info("Validating ...");
int[] targets = dp.getTargets();
if (targets == null || targets.length == 0) {
logger.info("Delete target not selected.");
errors.rejectValue("products", "error.target-not-specified", null,
"Value required.");
}
logger.info("Validation end.");
}
}

DeleteProductFormControllerを追加します。
package springapp.web;

import java.util.List;

import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import springapp.domain.Product;
import springapp.service.DeleteProducts;
import springapp.service.ProductManager;

public class DeleteProductsFormController extends SimpleFormController {
/** Logger for this class and subclasses */
protected final Log logger = LogFactory.getLog(getClass());

private ProductManager productManager;

public ModelAndView onSubmit(Object command) throws ServletException {
DeleteProducts dp = (DeleteProducts) command;
List<Product> products = dp.getProducts();
int[] targets = dp.getTargets();
for (int i = 0; i < targets.length; i++) {
for (Product product : products) {
if (targets[i] == product.getId()) {
productManager.deleteProduct(product);
logger.info("Deleted:" + product);
continue;
}
}
}
return new ModelAndView(new RedirectView(getSuccessView()));
}

protected Object formBackingObject(HttpServletRequest request)
throws ServletException {
DeleteProducts dp = new DeleteProducts();
dp.setProducts(productManager.getProducts());
return dp;
}

public void setProductManager(ProductManager productManager) {
this.productManager = productManager;
}

public ProductManager getProductManager() {
return productManager;
}

}

deleteProduct画面を追加します。
<%@ include file="/WEB-INF/jsp/include.jsp" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title><fmt:message key="title"/></title>
<style>
.error { color: red; }
</style>
</head>
<body>
<h1><fmt:message key="deleteproducts.heading"/></h1>
<form:form method="post" commandName="deleteProducts">
<c:forEach items="${deleteProducts.products}" varStatus="row">
<spring:bind path="deleteProducts.products[${row.index}].id">
<input type="checkbox"
<!-- "id"ではチェックボックスの値が取得できません -->
name="targets"
id="targets"
value="<c:out value="${status.value}"/>" />
</spring:bind>
<spring:bind path="deleteProducts.products[${row.index}].description">
<c:out value="${status.value}"/>
</spring:bind>
<spring:bind path="deleteProducts.products[${row.index}].price">
<i>$<c:out value="${status.value}"/></i>
</spring:bind>
<br>
</c:forEach>
<form:errors path="products" cssClass="error"/>
<br>
<input type="submit" align="center" value="Execute">
</form:form>
<a href="<c:url value="hello.htm"/>">Home</a>
</body>
</html>

springapp-servlet.xml
    <bean name="/deleteproducts.htm" class="springapp.web.DeleteProductsFormController">
<property name="sessionForm" value="true"/>
<property name="commandName" value="deleteProducts"/>
<property name="commandClass" value="springapp.service.DeleteProducts"/>
<property name="validator">
<bean class="springapp.service.DeleteProductsValidator"/>
</property>
<property name="formView" value="deleteproducts"/>
<property name="successView" value="hello.htm"/>
<property name="productManager" ref="productManager"/>
</bean>

メッセージ
deleteproducts.heading=Delete products :: SpringApp
error.target-not-specified=Target not specified!!!

hello.jspからのリンク
    <br><a href="<c:url value="deleteproducts.htm"/>">Delete products</a>

以上

0 件のコメント: