论坛首页 Java企业应用论坛

关于WEB应用的分层和异常处理的问题

浏览 7471 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-10-31  
DAO
先说说我现在用的方式:
数据层自己写,MVC用webwork
每个数据表对应一个DTO,一个VO,一个DAO、一个Manager(Service)和一个Action
其中DTO是对应于数据表的结构的
VO是继承了DTO,并且加入了一些其他的属性,比如和这个表关联的其他表的VO。
DAO层的类继承BaseDAO,BaseDAO包含了一些基本才作,比如执行更新的sql,执行查询的sql,执行查询记数的sql等等。
DAO、DTO和VO都是用自己写的一个代码生成工具生成基本代码,DAO主要生成了插入、删除、修改和查询的方法。
Service层也生成了基本的代码,也是插入删除修改和查询,直接调用DAO的方法。
Action层根据业务不同分别生生成一些代码,有些可能没用。比如基本的有toAddPage,toEditPage,toListPage,performAddAction,performEditAction,performDeleteAction等等。Action层直接调用Service层的方法。
由于为了简化开发提高效率,所以这里的DAO和Service都没用接口。


现在有几个问题不知道如何处理合适:
1、现在的Service层因为业务逻辑比较简单,所以很多方法都是直接调用dao层的一个方法就完成了,所以我感觉和直接调用DAO差不多,觉得挺浪费一样,但如果不加Service层,很多稍复杂一些的业务必须得用几个DAO才能完成。这个地方如何才能平衡一下?

2、异常处理部分我可能没有处理好,现在的方式是,在DAO层遇到异常以后没有抛出,而是直接在BaseDAO里面加了一个errorMessage的属性,每次有异常就把异常信息set进去,然后通过函数的返回值知道这个函数发生了异常,在Service层也是一样的处理方式,感觉这种处理方式很不好,想了一个方法不知道合理不合理:在DAO层直接抛出一个自己定义的DataAccessException,在Service层调用DAO的方法时候补获这个异常以后,再转换成自己定义BusinessException,再抛出,然后在Action里面判断如果出现异常,则调用setActionError把异常信息填入,再转向统一的一个出错页面输出。这样做是否合理?

3、处理数据表关联问题,很多时候需要联合查询两个甚至多个表,这个时候我的做法是把select出来的数据集合先存在一个Hashtable里面,再根据某种字段名和VO的属性的转换关系把这个Hashtable转换成一个或者多个VO,同时每个VO为了DAO的统一处理方便,都要实现IVO里面的parse方法,参数是DataRow,DataRow也是自己定义的工具类,提供了Result和DataRow之间的转换还有DataRow和VO之间的转换。这样,如果我两个表之间有关联需要获取关联的数据,我就可以再parse里面把关联表的那个VO的各个属性也都根据DataRow里面的数据Set进去,这样就可以解决关联的问题。

可能我说的挺乱的,还是看代码吧:
DataRow代码
package com.bang.adhibit.util;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.*;
import java.util.HashMap;


public class DataRow {
	private HashMap row = new HashMap();
	public Object get(String key)
	{
		return row.get(key);
	}
	
	public void put(String key,Object obj)
	{
		row.put(key,obj);
	}
	
	public int getInt(String key)
	{
		Integer i = (Integer)row.get(key);
		if(i != null)
			return i.intValue();
		return 0;
	}
	
	public long getLong(String key)
	{
		Long l = (Long)row.get(key);
		return (l!=null)?l.longValue():0;
	}
	
	public String getString(String key)
	{
		return (String)row.get(key);
	}
	
	public Timestamp getTimestamp(String key)
	{
		return (Timestamp)row.get(key);
	}
	
	public Date getDate(String key)
	{
		return (Date)row.get(key);
	}
	
	public static DataRow parse(ResultSet rs) throws SQLException
	{
		DataRow row = new DataRow();		
    	ResultSetMetaData rsmd = rs.getMetaData();
    	int count = rsmd.getColumnCount(); 
        for(int i=1;i<=count;i++)
        {
            row.put(rsmd.getColumnName(i).toLowerCase(),rs.getObject(i));
        }
        return row;
	}
	//定义转换关系,比如user_name字段在DTO里面的属性就是userName
	private String convert(String name)
	{
		String result = "";
		for(int i=0;i<name.length();i++)
		{
			char c = name.charAt(i);
			if(c >='A' && c <='Z')
				result += "_" + c;
			else
				result += c;
		}
		return result.toLowerCase();
	}
	
