asp.net和j2ee的三层结构代码比较

1 篇文章 0 订阅

1 前言
j2ee曾提出多层结构的开发框架,但实际项目中三层结构仍旧是主流。一般划分为数据展现层、业务逻辑层、数据访问层。数据展现层只管数据的显示,完全不关心具体的业务逻辑;业务逻辑层负责业务逻辑处理,它位于数据展现层和数据访问层中间,其主要任务为调用数据访问层获取数据,以便交给展现层进行显示;数据访问层一般就负责访问数据库,如通过jdbc/odbc/ado.net等手段访问数据库。
日常的三层结构开发中,大部分精力耗费在CRUD(增删改查)操作上。笔者分别在j2ee和asp.net 2.0上进行了小试验,比较两者之间的开发特征。

2 j2ee和asp.net三层开发代码结构

2.1 asp.net 2.0平台
展现层:GridView / FormView / DetailView / DataList / Reapter, MasterPage以及自定义控件等asp服务器端控件对传统HTML标签进行了封装。
业务层:BLL(Business Login Layer),对CRUD操作进行封装,并采用ObjectDataStore封装要返回给展现层的数据对象。
数据访问层:DAL(Data Access Layer),采用ADO.net(SqlConnection或OracleConnection等)来访问数据库。

2.1.1 展现层代码
利用GridView,CRUD操作可以集中在一个asp页面中。EditItemTemplate对应修改;增加记录时,可以将输入表单显示到脚注FooterTemplate中;删除不需要界面;查询显示用ItemTemplate,这4个方面合成到一个TemplateField中,针对不同的操作显示相应的控件。
<asp:TemplateField>
    <ItemTemplate>
        <asp:Label ID="lblProductName" runat="Server"><%# Eval("ProductName") %></asp:Label>
    </ItemTemplate>
    <EditItemTemplate>
        <asp:TextBox ID="txtProductName" runat="Server" Text='<%# Bind("ProductName") %>'></asp:TextBox>
    </EditItemTemplate>
    <FooterTemplate>
        <asp:Button ID="btnInsert" Text="新增" OnClick="btnInsert_Click" runat="server" />
        <asp:Button ID="btnCancel" Text="取消" OnClick="btnCancel_Click" runat="server" />
        <asp:TextBox ID="txtProductID" runat="Server" Text=''></asp:TextBox>
        <asp:TextBox ID="txtProductName" runat="Server" Text=''></asp:TextBox>
    </FooterTemplate>
</asp:TemplateField>

2.1.2 业务层代码
实现业务逻辑(比如参数值校验),并调用DAL对象提供的CRUD操作,比如GetProducts(),UpdateProduct(),DeleteProduct(), InsertProduct().
public SqlDataReader GetProducts();
public int UpdateProduct(int productID, string productName, double unitPrice, Int16 unitsInStock);
public int DeleteProduct(int productID);
public int InsertProduct(int ProductID,
        string ProductName,
        int SupplierID,
        int CategoryID,
        string QuantityPerUnit,
        double UnitPrice,
        int UnitsInStock,
        int UnitsOnOrder,
        int ReorderLevel,
        int Discontinued);
当然还有另外一种asp.net接受的函数声明方法,是将增删改的参数统一改成一个Product对象。
public SqlDataReader GetProducts();
public int UpdateProduct(Product prod);
public int DeleteProduct(Product prod);
public int InsertProduct(Product Prod);

2.1.3 数据访问层
调用ADO.net访问数据库(利用SqlConnection, SqlCommand, SqlDataReader等对象),注意不同数据库Connection的写法有不同。比如OracleConnection的参数绑定上与SqlConnection有区别。
// 返回DataSet或DataReader或者泛型列表List<Product>
public SqlDataReader GetProducts()
{
    SqlConnection conn = new SqlConnection(m_ConnStr);
    string sql = "select * from CatProd";
    SqlCommand cmd = new SqlCommand(sql, conn);

    conn.Open();
    SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
   
    return reader;
}

public int UpdateProduct(int productID, string productName, double unitPrice, Int16 unitsInStock)
{
    int cnt = 0;
    SqlConnection conn = new SqlConnection(m_ConnStr);
    string sql = @"update CatProd set ProductName = @ProductName, UnitPrice = @unitPrice, UnitsInStock = @unitsInStock
            where ProductID = @ProductID";
    SqlCommand cmd = new SqlCommand(sql, conn);
   
    cmd.Parameters.AddWithValue("@ProductID", productID);
    cmd.Parameters.AddWithValue("@ProductName", productName);
    cmd.Parameters.AddWithValue("@unitPrice", unitPrice);
    cmd.Parameters.AddWithValue("@unitsInStock", unitsInStock);

    conn.Open();
    cnt = cmd.ExecuteNonQuery();
    conn.Close();

    return cnt;
}

