在web开发中,很大一部分时间都是直接和数据库打交道,除非和别的公司合作,需要通过接口来访问或操作数据。因此,任何一家基于web开发的公司,其框架基本上包含其特有的MVC体系,数据库访问框架设计等。本文是笔者个人对这方面的一个小小的思考结果。

    先照例展现包结构:

    图中圈出一个DataAccessController类,笔者认为该类的设计是一个核心,各种业务层级的controller,如果需要基于这种模式做数据库访问,可以继承它。该类稍后介绍,先看一下如果对user信息做数据库访问操作,用与不用DataAccessController的差别。

      (1)不用DataAccessController,代码如下:

 

package com.wantall.application.mvc.controller;

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,代码如下:

 

package com.wantall.application.mvc.controller;

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!代码如下:

package com.wantall.framework.mvc.controller;

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的代码如下:

 

package com.wantall.framework.mvc.po.interfaces;

import java.io.Serializable;

public interface IBasePO {
    public Serializable getId();
    
    public void setId(Serializable id);
}

因此,基本上j2eelib中和MVC相关的po类,都实现了IBasePO。

     (5)DataAccessBO:此类型的实例是DataAccessController中的一个属性。通过该属性来构造MVC。从名字可以看出,该BO的用途是数据库访问,代码如下:

 

package com.wantall.framework.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,实现起来就相当容易:

 

package com.wantall.application.mvc.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吧~~

posted on 2012-10-05 14:45  王小涛  阅读(1695)  评论(1编辑  收藏  举报