	public Object toObject(Class c)
	{
		Object obj = null;		
		try {			
			obj = c.newInstance();
			Field[] f  = c.getDeclaredFields();
			BeanInfo info = Introspector.getBeanInfo(c);
			PropertyDescriptor[] pds = info.getPropertyDescriptors();
			for(int i=0;i<pds.length;i++)
			{				
				String name = pds[i].getName();
				String key = convert(name);
				Object value = get(key);
				if(value == null)
					continue;
				Method method = pds[i].getWriteMethod();				
				if(method != null)
				{
					try
					{
						String type = method.getParameterTypes()[0].getName();
						//System.out.println("@"+type+";value="+value.getClass().getName());
						if(type.equals("int"))
						{
							if(value instanceof BigDecimal)
							{
								BigDecimal bd = (BigDecimal)value;
								method.invoke(obj,new Object[]{new Integer(bd.intValue())});
							}
							if(value instanceof Long)
							{
								Long l = (Long)value;
								method.invoke(obj,new Object[]{new Integer(l.intValue())});
							}
							if(value instanceof Integer)
							{
								Long l = (Long)value;
								method.invoke(obj,new Object[]{(Integer)value});
							}
							
							
						}
						if(type.equals("long"))
						{
							if(value instanceof BigDecimal)
							{
								BigDecimal bd = (BigDecimal)value;
								method.invoke(obj,new Object[]{new Long(bd.longValue())});
							}
							if(value instanceof Integer)
							{
								Integer intt = (Integer)value;
								method.invoke(obj,new Object[]{new Long(intt.longValue())});
							}
							if(value instanceof Long)
							{
								Long l = (Long)value;
								method.invoke(obj,new Object[]{(Long)value});
							}
						}
						if(type.equals("float"))
						{
							method.invoke(obj,new Object[]{(Float)value});
						}
						if(type.endsWith("Timstamp"))
							method.invoke(obj,new Object[]{(Timestamp)value});
						
						
						if(type.endsWith("Date")){
							method.invoke(obj,new Object[]{new Date(((Timestamp)value).getTime())});
						}
						if(type.equals("double"))
						{
							method.invoke(obj,new Object[]{(Double)value});
						}
						if(type.endsWith("String"))
							method.invoke(obj,new Object[]{(String)value});
						
					}catch(Exception e)
					{
						e.printStackTrace();
					}
				}
			}
	
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return obj;
	}	
}



写不下了,其他见回复
   发表时间:2006-10-31  
DTO的代码:
package com.bang.adhibit.data;

import java.sql.*;
import java.util.HashMap;


/**
 * 
 * <p>Title: perfect_mobile_type表的数据对象类</p> 
 * 
 * <p>Description: 保存PerfectMobileType各个字段</p> 
 * 
 * <p>Copyright: Copyright (c) 2006</p> 
 * 
 * <p>Company: 邦邦网</p>
 * 
 * @author not attributable
 * @version 1.0
 */
public class PerfectMobileType
{

    private long typeId;    //型号ID
    private long mobileId;    //品牌ID
    private String typeName;    //型号名称(序列)
    private int width;    //宽度
    private int height;    //高度
    private int sortIndex;    //排列顺序
    private int enableFlag;    //显示标记
    private int state;    //状态

    //型号ID
    public void setTypeId(long typeId)
    {
    	this.typeId = typeId;
    }
    //品牌ID
    public void setMobileId(long mobileId)
    {
    	this.mobileId = mobileId;
    }
    //型号名称(序列)
    public void setTypeName(String typeName)
    {
        if(typeName == null)
            typeName = "";
    	this.typeName = typeName;
    }
    //宽度
    public void setWidth(int width)
    {
    	this.width = width;
    }
    //高度
    public void setHeight(int height)
    {
    	this.height = height;
    }
    //排列顺序
    public void setSortIndex(int sortIndex)
    {
    	this.sortIndex = sortIndex;
    }
    //显示标记
    public void setEnableFlag(int enableFlag)
    {
    	this.enableFlag = enableFlag;
    }
    //状态
    public void setState(int state)
    {
    	this.state = state;
    }