2.2 j2ee平台
展现层:struts框架显示数据。
业务层:Service/Manager等业务封装对象。(可选用Spring,EJB等框架)
数据访问层:DAO模式通过jdbc(或jdbc的轻量级封装工具Hibernate,ibatis,SpringJDBC等)访问底层数据库。

2.2.1 展现层代码
采用struts框架,MVC模式简化了展现层的代码,几乎完全消除了以往jsp代码中的嵌入式java代码。在jsp页面中采用了标签库
<%@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
<%@taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
<%@taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%>

一个典型的jsp列表页面(比如listbook.jsp书籍清单)为
<table align="left" border="1" width="70%">
    <tr bgcolor="#COCOCO">
        <td align="center">
            <bean:message key="book.id" />
        </td>
        <td align="center">
            <bean:message key="book.name" />
        </td>
        <td align="center">
            <bean:message key="book.author" />
        </td>
        <td align="center">
            <bean:message key="book.publish" />
        </td>
        <td align="center">
            <bean:message key="book.price" />
        </td>
        <td align="center">
            <bean:message key="book.operator" />
        </td>
    </tr>
    <logic:iterate id="book" name="books" scope="session">
        <tr>
            <td width="10%">
                <bean:write name="book" property="bookId" />
            </td>
            <td width="25%">
                <bean:write name="book" property="bookName" />
            </td>
            <td width="10%">
                <bean:write name="book" property="author" />
            </td>
            <td width="25%">
                <bean:write name="book" property="publish" />
            </td>
            <td width="10">
                <bean:write name="book" property="price" />
            </td>
            <td width="25%">
                <a
                    href="operatorAction.do?operator=showModify&bookid=<bean:write name="book" property="bookId"/>">
                    <bean:message key="link.modify" /> </a> &nbsp;&nbsp;
                <a
                    href="operatorAction.do?operator=showDelete&bookid=<bean:write name="book" property="bookId"/>">
                    <bean:message key="link.delete" /> </a>
            </td>
            <bean:define id="bookid" name="book" property="bookId" />
        </tr>
    </logic:iterate>
</table>

上述用bean:message,并将字符串保存到ApplicationResources.properties文件中,根据locale的不同,可以实现国际化的要求(比如一键切换中英文版本)

在action处理代码中,实现execute方法,调用业务逻辑层的代码,取得值对象(VO, Value Object)的列表,保存到jsp内建对象(如request或session)中后,进行页面跳转。
public ActionForward execute(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) {
    List books = null;

    BookDAO jdbc = new BookJdbcDAO();
    books = jdbc.findAllBooks();

    // request.setAttribute("books", books);
    request.getSession().setAttribute("books", books);

    return mapping.findForward("success");
}

2.2.2 业务层代码
业务层常见的方式是定义Service或Manger的接口以及相关实现类,比如UserService(UserManager, IUserService)接口,并提供对应实现类UserServiceImpl(UserManagerImpl, UserService),一般这些实现类都是直接调用相应的DAO中的方法。
public class UserServiceImpl implements UserService {
    private UserDAO userDAO = null;
   
    public void setUserDAO(UserDAO dao) {
        // TODO Auto-generated method stub
        this.userDAO = dao;
    }

    public int insert(User user) {
        // TODO Auto-generated method stub
        return userDAO.insert(user);
    }

    public int update(User user) {
        // TODO Auto-generated method stub
        return userDAO.update(user);
    }

    public int delete(User user) {
        // TODO Auto-generated method stub
        return userDAO.delete(user);
    }

    public int[] batchInsert(List users) {
        // TODO Auto-generated method stub
        return userDAO.batchInsert(users);
    }

    public User findById(int id) {
        // TODO Auto-generated method stub
        return userDAO.findById(id);
    }

    public List findByName(String name) {
        // TODO Auto-generated method stub
        return userDAO.findByName(name);
    }
}

