在web开发中,很大一部分时间都是直接和数据库打交道,除非和别的公司合作,需要通过接口来访问或操作数据。因此,任何一家基于web开发的公司,其框架基本上包含其特有的MVC体系,数据库访问框架设计等。本文是笔者个人对这方面的一个小小的思考结果。
先照例展现包结构:
图中圈出一个DataAccessController类,笔者认为该类的设计是一个核心,各种业务层级的controller,如果需要基于这种模式做数据库访问,可以继承它。该类稍后介绍,先看一下如果对user信息做数据库访问操作,用与不用DataAccessController的差别。
(1)不用DataAccessController,代码如下:
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import com.wantall.application.mvc.bo.UserBO;
import com.wantall.application.mvc.po.User;
public class UserController extends MultiActionController {
private UserBO userBO;
public ModelAndView insertUser(HttpServletRequest request,
HttpServletResponse response) {
String userName = request.getParameter("userName");
String password = request.getParameter("password");
String showName = request.getParameter("showName");
boolean status = true;
User user = new User();
user.setName(userName);
user.setPassword(password);
user.setShowName(showName);
user.setStatus(status);
userBO.insert(user);
return null;
}
public ModelAndView updateUser(HttpServletRequest request,
HttpServletResponse response) {
int userID = Integer.parseInt(request.getParameter("userID"));
String userName = request.getParameter("userName");
String password = request.getParameter("password");
String showName = request.getParameter("showName");
boolean status = true;
User user = userBO.queryOneByID(userID);
user.setName(userName);
user.setPassword(password);
user.setShowName(showName);
user.setStatus(status);
userBO.update(user);
return null;
}
public ModelAndView queryAllUserList(HttpServletRequest request,
HttpServletResponse response) {
List<User> userList = userBO.queryAllForList();
for(User user : userList) {
System.out.println("user name: " + user.getName());
}
return null;
}
public void setUserBO(UserBO userBO) {
this.userBO = userBO;
}
}
或许这段代码存在很多冗余,存在许多不好的地方,但是它说明了一个基本的思路,该controller继承于MultiActionController,需要通过一个userBO来实现update,insert等操作。
存在至少两点值得思考的地方:其一,增删查改等各种方法是不是可以提取?否则其他业务controller是不是要再把类似的代码写一遍?其二,request里的parameters转化到pojo,是不是可以存在基于反射的机制自动实现?而不需要如此笨拙的一个属性一个属性的手工转化?
(2)基于DataAccessController,代码如下:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import com.wantall.application.mvc.po.User;
import com.wantall.framework.mvc.controller.DataAccessController;
public class UserController2 extends DataAccessController<User> {
/**
* 必须通过一种方式去设置commandClass成员属性,
* 在构造器中设置是最常规的
*/
public UserController2() {
this.setCommandClass(User.class);
}
public void test(HttpServletRequest request,
HttpServletResponse response, User user, BindException errors) {
//测试一级缓存,下面的第一个query从缓存读,第二个query从数据库读
/*dataAccessBO.insert(user);
dataAccessBO.queryOneByID(user.getId());
dataAccessBO.queryOneByID(new Integer(22));*/
//如果user对象脱管 不应该有缓存
dataAccessBO.test(user);
}
}
好了,以上代码实现了增删改查的基本功能。就这么多。为什么?下面从DataAccessController开始进行介绍。
在spring的MVC体系中,存在一个AbstractCommandController类,可通过它提供的机制,方便的实现request到pojo的转化。具体实现细节就不多说啦。如果希望用到该机制,就继承它。但是继承它之后,就不能享受MultiActionController带来的方便。怎么办呢?DataAccessController!代码如下:
import java.io.Serializable;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractCommandController;
import com.wantall.framework.mvc.bo.DataAccessBO;
import com.wantall.framework.mvc.po.interfaces.IBasePO;
/**
* 提供一个数据访问的简便controller入口
* 其实现过程需依赖于request——>pojo转换工具
* @author Administrator
*
*/
public class DataAccessController<T extends IBasePO> extends AbstractCommandController {
/**
* 类似于MultiActionController,该名称可配
*/
private String methodName = "method";
protected DataAccessBO<T> dataAccessBO;
/**
* 构造器:需要在继承的子类中去定义commandClass
*/
public DataAccessController() {
}
@Override
protected ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
String invokeMethodName = request.getParameter(methodName);
if(invokeMethodName == null) {
errors.reject("no method name provided.");
throw new Exception("no method name provided.");
}
try {
T pojo = (T) command;
Method[] methods = getClass().getMethods();
//实践发现,只有上面的方式才会将该类的继承体系中的方法全部获取
//Method method = getClass().getMethod(invokeMethodName, HttpServletRequest.class,
// HttpServletResponse.class, this.getCommandClass(), BindException.class);
for(Method method : methods) {
System.out.println("method : " + method.getName());
if(method.getName().equals(invokeMethodName)) {
method.invoke(this, request, response, pojo, errors);
return null;
}
}
throw new Exception("there is no method named " + invokeMethodName);
} catch (Exception e) {
errors.reject("the method invoke process has been catched exception.");
throw e;
}
}
public void insertCommand(HttpServletRequest request,
HttpServletResponse response, T pojo, BindException errors) {
dataAccessBO.insert(pojo);
}
public void deleteCommand(HttpServletRequest request,
HttpServletResponse response, T pojo, BindException errors) {
dataAccessBO.delete(pojo);
}
public void updateCommand(HttpServletRequest request,
HttpServletResponse response, T pojo, BindException errors) {
dataAccessBO.update(pojo);
}
public T queryOneCommand(HttpServletRequest request,
HttpServletResponse response, T pojo, BindException errors) {
Serializable pojoId = pojo.getId();
T result = dataAccessBO.queryOneByID(pojoId);
return null;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void setDataAccessBO(DataAccessBO<T> dataAccessBO) {
this.dataAccessBO = dataAccessBO;
}
}
对于该类有如下几点值得说明:
(1)数据库访问:很显然,里面包含了增删改查的相关方法,只要业务层级的controller继承该类,就可以享受到它带来的便利,而无需自己再去定义这些基本方法,如updateCommand方法。
(2)request-pojo转化:该类继承了AbstractCommandController,因此可以通过设置command来定义需要转化的类。比如UserController2中的
this.setCommandClass(User.class);
(3)方法映射:该类仅仅继承了AbstractCommandController,因此无法享受到MultiActionController提供的方便。仅仅存在一个handle方法,明显不够。因此,handle方法在该类中的作用是作为方法映射而用。观察该类的代码,可以发现,该类的实现效果追求于MultiActionController,通过在访问路径中加上method=***来实现方法映射。(当然了,该机制的实现的确不如MultiActionController那么严密和完善)。
(4)T extends IBasePO:在该类的开头,泛型区里可以看到T extends IBasePO,为什么要继承IBasePO?因为增删改查等各个操作,需要用到 pojo.getId();
等类似操作,如果不限定一个po基本类型,就无法指定这些通用的方法。IBasePO的代码如下:
import java.io.Serializable;
public interface IBasePO {
public Serializable getId();
public void setId(Serializable id);
}
因此,基本上j2eelib中和MVC相关的po类,都实现了IBasePO。
(5)DataAccessBO:此类型的实例是DataAccessController中的一个属性。通过该属性来构造MVC。从名字可以看出,该BO的用途是数据库访问,代码如下:
import java.io.Serializable;
import java.util.List;
import com.wantall.application.mvc.po.User;
import com.wantall.framework.mvc.dao.interfaces.IBaseDAO;
/**
* 提供一个瘦型数据访问基础BO,其功能即调用基础DAO
* 目的在于简化开发
* @author Administrator
*
*/
public abstract class DataAccessBO<T> {
protected IBaseDAO dataAccessDAO;
public int insert(T obj) {
return this.dataAccessDAO.insert(obj);
}
public int delete(Serializable id) {
return this.dataAccessDAO.delete(id);
}
public int delete(T obj) {
return this.dataAccessDAO.delete(obj);
}
public int update(T obj) {
return this.dataAccessDAO.update(obj);
}
public T queryOneByID(Serializable id) {
return (T) this.dataAccessDAO.queryOneByID(id);
}
public List<T> queryAllForList() {
return this.dataAccessDAO.queryAllForList();
}
public void setBaseDAO(IBaseDAO dataAccessDAO) {
this.dataAccessDAO = dataAccessDAO;
}
public void setDataAccessDAO(IBaseDAO dataAccessDAO) {
this.dataAccessDAO = dataAccessDAO;
}
public abstract void test(T obj);
}
里面存在一个dataAccessDAO属性。是IBaseDAO型的。还记得上一篇博文吗?在DAO模式中,IBaseDAO的作用和实现。
有了DataAccessBO,对于各种业务层级的BO,实现起来就相当容易:
import com.wantall.application.mvc.dao.interfaces.IUserDAO;
import com.wantall.application.mvc.po.User;
import com.wantall.framework.mvc.bo.DataAccessBO;
public class UserBO extends DataAccessBO<User> {
private IUserDAO userDAO;
public void setUserDAO(IUserDAO userDAO) {
this.userDAO = userDAO;
}
@Override
public void test(User user) {
userDAO.test(user);
}
}
就这么些,结束了。
有了DataAccessController和DataAccessBO,对于需要做数据库访问的业务层,帮助是相当大的,因为实现的代码量大大缩减。
剩下还有一个工作,就是spring的ioc配置。结合上一篇博文(DAO模式),这个在此就省略了。
本文就简单介绍到这里,里面一定会存在很多不完善的地方,大家有兴趣的话可以留言指出哦,j2eelib是一个纯技术的生态系统,需要不断开发,不断完善。
后续将继续介绍其相关模块,要不换换口味,下篇博文说说webservice吧~~