    public long getTypeId(){ return this.typeId; }    
    public long getMobileId(){ return this.mobileId; }    
    public String getTypeName(){ return this.typeName; }    
    public int getWidth(){ return this.width; }    
    public int getHeight(){ return this.height; }    
    public int getSortIndex(){ return this.sortIndex; }    
    public int getEnableFlag(){ return this.enableFlag; }    
    public int getState(){ return this.state; }    


    public String toString()
    {
        String retStr = "The value list of PerfectMobileTypeDTO is:\r\n";
        retStr += ("[(型号ID)typeId] = " + typeId + "\r\n");
        retStr += ("[(品牌ID)mobileId] = " + mobileId + "\r\n");
        retStr += ("[(型号名称(序列))typeName] = " + typeName + "\r\n");
        retStr += ("[(宽度)width] = " + width + "\r\n");
        retStr += ("[(高度)height] = " + height + "\r\n");
        retStr += ("[(排列顺序)sortIndex] = " + sortIndex + "\r\n");
        retStr += ("[(显示标记)enableFlag] = " + enableFlag + "\r\n");
        retStr += ("[(状态)state] = " + state + "\r\n");
        return retStr;
    }



//start your code here

//finish your code here


}//end of file


VO的代码:

package com.bang.adhibit.data;

import com.bang.adhibit.util.*;

/**
 * 
 * <p>Title: Value Object</p> 
 * 
 * <p>Description: </p> 
 * 
 * <p>Copyright: Copyright (c) 2006</p> 
 * 
 * <p>Company: 邦邦网</p>
 * 
 * @author not attributable
 * @version 1.0
 */
public class PerfectMobileTypeVO
     extends PerfectMobileType
     implements IVO
{


//start your code here

	public Object parse(DataRow dr)
    {
        PerfectMobileTypeVO vo = (PerfectMobileTypeVO)dr.toObject(PerfectMobileTypeVO.class);
        //这里是关联了perfect_mobile表
        PerfectMobileVO pm = (PerfectMobileVO)dr.toObject(PerfectMobileVO.class);
        vo.setPerfectMobile(pm);       
        return vo;
    }

    private PerfectMobileVO perfectMobile;

	public PerfectMobileVO getPerfectMobile() {
		return perfectMobile;
	}


	public void setPerfectMobile(PerfectMobileVO perfectMobile) {
		this.perfectMobile = perfectMobile;
	}
    

//finish your code here


}//end of file


BaseDAO的代码:
package com.bang.adhibit.dao;

import java.sql.*;
import java.util.*;

import com.apan.util.DB;
import com.bang.adhibit.data.IVO;
import com.bang.adhibit.util.DataRow;

public class BaseDAO {
	//出错信息
	private String errorMessage;
	protected void setErrorMessage(String msg)
	{
		errorMessage = msg;
	}
	