2.2.3 数据访问层
具体访问数据库,提供CRUD的方法,一般可以借助于SpringJDBC,Hibernate / iBatis等工具来简化编写数据库访问的代码。下面给出以SpringJDBC为数据访问方式的代码。
public class UserDAOImpl implements UserDAO {
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemp) {
        this.jdbcTemplate = jdbcTemp;
    }

    public int insert(User user) {
        String sql = "insert into user(id, name, password) values(null, ?, ?)";
        Object[] values = new Object[] { user.getName(), user.getPassword() };

        return jdbcTemplate.update(sql, values);
    }

    public int update(User user) {
        String sql = "update user set name = ?, password = ? where id = ?";
        Object[] values = new Object[] { user.getName(), user.getPassword(),
                new Integer(user.getId()) };

        return jdbcTemplate.update(sql, values);
    }

    public int[] batchInsert(final List users) {
        String sql = "insert into user(id, name, password) values(null, ?, ?)";

        BatchPreparedStatementSetter setter = new BatchPreparedStatementSetter() {
            public int getBatchSize() {
                return users.size();
            }

            public void setValues(PreparedStatement pstmt, int index)
                    throws SQLException {
                User user = (User) users.get(index);

                pstmt.setString(1, user.getName());
                pstmt.setString(2, user.getPassword());
            }
        };

        return jdbcTemplate.batchUpdate(sql, setter);
    }

    public int delete(User user) {
        String sql = "delete from user where id = ?";
        Object[] values = new Object[] { new Integer(user.getId()) };

        return jdbcTemplate.update(sql, values);
    }

    public User findById(int id) {
        String sql = "select * from user where id = ?";
        Object[] values = new Object[] { new Integer(id) };

        final User user = new User();

        jdbcTemplate.query(sql, values, new RowCallbackHandler() {
            public void processRow(ResultSet rs) throws SQLException {
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setPassword(rs.getString("password"));
            }
        });

        return user;
    }

    public List findByName(String name) {
        String sql = "select * from user where name = ?";
        Object[] values = new Object[] { name };

        //final List users = new ArrayList();
        return (List) jdbcTemplate.query(sql, values, new ResultSetExtractor() {
            public Object extractData(ResultSet rs) throws SQLException,
                    DataAccessException {
                List users = new ArrayList();
                while (rs.next()) {
                    User user = new User();
                    user.setId(rs.getInt("id"));
                    user.setName(rs.getString("name"));
                    user.setPassword(rs.getString("password"));

                    users.add(user);
                }
                return users;
            }
        });

        //return users;
    }
}

3 总结
3.1 粗浅的比较
在数据展现层方面,asp.net实现了代码和HTML标签元素的彻底分离(比如filename.aspx, filename.aspx.cs),通过回发(postback)对鼠标、键盘等事件进行处理。配套VS2005的可视化编辑功能,代码编写的工作量较小。而这方面J2EE平台上略嫌不足,Struts的配置以及jsp页面上的标签代码编写稍显繁琐(不知JSF对展现层的代码简化程度如何?)。

在业务逻辑方面,asp.net隔离为BLL层,编写业务逻辑控制的相关代码。在J2EE平台上,产生了各种业务逻辑层的开发框架,比如Spring的容器,管理各种业务逻辑对象的生命周期,使得程序员可以更加专注于业务逻辑,同时增加了代码注入和对象组装和灵活配置的能力(AOP,代码反转注入等)

在数据访问层,asp.net采用ADO.net的方式,对SQLServer支持很好,通过System.data.OracleClient亦对Oracle进行了支持。J2EE可以采用jdbc或各种jdbc的封装框架(SpringJdbc, Hibernate/iBatis等),可选择的范围很广,故可根据项目需要进行搭配。

两者之间的差别,很大程度上是由于一个是商业软件,一个是开源社区推动型的两大阵营的差异造成的。从asp.net的三层结构开发的设计思想来看,应该是吸取并借鉴了很多开源框架的优秀设计思想,但总体来说,开源社区更具有活力。

3.2 其它需考虑的问题
关于事务的处理:即将事务控制放在数据访问层还是业务逻辑层?取决于业务的复杂程度,若分布式跨越多个数据库,则在J2EE平台上用JTA,因为JDBC的事务控制局限于当前连接,而一个连接只属于一个数据库。

层之间的接口以及数据传输:ORM映射后,对象和关系(数据库表)建立起了对应关系,j2ee平台用值对象(VO, PO, javabean)来实现数据封装和层之间的传递,asp.net上通常用DataSet,当然也可以用值对象来传输。

如何基于项目需求来选择合适的开发框架:项目的规模,需求特征(如增删改操作和查询操作分别所占的比例),是否数据密集型(使用普通SQL语句或调用存储过程的决定),框架的开发成本和人力成本等各种因素的考虑。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值