基于Spring MVC的CRUD控制器
package cn.jcwx.core.web;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.validation.Validator;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.ByteArrayMultipartFileEditor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.WebUtils;
import cn.jcwx.core.service.HibernateDao;
import cn.jcwx.core.service.support.ScrollResult;
import cn.jcwx.core.tag.table.TableTag;
import cn.jcwx.core.utils.BeanUtils;
import cn.jcwx.core.utils.StringUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
/**
* 实现了CRUD功能的Controller <br/>
* 建议子类重写回调函数 <br/><br/>
*
* 保存或更新实体:save.do <br/>
* 删除实体:remove.do <br/>
* 查询单个实体信息:show.do <br/>
*
* @author [email protected]
* @date 2011-2-23
* @version 1.0
*/
public abstract class CrudController<T>{
@Resource
private Validator validator;
protected static final Logger logger = LoggerFactory.getLogger(CrudController.class);
protected Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
protected String className;
protected String entityName;
protected T entity;
protected String listView = null;
protected String formView = null;
public static final String successView = "/share/common/success.jsp";
public CrudController() {
className = BeanUtils.getSuperClassGenricType(getClass()).getName();
entityName = StringUtils.substringAfterLast(className, ".");
entityName = entityName.substring(0, 1).toLowerCase() + entityName.substring(1, entityName.length());
listView = "list.jsp";
formView = "form.jsp";
}
/**
* 获取实体服务类的实例
* @return
*/
protected abstract HibernateDao<T, Integer> getEntityService();
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
binder.registerCustomEditor(Integer.class, new CustomNumberEditor(Integer.class, true));
binder.registerCustomEditor(Double.class, new CustomNumberEditor(Double.class, true));
binder.registerCustomEditor(MultipartFile.class, new ByteArrayMultipartFileEditor());
}
/**
* 根据默认的请求参数进行分页查询。
* 回调函数:beforeDoList(...), afterDoList(...)
* @param request 当前的HttpServletRequest
* @param filterMap 过滤条件,如:filterMap.put("id_EQ_I", 12)。 可以为null
*
* @param sortMap 排序条件,如:sortMap.put("id", "desc"); asc为正序,desc为倒序。 可以为null
* @return ScrollResult<T>
*/
@RequestMapping(value = "list.do")
public ModelAndView doList(HttpServletRequest request, HttpServletResponse response){
ModelAndView mav = new ModelAndView(listView);
//提取客户端可能传送的过滤条件和排序条件
Map<String, Object> filterMap = WebUtils.getParametersStartingWith(request, "search_");
Map<String, Object> sortMap = WebUtils.getParametersStartingWith(request, "sort_");
Integer no, size;
String pSize = (String) request.getParameter("p_size");
String pNo = (String) request.getParameter("p_no");
if (StringUtils.isEmpty(pNo)) {
no = 1;
} else {
no = Integer.parseInt(pNo);
}
if (StringUtils.isEmpty(pSize)) {
size = TableTag.DEF_ROWS_MIN;
} else {
size = Integer.parseInt(pSize);
}
beforeList(request, filterMap, sortMap);
ScrollResult<T> result = (ScrollResult<T>) getEntityService().findScrollResult(no, size, filterMap, sortMap);
mav.addObject(entityName + "s", result.getResultList());
mav.addObject("totalRows", result.getTotal());
afterList(request, response, mav);
return mav;
}
/**
* 分页查询(list.do)回调函数,该方法在执行查询之前调用。可以继续添加过滤条件和排序条件。
* @param request
* @param filterMap
* @param sortMap
*/
protected void beforeList(HttpServletRequest request, Map<String, Object> filterMap, Map<String, Object> sortMap){};
/**
* 分页查询(list.do)回调函数,该方法在返回视图之前调用。可以继续添加返回信息。
* @param request
* @param response
* @param mav
*/
protected void afterList(HttpServletRequest request, HttpServletResponse response, ModelAndView mav){};
/**
* 定向到新增实体的表单界面<br/>
* 回调函数:onCreate(...)
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping(value = "create.do")
public ModelAndView doCreate(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView(formView);
onCreate(request,response, mav);
return mav;
}
/**
* 新增实体的表单界面(create.do)回调函数。该方法在返回视图之前调用,可以继续添加返回信息。
* @param request
* @param response
* @param mav
*/
protected void onCreate(HttpServletRequest request, HttpServletResponse response, ModelAndView mav){};
/**
* 表单编辑界面
*/
@RequestMapping(value = "edit.do")
public ModelAndView edit(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView(formView);
String id = request.getParameter("id");
if (StringUtils.isNotEmpty(id)) {
entity = getEntityService().get(Integer.valueOf(id));
mav.addObject(entityName, entity);
}
onEdit(entity, mav, request);
return mav;
}
protected void onEdit(T entity, ModelAndView mav, HttpServletRequest request){};
/**
* 保存实体<br/>
* 回调函数:beforeDoSave(...), afterDoSave(...)
* @param request
* @param response
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
@RequestMapping(value = "save.do")
public ModelAndView doSave(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView(successView);
String id = request.getParameter("id");
if (StringUtils.isEmpty(id)) {
entity = (T) BeanUtils.getSuperClassGenricType(getClass()).newInstance();
} else {
entity = getEntityService().get(Integer.valueOf(id));
}
boolean check = beforeBindRequestEntity(request, entity, mav);
if(!check)
return mav;
beforeBindRequestEntity(request, entity, mav);
BindException errors = bindRequestEntity(request, entity);
beforeSave(request, entity, errors, mav);
if (errors.hasErrors()) {
logger.error(errors.getMessage());
mav.addObject("msg", getMessageFromErrors(errors));
mav.addObject("state", "failed");
mav.addObject("url", "list.do");
return mav;
}
getEntityService().save(entity);
mav.addObject("msg", "保存成功!");
mav.addObject("state", "ok");
afterSave(request, response, mav, entity);
return mav;
}
/**
* 从Request中绑定对象并进行校验.
*/
protected BindException bindRequestEntity(HttpServletRequest request, T entity) throws Exception {
ServletRequestDataBinder binder = new ServletRequestDataBinder(entity);
initBinder(binder);
binder.bind(request);
BindException errors = new BindException(binder.getBindingResult());
validator.validate(entity, errors);
return errors;
}
/**
* 保存实体(save.do)回调函数,在执行实体与Request参数绑定之前调用用。
* 注意:由于entity可能是托管对象,对entity所做的修改都将反映到数据库。
* 所以有必要在此方法中进行前期的数据校验,以免发生意外。
* @param request
* @param entity
* @param mav
* @return 是否通过校验
*/
protected boolean beforeBindRequestEntity(HttpServletRequest request, T entity, ModelAndView mav){ return false;};
/**
* 保存实体(save.do)回调函数,在执行保存之前调用用。可以进行数据校验。
* @param request HttpServletRequest
* @param entity 实体对象
* @param errors BindException 可以添加错误信息
* @param mav ModelAndView
*/
protected void beforeSave(HttpServletRequest request, T entity, BindException errors, ModelAndView mav){};
/**
* 保存实体(save.do)回调函数,在返回视图之前调用用。可以继续添加返回信息。
* @param request
* @param response
* @param mav
*/
protected void afterSave(HttpServletRequest request, HttpServletResponse response, ModelAndView mav, T entity){};
@RequestMapping(value = "remove.do")
public ModelAndView doRemove(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView(successView);
String id = request.getParameter("id");
T t = getEntityService().load(Integer.valueOf(id));
beforeRemove(request, response, t);
getEntityService().remove(t);
afterRemove(request, response, mav, new StringBuilder().append("成功删除")
.append("1个").append(entityName)
.append(" id:").append(id).toString());
return mav;
}
/**
* 批量删除实体<br/>
* 回调函数:beforeDoRemove(...), afterDoRemove(...)
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping(value = "batchRemove.do")
public ModelAndView doBatchRemove(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView(successView);
String[] ids = request.getParameterValues("itemlist");
String entity = request.getParameter("entityName");
StringBuilder sb = null;
int success = 0;
T t = null;
if (ids != null) {
for (String id : ids) {
t = getEntityService().get(Integer.valueOf(id));
beforeRemove(request, response, t);
getEntityService().remove(t);
success++;
}
sb = new StringBuilder().append("成功删除")
.append(success).append("个").append(entity)
.append(" ids:").append(Arrays.toString(ids));
mav.addObject("msg", sb.toString());
} else {
mav.addObject("msg", "未选择" + entity);
}
afterRemove(request, response, mav, sb.toString());
return mav;
}
/**
* 删除实体(remove.do)回调函数,在执行保存之前调用用。可以进行数据校验。
* @param request
* @param response
* @param entity 实体对象
*/
protected void beforeRemove(HttpServletRequest request, HttpServletResponse response, T entity){};
/**
* 删除实体(remove.do)回调函数,在返回视图之前调用用。可以继续添加返回信息。
* @param request
* @param response
* @param mav
*/
protected void afterRemove(HttpServletRequest request, HttpServletResponse response, ModelAndView mav, String msg){};
/**
* 查询实体信息<br/>
* 回调函数:onShow(...)
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping(value = "show.do")
public ModelAndView doShow(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView(formView);
entity = getEntityService().get(Integer.valueOf(request.getParameter("id")));
mav.addObject(entityName, entity);
onShow(request, response, entity, mav);
return mav;
}
/**
* 查询实体信息(show.do)回调函数,在返回视图之前调用用。可以继续添加返回信息。
* @param request
* @param response
* @param entity 实体对象
* @param mav
*/
protected void onShow(HttpServletRequest request, HttpServletResponse response, T entity, ModelAndView mav){};
@SuppressWarnings("unchecked")
private String getMessageFromErrors(BindException errors){
StringBuilder sb = new StringBuilder();
sb.append("错误信息:");
List<ObjectError> list= errors.getAllErrors();
for(ObjectError error : list){
sb.append(error.getDefaultMessage()).append(";");
}
return sb.toString();
}
/**
* 向客户端写入JSON数据
* @param response
* @param element JsonElement的子类可以是JsonArray或JsonObject
*/
protected void writeJsonDate(HttpServletResponse response, JsonElement element){
try {
response.setCharacterEncoding("UTF-8");
response.getWriter().print(element.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}