	public String getErrorMessage()
	{
		return errorMessage;
	}
	//获得一个序列的next值
	protected long getSeq(String seqName)
	{
		long result = 0;
		DB db = null;
		try {
			db = new DB();
			ResultSet rs = db.getStat().executeQuery("select " + seqName + ".nextval from dual");
			if(rs.next())
				result = rs.getLong(1);
			rs.close();
		} catch (Exception e) {
			e.printStackTrace();
			setErrorMessage(e.toString());
		}finally{
			if(db != null)
				db.close();
		}
		return result;
	}
	//执行非查询语句
	protected boolean executeNonQuery(String strSql,Object[] param)
	{
			DB db = null;
			try {
				db = new DB();
				PreparedStatement pstmt = db.getConn().prepareStatement(strSql);
				if(param != null)
				{
					for(int i=0;i<param.length;i++)
						pstmt.setObject(i+1,param[i]);
				}
				pstmt.executeUpdate();
			} catch (Exception e) {
				e.printStackTrace();
				setErrorMessage(e.toString());
				return false;
			}finally{
				if(db != null)
					db.close();
			}
			return true;
	}
	//执行查询语句
	protected List executeQuery(String strSql,Object[] param,Class cls)
	{
		DB db = null;
		ArrayList list = new ArrayList();
		
		try {
			IVO vo = (IVO)cls.newInstance();
			db = new DB();
			PreparedStatement pstmt = db.getConn().prepareStatement(strSql);
			if(param != null)
			{
				for(int i=0;i<param.length;i++)
					pstmt.setObject(i+1,param[i]);
			}
			ResultSet rs = pstmt.executeQuery();
			while(rs.next())
			{
				DataRow dr = DataRow.parse(rs);
				Object obj = vo.parse(dr);
				list.add(obj);
			}
		} catch (Exception e) {
			setErrorMessage(e.toString());
			e.printStackTrace();
		}finally{
			if(db != null)
				db.close();
		}
		return list;		
	}
	//获取记数的值,比如需要统计之类的时候select count(*) from ....
	protected int getNumberField(String strSql,Object[] param)
	{
		DB db = null;
		int result = -1;
		try {
			db = new DB();
			PreparedStatement pstmt = db.getConn().prepareStatement(strSql);
			if(param != null)
			{
				for(int i=0;i<param.length;i++)
					pstmt.setObject(i+1,param[i]);
			}
			ResultSet rs = pstmt.executeQuery();
			while(rs.next())
			{
				result = rs.getInt(1);
			}
		} catch (Exception e) {
			setErrorMessage(e.toString());
			e.printStackTrace();
		}finally{
			if(db != null)
				db.close();
		}
		return result;			
	}
}


DAO类的代码:
package com.bang.adhibit.dao;

import com.bang.adhibit.util.*;
import com.bang.adhibit.data.*;
import java.sql.*;
import java.util.*;

/**
 * 
 * <p>Title: 访问perfect_mobile_type数据访问类</p> 
 * 
 * <p>Description: DAO类,包含了数据库的各种操作</p> 
 * 
 * <p>Copyright: Copyright (c) 2006</p> 
 * 
 * <p>Company: 邦邦网</p>
 * 
 * @author not attributable
 * @version 1.0
 */

public class PerfectMobileTypeDAO
    extends BaseDAO
{
    
    /**
     * 向数据库插入一条记录
     * @param dto PerfectMobileTypeDTO 要插入的数据
     * @return PerfectMobileType 返回插入的记录
     */
    public PerfectMobileType insert(PerfectMobileType perfectMobileType)
    {
        long seq = getSeq("perfect_mobile_type_seq");
        perfectMobileType.setTypeId(seq);
        String strSql = "Insert into perfect_mobile_type (";
        strSql += "type_id, mobile_id, type_name, width, height, sort_index, enable_flag, state";
        strSql += ") values (?, ?, ?, ?, ?, ?, ?, ?)";
	
        Object[] param = new Object[8];
        param[0] = new Long(seq);
        param[1] = new Long(perfectMobileType.getMobileId());
        param[2] = perfectMobileType.getTypeName();
        param[3] = new Integer(perfectMobileType.getWidth());
        param[4] = new Integer(perfectMobileType.getHeight());
        param[5] = new Integer(perfectMobileType.getSortIndex());
        param[6] = new Integer(perfectMobileType.getEnableFlag());
        param[7] = new Integer(perfectMobileType.getState());
        boolean result = executeNonQuery(strSql,param);
        if(!result)
            return null;
        else
            return perfectMobileType;
    }



    /**
     * 编辑一条记录
     * @param dto PerfectMobileTypeDTO 要编辑的数据
     * @return boolean 返回编辑是否成功,如果失败,错误信息可用getErrorMessage方法获得
     */
    public boolean edit(PerfectMobileType perfectMobileType)
    {

        String strSql = "update perfect_mobile_type set ";
        strSql += "mobile_id=?, type_name=?, width=?, height=?, sort_index=?, enable_flag=?, state=?";
        strSql += " where type_id=? ";
        Object[] param = new Object[8];
        param[0] = new Long(perfectMobileType.getMobileId());
        param[1] = perfectMobileType.getTypeName();
        param[2] = new Integer(perfectMobileType.getWidth());
        param[3] = new Integer(perfectMobileType.getHeight());
        param[4] = new Integer(perfectMobileType.getSortIndex());
        param[5] = new Integer(perfectMobileType.getEnableFlag());
        param[6] = new Integer(perfectMobileType.getState());

        param[7] = new Long(perfectMobileType.getTypeId());
        return executeNonQuery(strSql,param);
    }

    /**
     * 根据主键查询一条记录
     * @param ... 主键的值列表
     * @return PerfectMobileTypeVO 返回查询内容,如果返回null表示记录不存在或者出错。
     */    
    public PerfectMobileTypeVO findByPrimaryKey(long typeId)
    {
        PerfectMobileTypeVO vo = null;
        String strSql = "select a.* from " + innerStr + " where  type_id=? ";
        Object[] param = new Object[1];
        param[0] = new Long(typeId);
        List list = executeQuery(strSql,param,PerfectMobileTypeVO.class);
        if(list != null && list.size()==1)
        {
            vo = (PerfectMobileTypeVO)list.get(0);
        }
        return vo;
    }

    /**
     * 根据主键删除一条记录
     * @param ... 主键的值列表
     * @return boolean 返回删除是否成功
     */
    public boolean deleteByPrimaryKey(long typeId)
    {
        String strSql = "delete from perfect_mobile_type where type_id=? ";
        Object[] param = new Object[1];
        param[0] = new Long(typeId);
        return executeNonQuery(strSql,param);
    }

    /**
    * 查询数据
    * @param strSql sql语句
    * @param param 预处理参数
    * @return 查询结果列表
    */
    public List query(String strSql,Object[] param)
    {
        return executeQuery(strSql,param,PerfectMobileTypeVO.class);
    }
    
 


//start your code here
    
    private String innerStr = "perfect_mobile_type a inner join perfect_mobile b on a.mobile_id=b.mobile_id";
    
    /**
     * 根据mobileId获取手机型号列表
     * @param mobileId 品牌ID
     * @return
     */
    public List getMobileTypeList(long mobileId)
    {
    	String strSql = "select a.*,b.mobile_name from " + innerStr + " where a.mobile_id=? order by a.sort_index";
    	return query(strSql,new Object[]{new Long(mobileId)});
    }
    
    
    /**
     * 设置有效标志 
     * @param typeId 手机型号ID
     * @param flag 有效标志  0-无效 1-有效
     * @return
     */
    public boolean enable(long typeId,int flag)
    {
    	String strSql = "update perfect_mobile_type set enable_flag=? where type_id=?";
    	return this.executeNonQuery(strSql,new Object[]{new Integer(flag),new Long(typeId)});
    }
    
    public List getScreenList()
    {
    	String strSql = "select width,height from perfect_mobile_type group by width,height";
    	List list = executeQuery(strSql,null,MobileScreen.class);
    	return list;
    }

//finish your code here


}//end of file


以上就是现在我采用的数据层代码的处理方式,但是这种方式当两个关联表中有同明字段而且都要使用的时候,就会有问题了,因为同一个Hashtable的key是唯一的,这样肯定就有一个表中的某个字段丢失了。我想请教各位在做数据层的时候,是如何处理表关联的问题的呢?有没有一种简便的方式来处理数据层的问题呢?


我还是菜鸟,不过在这个论坛上收获很多,还请各位不要见笑,请多多指教,谢谢!
0 请登录后投票
   发表时间:2006-11-01  
天, 这么长的代码, 有几个能够耐心看完的?
0 请登录后投票
   发表时间:2006-11-01  
把接口放上来看看(要写注释)
0 请登录后投票
   发表时间:2006-11-21  
和我现在公司的代码比好很多很多,(5000行的java类大家见过没有,几乎没有小于2000行的java类,同一个方法每个类里都有定义,所有的类都独立,不依赖任何类,所有业务都在action里)最起码总的思路是正确的
但感觉具体实现上有些乱,需要重构
0 请登录后投票
   发表时间:2006-11-21  
这是我的个人意见:
(1)你对jdbc的封装有点像spring的jdbc模块,不过多了一个直接返回Object的功能。对于数据库的异常处理,其实可以仿照Spring的jdbc模块,抛出一个unchecked的DataAccessException
(2)DataAccessException不属于BusinessException,没有必要重新包装。
0 请登录后投票
   发表时间:2006-11-28  
唉,以前没有看过Spring的JDBC模块,今天看到楼上的回复,找了些资料看了一下,真的很方便,才知道自己做了这么多的无用功,人家做好的东西自己还在拼命想,浪费好多时间.以后准备把框架往Spring上转了...多谢各位!!
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics