首    页 建模架构 设计模式 培训咨询 jdon框架 论坛

上一级 首页 下一级


 J2EE应用系统JPetstore

IBatis.comiBATIS-Jpetstore为例,我们使用Jdon框架对其重构成为Jdon-JPetstore,本章开发环境是Eclipse(本章案也适用其他开发工具),部署运行环境是JBoss

Eclipse是一个非常不错的开源开发工具,使用Eclipse开发和使用JBuilder将有完全不同的开发方式。我们使用Eclipse基于Jdon框架开发一个完全Web应用,或者可以说,开发一个轻量(lightweight)的J2EE应用系统。

通过这个轻量系统开发,说明Jdon框架对完全POJO架构的支持,因为EJB分布式集群计算能力,随着访问量提升,可能需要引入EJB架构,这时只要使用EJB session Bean包装POJO服务则可以无缝升级到EJB。使用Jdon框架可实现方便简单地架构升迁。

Eclipse安装简要说明

1.下载Eclipse:http://www.eclipse.org 的下载点中选择tds ISP 比较快。

2.安装免费插件:

编辑Jsp需要lomboz http://www.objectlearn.com/projects/download.jsp

注意对应的Eclipse版本。

编辑XML,使用Xmlbuddy: http://xmlbuddy.com/

基本上这两个插件就够了。

 

如果希望开发 Hibernate,插件:

http://www.binamics.com/hibernatesync

 

代码折叠

http://www.coffee-bytes.com/eclipse/update-site/site.xml

 

3. 关键学习ant的编写build.xml,在build.xml将你的jsp javaclass打包成warjarear就可以。都可以使用antjar打包,build.xml只要参考一个模板就可以:SimpleJdonFrameworkTest.rar 有一个现成的,可拷贝到其它项目后修改后就可用.

然后在这个模板上修改,参考 ant的命令参考:

http://ant.apache.org/manual/tasksoverview.html

网上有中文版的ant参考,在google搜索就能找到。

关键是学习antbuild.xml编辑,SimpleJdonFrameworkTest.rar 有一个现成的,可拷贝到其它项目后修改后就可用.

ant编译替代Eclipse的缺省编译:选择项目属性-->Builders ---> new --> Ant Builder --->选择本项目的build.xml workspace 选择本项目

eclipse开发就这些,非常简单,不象Jbuilder那样智能化,导致项目目录很大。eclipse只负责源码开发,其它都由ant负责

架构设计要点

Jdon-JPetstore除了保留iBATIS-JPetstore 4.0.5的域模型、持久层ibatis实现以及Jsp页面外,其余部分因为使用了Jdon框架而和其有所不同。

保留域模型和Jsp页面主要是在不更改系统需求的前提下,重构其架构实现为Jdon框架,通过对比其原来的实现或SpringJPetstore实现,可以发现Jdon框架的使用特点。

在原来jpetstore iBatis包会延伸到表现层,例如它的分页查询PaginatedListiBatis只是持久层框架,它的作用范围应该只限定在持久层,这是它的专业范围,如果超过范围,显得 ….。所以,在Jdon-Jpetstore中将iBatis封装在持久层(砍掉PaginatedList这只太长的手),Jdon框架是一种中间层框架,联系前后台的工作应该由Jdon这样的中间层框架完成。

iBatis 4.0.5版本中,它使用了一个利用方法映射Reflection的小框架,这样,将原来需要在Action实现方法整入了ActionForm中实现,ActionForm变成了一个复杂的对象:页面表单抽象以及与后台Service交互,在ActionForm中调用后台服务是通过单态模式实现,这是在一般J2EE开发中忌讳的一点,关于单态模式的讨论可见:http://www.jdon.com/jive/article.jsp?forum=91&thread=17578

Jdon框架使用了微容器替代单态,消除了Jpetstore的单态隐患,而且也简化了ActionForm和服务层的交互动作(通过配置实现)。

用户注册登陆模块实现

用户域建模(Model

首先,我们需要从域建模开始,建立正确的领域模型,以用户账号为例,根据业务需求我们确立用户账号的域模型Account,该模型需要继承Jdon框架中的com.jdon.controller.model.Model

 

public class Account extends Model {

 

  private String username;

  private String password;

  private String email;

  private String firstName;

  private String lastName;

  ……

 

}

username是主键。

域模型建立好之后,就可以花开两朵各表一支,表现层和持久层可以同时开发,先谈谈持久层关于用户模型的CRUD功能实现。

持久层Account CRUD实现

主要是用户的新增和修改,主要用于注册新用户和用户资料修改。

public interface AccountDao {

  Account getAccount(String username);  //获得一个Account

  void insertAccount(Account account);  //新增

  void updateAccount(Account account); //修改

}

持久层可以使用多种技术实现,例如Jdon框架的JdbcTemp代码实现比较方便,如果你的sql语句可能经常改动,使用iBatissql语句XML定义有一定好处,本例程使用Jpetstore原来的持久层实现iBatis。见源码包中的Account.xml

 

表现层Account表单创建(ModelForm

这是在Domain Model建立后最重要的一步,是前台表现层Struts开发的起步,表单创建有以下注意点:

表单类必须继承com.jdon.model.ModelForm

表单类基本是Domain Model的影子,每一个Model对应一个ModelForm实例,所谓对应:就是字段名称一致。ModelForm实例是由Model实例复制获得的。

 

public class AccountForm extends ModelForm {

 

  private String username;

  private String password;

  private String email;

  private String firstName;

  private String lastName;

  ……

 

}

当然AccountForm可能有一些与显示有关的字段,例如注册时有英文和中文选择,以及类别的选择,那么增加两个字段在AccountForm中:

  private List languages;

  private List categories;

这两个字段需要初始化值的,因为在AccountForm对应的Jsp的页面中要显示出来,这样用户才可能进行选择。选择后的值将放置在专门的字段中。

有两种方式初始化这两个字段:

1. AccountForm构造方法中初始化,前提是:这些初始化值是常量,如:

public AccountForm() {

    languages new ArrayList();

    languages.add("english");

    languages .add("japanese");

}

2.如果初始化值是必须从数据库中获取,那么采取前面章节介绍的使用ModelHandler来实现,这部分又涉及配置和代码实现,缺省时我们考虑通过jdonframework.xml配置实现。

 

Account CRUDstruts-config.xml的配置

第一步配置ActionForm

上节编写了ModelForm代码,ModelForm也就是strutsActionForm,在struts-config.xml中配置ActionForm如下:

 <form-bean name="accountFrom" type="com.jdon.framework.samples.jpetstore.presentation.form.AccountForm"/>

第二步配置Action

这需要根据你的CRUD功能实现需求配置,例如本例中用户注册和用户修改分开,这样,配置两套ModelViewActionModelSaveAction,具体配置见源码包中的struts-config-security.xml,这里将struts-config.xml根据模块划分成相应的模块配置,实现多模块开发,本模块是用户注册登陆系统,因此取名struts-config-security.xml

Account CRUDJdonframework.xml配置

    <model key="username" class ="com.jdon.framework.samples.jpetstore.domain.Account">

      <actionForm name="accountForm"/>

      <handler>

        <service ref="accountService">

          <initMethod   name="initAccount" />                           

          <getMethod    name="getAccount" />

          <createMethod name="insertAccount" />

          <updateMethod name="updateAccount" />

          <deleteMethod name="deleteAccount" />

        </service>

      </handler>

    </model> 

.其中有一个initMethod主要用于AccuntForm对象的初始化。其他都是增删改查的常规实现。

Account CRUD Jsp页面实现

在编辑页面EditAccountForm.jsp中加入:

<html:hidden name="accountFrom" property="action" value="create" />

在新增页面NewAccountForm.jsp加入:

<html:hidden name="accountFrom" property="action" value="edit" />

所有的字段都是直接来自accountFrom

 

整理模块配置

商品模块功能完成,struts提供了多模块开发,因此我们可以将这一模块单独保存在一个配置中:/WEB-INF/struts-config-security.xml,这样以后扩展修改起来方便。

 

商品查询模块实现

iBATIS-JPetstore中没有单独的CategoryForm,而是将三个ModelCategoryProduct、,Item合并在一个CatalogBean中,这样做的缺点是拓展性不强,将来这三个Model也许需要单独的ActionForm

由于我们使用Jdon框架的CRUD功能配置实现,因此,不怕细分这三个Model带来代码复杂和琐碎。

由于原来的Jpetstore“偷懒”,没有实现Category Product等的CRUD功能,只实现它们的查询功能,因此,我们使用Jdon框架的批量查询来实现查询。

持久层 Product批量查询实现

商品查询主要有两种批量查询,根据其类别IDCategoryId查询所有该商品目录下所有的商品;根据关键字搜索符合条件的所有商品,下面以前一个功能为例子:

iBatis-jpetstore使用PaginatedList作为分页的主要对象,该对象需要保存到HttpSession中,然后使用PaginatedListNextPage等直接遍历,这种方法只适合在小数据量合适,J2EE编程中不推荐向HttpSession放入大量数据,不利于cluster

根据Jdon批量查询的持久层要求,批量查询需要两种SQL语句实现:符合条件的ID集合和符合条件的总数:以及单个Model查询。

  //获得ID集合

 List getProductIDsListByCategory(String categoryId, int pagessize);

  //获得总数

  int getProductIDsListByCategoryCount(String categoryId);

  //单个Model查询

  Product getProduct(String productId) ;

这里我们需要更改一下iBatis原来的Product.xml配置,原来,它设计返回的是符合条件的所有Product集合,而我们要求是Product ID集合。

修改Product.xml如下:

<resultMap id="productIDsResult" class="java.lang.String">

    <result property="value" column="PRODUCTID"/>

</resultMap>              

                  

<select id="getProductListByCategory" resultMap="productIDsResult" parameterClass="string">

    select PRODUCTID from PRODUCT where CATEGORY = #value#

</select>

        

<select id="getProductListByCategoryCount"  resultClass="java.lang.Integer" parameterClass="string">

    select count(1) as value from PRODUCT where CATEGORY = #value#

</select>

    ProductDaoIBatis DAO实现,读取Product.xml中配置:

    public List  getProductIDsListByCategory(String categoryId, int start, int pagessize) {

        return sqlMapDaoTemplate.queryForList(

                "getProductListByCategory", categoryId, start, pagessize);

    }

 

    public int getProductIDsListByCategoryCount(String categoryId){

        Integer countI = (Integer)sqlMapDaoTemplate.queryForObject(

                "getProductListByCategoryCount", categoryId);

        return countI.intValue();

    }  

这样,结合配置的iBatis DAOJdon框架批量查询,在ProductManagerImp中创建PageIterator,当然这部分代码也可以在ProductDao实现,创建PageIterator代码如下:

      public PageIterator getProductIDsListByCategory(String categoryId, int start, int count)

       {

           PageIterator pageIterator = null;

            try {

                List list = productDao.getProductIDsListByCategory(categoryId, start, count);

                int allCount = productDao.getProductIDsListByCategoryCount(categoryId);

                int currentCount = start + list.size();

                pageIterator = new PageIterator(allCount, list.toArray(), start,

                        (currentCount < allCount)?true:false);

 

         } catch (DaoException daoe) {

             Debug.logError(" Dao error : " + daoe, module);

         }

       

       return pageIterator;

表现层Product批量查询实现

    根据批量查询的编程步骤,在表现层主要是实现ModelListAction继承、配置和Jsp编写,下面分步说:

    第一步,创建一个ModelListAction子类ProductListAction,实现两个方法:getPageIteratorfindModelByKeygetPageIterator方法如下:

      public PageIterator getPageIterator(HttpServletRequest request, int start,

            int count) {

        ProductManager productManager = (ProductManager) WebAppUtil.getService(

                "productManager", request);

        String categoryId = request.getParameter("categoryId");

        return productManager.getProductIDsListByCategory(categoryId, start, count);

                

    }

    findModelByKey方法如下:

      public Model findModelByKey(HttpServletRequest request, Object key) {

        ProductManager productManager = (ProductManager) WebAppUtil.getService(

                "productManager", request);

        return productManager.getProduct((String)key);

    }

    由于我们实现的是查询一个商品目录下所有商品功能,因此,需要显示商品目录名称,而前面操作的都是Product模型,所以在显示页面也要加入商品目录Category模型,我们使用ModelListActioncustomizeListForm方法:

    public void customizeListForm(ActionMapping actionMapping,

            ActionForm actionForm, HttpServletRequest request,

            ModelListForm modelListForm) throws Exception {

        ModelListForm listForm = (ModelListForm) actionForm;

        ProductManager productManager = (ProductManager) WebAppUtil.getService(

                "productManager", request);

        String categoryId = request.getParameter("categoryId");

        Category category = productManager.getCategory(categoryId);

        listForm.setOneModel(category);

    }

    第二步,配置struts-config.xml,配置ActionFormAction

    <form-bean name="productListForm" type="com.jdon.strutsutil.ModelListForm"/>

    action配置如下:

    <action path="/shop/viewCategory"

type="com.jdon.framework.samples.jpetstore.presentation.action.ProductListAction"

      name="productListForm" scope="request"

      validate="false" >

      <forward name="success" path="/catalog/Category.jsp"/>

    </action>

  第三步,编写Category.jsp

  productListForm中取出我们要显示两个模型,一个是oneModel中的Category;另外一个是Product Model集合listJsp语法如下:

<bean:define id="category" name="productListForm" property="oneModel" />

<bean:define id="productList" name="productListForm" property="list" />

我们可以显示商品目录名称如下:

<h2><bean:write name="category" property="name" /></h2>

这样我们就可以遍历productList中的Product如下:

<logic:iterate id="product" name="productList" >

  <tr bgcolor="#FFFF88">

  <td><b><html:link paramId="productId" paramName="product" paramProperty="productId" page="/shop/viewProduct.shtml"><font color="BLACK"><bean:write name="product" property="productId" /></font></html:link></b></td>

  <td><bean:write name="product" property="name" /></td>

  </tr>

</logic:iterate>

加上分页标签库如下:

  <MultiPages:pager actionFormName="productListForm " page="/shop/viewCategory.do"

               paramId="categoryId" paramName="category" paramProperty="categoryId">

<MultiPages:prev><img src="../images/button_prev.gif" border="0"></MultiPages:prev>

<MultiPages:index />

<MultiPages:next><img src="../images/button_next.gif" border="0"></MultiPages:next>

</MultiPages:pager>

至此,一个商品目录下的所有商品批量查询功能完成,由于是基于框架的模板化编程,直接上线运行成功率高。

商品搜索批量查询:

参考上面步骤,商品搜索也可以顺利实现,从后台到前台按照批量查询这条线索分别涉及的类有:

持久层实现:ProductDao中的三个方法:

List searchProductIDsList(String keywords, int start, int pagessize); //ID集合

 

int searchProductIDsListCount(String keywords); //总数

 

Product getProduct(String productId) ; //单个Model

表现层:建立ProductSearchAction类,配置struts-config.xml如下:

    <action path="/shop/searchProducts"

type="com.jdon.framework.samples.jpetstore.presentation.action.ProductSearchAction"

      name="productListForm" scope="request"

      validate="false">

      <forward name="success" path="/catalog/SearchProducts.jsp"/>

    </action>

与前面使用的都是同一个ActionFormproductListForm

编写SearchProducts .jsp,与Category.jsp类似,相同的是ActionForm;不同的是action

商品条目Item批量查询

条目Item批量实现与Product批量查询类似:

持久层:ItemDao提供三个方法:

List getItemIDsListByProduct(String productId, int start, int pagessize);//ID集合

 

  int getItemIDsListByProductCount(String productId);//总数

 

  Item getItem(String itemId); //单个Model

表现层:创建一个ItemListAction继承ModelListAction:完成getPageIteratorfindModelByKey,如下:

public class ItemListAction extends ModelListAction {

 

    public PageIterator getPageIterator(HttpServletRequest request, int start,

            int count) {

        ProductManager productManager = (ProductManager) WebAppUtil.getService(

                "productManager", request);

        String productId = request.getParameter("productId");

        return productManager.getItemIDsListByProduct(productId, start, count);

    }

 

    public Model findModelByKey(HttpServletRequest request, Object key) {

        ProductManager productManager = (ProductManager) WebAppUtil.getService(

                "productManager", request);

        return productManager.getItem((String)key);

    }

 

    public void customizeListForm……….

}

与前面的ProductListAction相比,非常类似,不同的是Model名称不一样,一个是Product一个是Item;

struts-config.xml配置如下:

    <form-bean name="itemListForm" type="com.jdon.strutsutil.ModelListForm"/>

 

    <action path="/shop/viewProduct"

type="com.jdon.framework.samples.jpetstore.presentation.action.ItemListAction"

      name="itemtListForm" scope="request"

      validate="false">

      <forward name="success" path="/catalog/Product.jsp"/>

    </action>

比较前面product的配置,非常类似,其实itemListFormproductListForm是同一个ModelListForm类型,可以合并起来统一命名为listForm,节省ActionForm的配置。

Product.jsp页面与前面的Category.jsp SearchProdcuts.jsp类似。

<bean:define id="product" name="itemListForm" property="oneModel" />

<bean:define id="itemList" name="itemListForm" property="list" />

分页显示:

  <MultiPages:pager actionFormName="itemListForm" page="/shop/viewProduct.do"

              paramId="productId" paramName="product" paramProperty="productId">

    …..

商品条目Item单条查询

单个显示属于CRUD中的一个查询功能,我们需要建立Model对应的ModelForm,将Item的字段拷贝到ItemForm中。配置这个ActionForm如下:

    <form-bean name="itemForm"

type="com.jdon.framework.samples.jpetstore.presentation.form.ItemForm"/>

第二步:因为这个功能属于CRUD一种,无需编程,但是需要配置jdonframework.xml

   <model key="itemId" class ="com.jdon.framework.samples.jpetstore.domain.Item">

      <actionForm name="itemForm"/>

      <handler>

        <service ref="productManager">

          <getMethod name="getItem" />

        </service>

      </handler>

    </model>

配置中只要一个方法getMethod就可以,因为只用到CRUD中的读取方式。

第三步:配置struts-config.xml如下:

    <action  path="/shop/viewItem"  type="com.jdon.strutsutil.ModelDispAction"

                   name="itemForm" scope="request"

                   validate="false">

      <forward name="success" path="/catalog/Item.jsp" />

      <forward name="failure" path="/catalog/Product.jsp" />

    </action>

第四步编辑Item.jsp,现在开始发现一个问题,Item.jsp中不只是显示Item信息,还有Product信息,而前面我们定义的是Item信息,如果使得Item.jsp显示Product信息呢,这就从设计起源Domain Model上考虑,在ItemModel中有Product引用:

  private Product product;

  public Product getProduct() { return product; }

  public void setProduct(Product product) { this.product = product; }

ItemProduct的多对一关系其实应该在域建模开始就考虑到了。

那么,我们只要在持久层查询Item时,能够将其中的Product字段查询就可以。在持久层的iBatisProduct.xml实现有下列SQL语句:

  <select id="getItem" resultMap="resultWithQuantity" parameterClass="string">

    select

      I.ITEMID, LISTPRICE, UNITCOST, SUPPLIER, I.PRODUCTID, NAME,

      DESCN, CATEGORY, STATUS, ATTR1, ATTR2, ATTR3, ATTR4, ATTR5, QTY

    from ITEM I, INVENTORY V, PRODUCT P where P.PRODUCTID = I.PRODUCTID and I.ITEMID = V.ITEMID and I.ITEMID = #value#

  </select>

这段语法实际在查询Item时,已经将Product查询出来,这样Item Model中已经有Product数据,因为ActionFormModel映射,因此,前台Jsp也可以显示Product数据。

Item.jsp中,进行下面定义:

<bean:define id="product" name="itemForm " property="product" />

<bean:define id="item" name="itemForm " />

itemFormproduct属性定义为product即可;这样不必大幅度修改原来的Item.jsp了。

整理模块配置

商品模块功能完成,struts提供了多模块开发,因此我们可以将这一模块单独保存在一个配置中:/WEB-INF/struts-config-catalog.xml,这样以后扩展修改起来方便。

 

购物车模块实现

购物车属于一种有状态数据,也就是说,购物车的scope生命周期是用户,除非这个用户离开,否则购物车一直在内存中存在。

有态POJO服务

现在有两种解决方案:

第一,将购物车状态作为数据类,保存到ActionForm中,设置scopesession,这种形式下,对购物车的数据操作如加入条目等实现不很方便,iBatis-jpetstore 4.0.5就采取这个方案,在数据类Cart中存在大量数据操作方法,那么Cart这个类到底属于数据类Model?还是属于处理服务类呢?

在我们J2EE编程中,通常使用两种类来实现功能,一种是数据类,也就是我们设计的Model;一种是服务类,如POJO服务或EJB服务,服务属于一种处理器,处理过程。使用这两种分类比较方便我们来解析业务需求,EJB中实体BeanSession Bean也是属于这两种类型。

iBatis-jpetstore 4.0.5则是将服务和数据类混合在一个类中,这也属于一种设计,但是我们认为它破坏了解决问题的规律性,而且造成数据和操作行为耦合性很强,在设计模式中我们还使用桥模式来分离抽象和行为,因此这种做法可以说是反模式的。那么我们采取数据类和服务分离的方式方案来试试看:

第二.购物车功能主要是对购物车这个ModelCRUD,与通常的CRUD区别是,数据是保存到HttpSession,而不是持久化到数据库中,是数据状态保存不同而已。所以如果我们实现一个CartService,它提供addupdatedelete等方法,只不过操作对象不是数据库,而是其属性为购物车Cart,然后将该CarService实例保存到HttpSession,实现每个用户一个CartService实例,这个我们成为有状态的POJO服务。

这种处理方式类似EJB架构处理,如果我们业务服务层使用EJB,那么使用有态会话Bean实现这个功能。

现在问题是,Jdon框架目前好像没有提供有状态POJO服务实例的获得,那么我们自己在WebAppUtil.getService获得实例后,保存到HttpSession中,下次再到HttpSession中获得,这种有状态处理需要表现层更多代码,这就不能使用Jdon框架的CRUD配置实现了,需要我们代码实现ModelHandler子类。

考虑到可能在其他应用系统还有这种需求,那么能不能将有状态的POJO服务提炼到Jdon框架中呢?关键使用什么方式加入框架,因为这是设计目标服务实例的获得,框架主要流程代码又不能修改,怎么办?

Jdon框架的AOP功能在这里显示了强大灵活性,我们可以将有状态的POJO服务实例获得作为一个拦截器,拦截在原来POJO服务实例获得之前。在Jdon框架设计中,目标服务实例的获得一般只有一次。

创建有状态POJO服务拦截器com.jdon.aop.interceptor. StatefulInterceptor,再创建一个空接口:com.jdon.controller.service.StatefulPOJOService,需要实现有状态实例的POJO类只要继承这个接口就可以。

配置aspect.xml,加入这个拦截器:

  <interceptor name="statefulInterceptor" class="com.jdon.aop.interceptor.StatefulInterceptor"

pointcut="pojoServices" />      

这里需要注意的是:你不能让一个POJO服务类同时继承Poolable,然后又继承Stateful,因为这是两种不同的类型,前者适合无状态POJO;后者适合CartService这样有状态处理;这种选择和EJB的有态/无态选择是一样的。

ModelService设计

购物车模块主要围绕域模型Cart展开,需要首先明确Cart是一个什么样的业务模型,购物车页面是类似商品条目批量查询页面,不过购物车中显示的不但是商品条目,还有数量,那么我们专门创建一个Model来指代它,取名为CartItemCartItemItem父集,多了一个数量。

这样购物车页面就是CartItem的批量查询页面,然后还有CartItemCRUD操作,所以购物车功能主要是CartItemCRUD和批量查询功能。

iBatis 4.0.5原来设计了专门Cart Model,其实这个Cart主要是一个功能类,因为它的数据项只有一个MapList,这根本不能代表业务需求中的一个模型。虽然iBatis 4..0.5也可以自圆其说实现了购物车功能,但是这种实现是随心所欲,无规律性可遵循,因而以后维护起来也是困难,维护人员理解困难,修改起来也没有章程可循,甚至乱改一气。

CartItem可以使用iBatis原来的CartItem,这样也可保持Cart.jsp页面修改量降低。删除原来的Cart这个Model,建立对应的CartService,实现原来的Cart一些功能。

public interface CartService {

       CartItem getCartItem(String itemId);

       void addCartItem(EventModel em);

       void updateCartItem(EventModel em);

       void deleteCartItem(EventModel em);

       PageIterator getCartItems();

}

CartServiceImpCartService子类,它是一个有状态POJO服务,代码简要如下:

public class CartServiceImp implements CartService, Stateful{

    private ProductManager productManager;

    //将原来iBatis Cart类中两个属性移植到CartServiceImp

    private final Map itemMap = Collections.synchronizedMap(new HashMap());

    private  List itemList = new ArrayList();

 

    public CartServiceImp(ProductManager productManager) {

        super();

        this.productManager = productManager;

    }

    ……

}

itemMap是装载CartItem的一个Map,是类属性,由于CartServiceImp是有状态的,每个用户一个实例,那么也就是每个用户有自己的itemMap列表,也就是购物车。

CartServiceImp中的 getCartItemIDs是查询购物车当前页面的购物条目,属于批量分页查询实现,这里有一个需要考量的地方,是getCartItems方法还是getCartItemIDs方法?也就是返回CartIem的实例集合还是CartItemItemId集合?按照前面标准的Jdon框架批量分页查询实现,应该返回CartItemItemId集合,然后由Jdon框架的ModelListAction根据ItemId首先从缓存中获得CartItem实例,但是本例CartItem本身不是持久化在数据库,而也是内存HttpSession中,所以ModelListAction这种流程似乎没有必要。

如果将来业务需求变化,购物车状态不是保存在内存而是数据库,这样,用户下次登陆时,可以知道他上次购物车里的商品条目,那么采取Jdon框架标准查询方案还是有一定扩展性的。

这里,我们就事论事,采取返回CartIem的实例集合,展示一下如何灵活应用Jdon框架的批量查询功能。下面是CartServiceImp getCartItems方法详细代码:

 public PageIterator getCartItems(int start, int count) {

        int offset = itemList.size() - start; //获得未显示的总个数

        int pageCount = (count < offset)?count:offset;

        List pageList = new ArrayList(pageCount); //当前页面记录集合

        for(int i=start; i< pageCount + start;i++){

            pageList.add(itemList.get(i));

        }

        int allCount = itemList.size();

        int currentCount = start + pageCount;

        return new PageIterator(allCount, pageList.toArray(new CartItem[0]), start,

                (currentCount < allCount)?true:false);

}

getCartItems方法是从购物车所有条目itemList中查询获得当前页面的条目,并创建一个PageIterator

注意,现在这个PageIteratorkeys属性中装载的不是数据ID集合,而是完整的CartItem集合,因为上面代码中pageList中对象是从itemList中获得,而itemList中装载的都是CartItem

表现层购物车显示功能

由于PageIterator中封装的是完整Model集合,而不是ID集合,所以现在表现层有两种方案,继承框架的ModelListAction;或重新自己实现一个Action,替代ModelListAction

这里使用继承框架的ModelListAction方案,巧妙地实现我们的目的,省却编码:

public class CartListAction extends ModelListAction {

 

    public PageIterator getPageIterator(HttpServletRequest request, int arg1, int arg2) {

        CartService cartService = (CartService)WebAppUtil.getService("cartService", request);

        return cartService.getCartItems();

    }

 

    public Model findModelByKey(HttpServletRequest arg0, Object key) {

        return (Model)key; //因为key不是主键,而是完整的Model,直接返回

    }

   

    protected boolean isEnableCache(){

        return false;  //无需缓存,CartItem本身实际是在内存中。

    }

 

}

配置struts-config.xml

<form-beans>    

   <form-bean name="listForm" type="com.jdon.strutsutil.ModelListForm"/>

</form-beans>   

 <action-mappings>

    <action path="/shop/viewCart"

type="com.jdon.framework.samples.jpetstore.presentation.action.CartListAction"

      name="listForm" scope="request"

      validate="false">

      <forward name="success" path="/cart/Cart.jsp"/>

    </action>

    ……

  </action-mappings>

上面是购物车显示实现,只要调用/shop/viewCart.shtml就可以显示购物车了。

Cart.jsp页面插入下面标签:

<logic:iterate id="cartItem" name="listForm" property="list">

    ….

</logic:iterate>

分页显示标签如下:

<MultiPages:pager actionFormName="listForm" page="/shop/viewCart.shtml">

<MultiPages:prev name="<font color=green><B>&lt;&lt; Prev</B></font>"/>

<MultiPages:index />

<MultiPages:next name="<font color=green><B>Next &gt;&gt;</B></font>"/>

</MultiPages:pager>

 

 

购物车新增删除条目功能

前面完成了购物车显示功能,下面是设计购物车的新增和删除、修改功能。

参考Jdon框架的CRUD功能实现,ModelCartItem,配置jdonframework.xml使其完成新增删除功能:

                   <model key="workingItemId"

                            class="com.jdon.framework.samples.jpetstore.domain.CartItem">

                            <actionForm name="cartItemForm"/>

                            <handler>

                                     <service ref="cartService">

                                               <createMethod name="addCartItem"/>

                                               <deleteMethod name="deleteCartItem"/>

                                     </service>

                            </handler>

                   </model>

在这个配置中,只有新增和删除方法,修改方法没有,因为购物车修改主要是其中商品条目的数量修改,它不是逐条修改,而是一次性批量修改,这里的ModelCartItem,这是购物车里的一个条目,因此如果这里写修改,也只是CartItem一个条目的修改,不符合我们要求。下面专门章节实现这个修改。

表现层主要是配置,没有代码,代码都依靠cartService中的addCartItemdeleteCartItem实现:例如:

    public void addCartItem(EventModel em) {

        CartItem cartItem = (CartItem) em.getModel();

        String workingItemId = cartItem.getWorkingItemId();

        ……

    }

注意addCartItem中从EventModel实例中获取的ModelCartItem,这与我们在jdonframework.xml中上述定义的Model类型是统一的。

Struts-config.xml中定义是CRUD的标准定义,注意,这里只有ModelSaveAction无需ModelViewAction,因为将商品条目加入或删除购物车这个功能没有专门的显示页面:

    <action path="/shop/addItemToCart" type="com.jdon.strutsutil.ModelSaveAction"

      name="cartItemForm" scope="request"

      validate="false">

      <forward name="success" path="/cart/Cart.jsp"/>

    </action>

 

    <action path="/shop/removeItemFromCart" type="com.jdon.strutsutil.ModelSaveAction"

      name="cartItemForm" scope="request"

      validate="false">

      <forward name="success" path="/cart/Cart.jsp"/>

    </action>

注意,调用删除功能时,需要附加action参数:

/shop/removeItemFromCart.shtml?action=delete

/shop/addItemToCart.shtml是新增属性,缺省后面无需跟参数。

购物车条目批量修改功能

上面基本完成了购物车主要功能;购物车功能一个复杂性在于其显示功能和修改功能合并在一起,修改功能是指修改购物车里所有商品条目的数量。

既然有修改功能,而且这个修改功能比较特殊,我们需要设计一个独立的ActionForm,用来实现商品条目数量的批量修改。

首先设计一个ActionFormModelForm),该ModelForm主要用来实现购物车条目数量的更改,取名为CartItemsForm,其内容如下:

public class CartItemsForm extends ModelForm {

    private String[] itemId;

    private int[]quantity;

    private BigDecimal totalCost;

    …..

}

itemIdquantity设计成数组,这样,Jsp页面可以一次性提交多个itemIdquantity数值。

现在CartItemsForm已经包含前台jsp输入的数据,我们还是将其传递递交到服务层实现处理。因此建立一个Model,内容与CartItemsForm类似,这里的Model名为CartItems,实际是一个传输对象。

public class CartItems extends Model{

    private String[] itemId;

    private int[] quantity;

    private BigDecimal totalCost;

    ……

}

表现层在jdonframework.xml定义配置就无需编码,配置如下:

                   <model key=" "

                            class="com.jdon.framework.samples.jpetstore.domain.CartItems">

                            <actionForm name="cartItemsForm"/>

                            <handler>

                                     <service ref="cartService">

                                               <updateMethod name="updateCartItems"/>

                                     </service>

                            </handler>

                   </model>

上面配置中,ModelCartItemsActionFormcartItemsForm,这两个是专门为批量修改设立的。只有一个方法updateMethod。因为在这个更新功能中,没有根据主键从数据库查询Model的功能,因此,这里modelkey可以为空值。

服务层CartServiceImpupdateCartItems方法实现购物车条目数量更新:

    public void updateCartItems(EventModel em) {

        CartItems cartItems = (CartItems) em.getModel();

        try {

            String[] itemIds = cartItems.getItemId();

            int[] qtys = cartItems.getQuantity();

            int length = itemIds.length;

            for (int i = 0; i < length; i++) {

                updateCartItem(itemIds[i], qtys[i]);//逐条更新购物车中的数量

            }

        } catch (Exception ex) {

            logger.error(ex);

        }

    }

 

注意updateCartItems中从EventModel取出的是CartItems,和前面addCartItem方法中取出的是CartItem Model类型不一样,这是因为这里我们在jdonframework.xml中定义与updateCartItems相对应的ModelCartItems.

最后一步工作是Cat.jsp中加入CartItemsForm,能够在购物车显示页面有一个表单提交,客户按提交按钮,能够立即实现当前页面购物车数量的批量修改。Cat.jsp加入如下代码:

<html:form action="/shop/updateCartQuantities.shtml" method="post" >

<html:hidden property="action" value="edit" />

……

<input type="hidden" name="itemId" value="<bean:write name="cartItem" property="workingItemId"/>">

  <input type="text" size="3" name="quantity" value="<bean:write name="cartItem"

property="quantity"/>" />

…….

注意,一定要有action赋值edit这一行,这样提交给updateCartQuantities.shtml实际是ModelSaveAction时,框架才知道操作性质。

购物车总价显示功能

最后,还有一个功能需要完成,在购物车显示时,需要显示当前购物车的总价格,注意不是显示当前页面的总价格,所以无法在Cart.jsp直接实现,必须遍历购物车里所有CartItem计算总数。

该功能是购物车显示时一起实现,购物车显示是通过CartListAction实现的,这个CartListAction实际是生成一个ModelListForm,如果ModelListForm能够增加一个getTotalPrice方法就可以,因此有两种实现方式:继承ModelListForm加入自己的getTotalPrice方法;第二种无需再实现自己的ModelListFormModelListForm可以携带一个Model,通过setOneModel即可,这个方法是在ModelListAction的子类CartListAction可以override覆盖实现的,在CartListAction加入下列方法:

  protected Model setOneModel(HttpServletRequest request){

        CartService cartService = (CartService)WebAppUtil.getService("cartService", request);

        CartItems cartItems = new CartItems();

        cartItems.setTotalCost(cartService.getSubTotal());       

        return cartItems;

    }

我们使用空的CartItems作为携带价格总数的Model,然后在Cart.jsp中再取出来显示:

<bean:define id="cartItems " name="listForm" property="oneModel" />

<b>Sub Total: <bean:write name="cartItems" property="subTotal" format="$#,##0.00" />

将当前页面listForm中属性oneModel定义为cartItems,它实际是我们定义的CartItems

下一行取出总价即可。

用户喜欢商品列表功能

在显示购物车时,需要一起显示该用户喜欢的商品列表,很显然这是一个批量分页查询实现,但是它有些特殊,它首先显示的第一页不是由URL调用的,而是嵌入在购物车显示中,那么只能在购物车显示页面的ModellistForm中做文章。

在上节中,在CartListActionsetOneModel方法中,使用CartItems作为价格总数的载体,现在恐怕我们也要将之作为本功能实现载体。

还有一种实现载体,就是其他scopesessionActionFormAccountForm很适合做这样的载体,而且和本功能意义非常吻合,所以在AccountForm/Account中增加一个myList字段,在myList字段中,放置的是该用户喜欢的商品Product集合,注意不必放置Product的主键集合,因为我们只要显示用户喜欢商品的第一页,这一页是嵌入购物车显示页面中,所以第一页显示的个数是由程序员可事先在程序中定义。

这样在Account获得时,一起将myList集合值获得。

订单模块实现

我们还是从域模型开始,Order是订单模块的核心实体,其内容可以确定如下:

public class Order extends Model {

 

  /* Private Fields */

 

  private int orderId;

  private String username;

  private Date orderDate;

  private String shipAddress1;

  private String shipAddress2;

  …..

}

第二步,建立与Model对应的ModelForm,我们可以称之为边界模型,代码从Order拷贝过来即可。当然OrderForm还有一些特殊的字段以及初始化:

public class OrderForm extends ModelForm

private boolean shippingAddressRequired;

    private boolean confirmed;

    static {

        List cardList = new ArrayList();

        cardList.add("Visa");

        cardList.add("MasterCard");

        cardList.add("American Express");

        CARD_TYPE_LIST = Collections.unmodifiableList(cardList);

      }   

   

    public OrderForm(){

        this.shippingAddressRequired = false;

        this.confirmed = false;       

    }

   …..

}

第三步,建立Order Model的业务服务接口,如下:

public interface OrderService {

       void insertOrder(Order order);

       Order getOrder(int orderId);

       List getOrdersByUsername(String username);

}

第四步,实现OrderServicePOJO子类:OrderServiceImp

第五步,表现层实现,本步骤可和第四步同时进行。

OrderService中有订单的插入创建功能,我们使用Jdon框架的CRUDcreate配置实现,配置struts-config.xmljdonframework.xml

      <form-bean name="orderForm"

type="com.jdon.framework.samples.jpetstore.presentation.form.OrderForm"/>

 

         <model key="orderId"

                            class="com.jdon.framework.samples.jpetstore.domain.Order">

                            <actionForm name="orderForm"/>

                            <handler>

                                     <service ref="orderService">

                                               <createMethod name="insertOrder"/>

                                     </service>

                            </handler>

         </model>

第六步:根据逐个实现界面功能,订单的第一个功能创建一个新的订单,在新订单页面NewOrderForm.jsp推出之前,这个页面的ActionForm已经被初始化,是根据购物车等Cart其他Model数据初始化合成的。

新订单页面初始化

根据Jdon框架中CRUD功能实现,初始化一个ActionForm有两种方法:一继承ModelHandler实现initForm方法;第二通过jdonframework.xmlinitMethod方法配置。

这两个方案选择依据是根据用来初始化的数据来源什么地方。

订单表单初始化实际是来自购物车信息或用户账号信息,这两个都事先保存在HttpSession中,购物车信息是通过有态CartService实现的,所以这些数据来源是和request相关,那么我们选择第一个方案。

继承ModelHandler之前,我们可以考虑首先继承ModelHandler的子类XmlModelHandler,只要继承initForm一个方法即可,这样节省其他方法编写实现。

public class OrderHandler extends XmlModelHandler {

   

    public ModelForm initForm(HttpServletRequest request) throws

    Exception{

        HttpSession session = request.getSession();

        AccountForm accountForm = (AccountForm) session.getAttribute("accountForm");

        OrderForm orderForm = createOrderForm(accountForm);

        CartService cartService = (CartService)WebAppUtil.getService("cartService", request);

       

        orderForm.setTotalPrice(cartService.getSubTotal());

 

        //below can read from the user's creditCard service;

        orderForm.setCreditCard("999 9999 9999 9999");

        orderForm.setExpiryDate("12/03");

        orderForm.setCardType("Visa");

        orderForm.setCourier("UPS");

        orderForm.setLocale("CA");

        orderForm.setStatus("P");

 

        Iterator i = cartService.getAllCartItems().iterator();

        while (i.hasNext()) {

          CartItem cartItem = (CartItem) i.next();

          orderForm.addLineItem(cartItem);

        }

        return orderForm;        

    }

   

    private OrderForm createOrderForm(AccountForm account){

      ……

    }

}

ModelHandlerinitForm继承后,因为这使用了JdonCRUD功能实现,这里我们只使用到CRUD中的创建功能,因此,findModelBykey方法就无需实现,或者可以在jdonframework.xml中配置该方法实现。

考虑到在initForm执行后,需要推出一个NewOrderForm.jsp页面,这是一个新增性质的页面。所以在struts-config.xml

                  <action path="/shop/newOrderForm" type="com.jdon.strutsutil.ModelViewAction"

      name="orderForm" scope="request"      validate="false">

        <forward name="create" path="/order/NewOrderForm.jsp"/>

    </action>      

订单确认流程

新的订单页面推出后,用户需要经过两个流程才能确认保存,这两个流程是填写送货地址以及再次完整确认。这两个流程实现的动作非常简单,就是将OrderForm中的shippingAddressRequired字段和confirm字段赋值,相当于简单的开关,这是一个很简单的动作,可以有两种方案:直接在jsp表单中将这两个值赋值;直接使用strutsAction实现。后者需要编码,而且不是非有这个必要,只有第一个方案行不通时才被迫实现。

第一步:填写送货地址

使用Jdon框架的推出纯Jsp功能的Action配置struts-config.xml

    <action path="/shop/shippingForm" type="com.jdon.strutsutil.ForwardAction"

      name="orderForm" scope="session"      validate="false">

      <forward name="forward" path="/order/ShippingForm.jsp"/>         

    </action>

这是实现送货地址页面的填写,使用的还是OrderForm。更改前面流程NewOrderForm.jsp中的表单提交action值为本action path: shippingForm.shtml

<html:form action="/shop/shippingForm.shtml" styleId="orderForm" method="post" >

  ……

</html:form>

ShippingForm.jsp中增加将shippingAddressRequired赋值的字段:

<html:hidden name="orderForm"  property="shippingAddressRequired" value="false"/>

第二步:确认订单

类似上述步骤,配置struts-config.xml

   <action path="/shop/confirmOrderForm" type="com.jdon.strutsutil. ForwardAction"

      name="orderForm" scope="session"  validate="false">

      <forward name="forward" path="/order/ConfirmOrder.jsp"/>          

   </action>        

将上一步ShippingForm.jsp的表单action改为本actionpath: confirmOrderForm.shtml

<html:form action="/shop/confirmOrderForm.shtml" styleId="orderBean" method="post" >

修改ConfirmOrder.jsp中提交的表单为最后一步,保存订单newOrder.shtml

<html:link page="/shop/newOrder.shtml?confirmed=true"><img border="0" src="../images/button_continue.gif" /></html:link>

第三步:下面是创建数据保存功能实现:

    <action path="/shop/newOrder" type="com.jdon.strutsutil.ModelSaveAction"

      name="orderForm" scope="session"

      validate="true" input="/order/NewOrderForm.jsp">

      <forward name="success" path="/order/ViewOrder.jsp"/>

    </action>

ModelSaveAction是委托ModelHandler实现的,这里有两种方式:配置方式:在jdonframework.xml中配置了方法插入;第二种是实现代码,这里原本可以使用配置方式实现,但是因为在功能上有要求:在订单保存后,需要清除购物车数据,因此只能使用代码实现方式,在ModelHandler中实现子类方法serviceAction

public void serviceAction(EventModel em, HttpServletRequest request) throws java.lang.Exception {

   try {

      CartService cartService = (CartService) WebAppUtil.getService("cartService", request);

cartService.clear(); //清楚购物车数据

 

      OrderService orderService = (OrderService) WebAppUtil.getEJBService("orderService", request);

      switch (em.getActionType()) {

            case Event.CREATE:

                Order order = (Order) em.getModel();

                orderService.insertOrder(order);

                cartService.clear();

                break;

        case Event.EDIT:

                break;

        case Event.DELETE:

                break;

       }

   } catch (Exception ex) {

            throw new Exception(" serviceAction Error:" + ex);

   }

}

用户订单列表

用户查询自己的订单列表功能很明显可以使用Jdon框架的批量查询事先。

struts-config.xml中配置ModelListForm如下:

      <form-bean name="listForm" type="com.jdon.strutsutil.ModelListForm"/>

建立继承ModelListAction子类OrderListAction

public class OrderListAction extends ModelListAction {

 

    public PageIterator getPageIterator(HttpServletRequest request, int start, int count) {

        OrderService orderService = (OrderService) WebAppUtil.getService("orderService", request);

        HttpSession session = request.getSession();

        AccountForm accountForm = (AccountForm) session.getAttribute("accountForm");

        if (accountForm == null) return new PageIterator();

        return orderService.getOrdersByUsername(accountForm.getUsername(), start, count);

    }

 

    public Model findModelByKey(HttpServletRequest request, Object key) {

        OrderService orderService = (OrderService) WebAppUtil.getService("orderService", request);

        return orderService.getOrder((Integer)key);

    }

 

}

修改OrderService, 将获得Order集合方法改为:

public class OrderService{

 

PageIterator getOrdersByUsername(String username, int start, int count)

….

}

根据Jdon批量查询要求,使用iBatis实现返回ID集合以及符合条件的总数。

最后编写ListOrders.jsp,两个语法:logic:iterator MultiPages

配置jdon框架启动

目前我们有四个struts-config.xml,前面每个模块一个配置:

/WEB-INF/struts-config.xml 主配置

/WEB-INF/struts-config-catalog.xml  商品相关配置

/WEB-INF/struts-config-security.xml 用户相关配置

/WEB-INF/struts-config-cart.xml 购物车相关配置

/WEB-INF/struts-config-order.xml 订单相关配置

本项目只有一个jdonframework.xml,当然我们也可以创建多个jdonframework.xml,然后在其struts-config.xml中配置。

  <plug-in className="com.jdon.strutsutil.InitPlugIn">

    <set-property property="modelmapping-config"  value="jdonframework_iBATIS.xml" />

  </plug-in>

修改iBatisDAO配置

iBatis 4.0.5中原来的配置过于扩张(从持久层扩张到业务层),AccountDao每个实例获得都需要通过daoManager.getDao这样形式,而使用Jdon框架后,AccountDaoDAO实例获得无需特别语句,我们只要在AccountService直接引用AccountDao接口,至于AccountDao的具体实例,通过Ioc注射进入即可。

因此,在jdonframework.xml中有如下配置:

         <pojoService name="accountDao"

                   class="com.jdon.framework.samples.jpetstore.persistence.dao.sqlmapdao.AccountSqlMapDao"/>

         <pojoService name="accountService"

                            class="com.jdon.framework.samples.jpetstore.service.bo.AccountServiceImp"/>

         <pojoService name="productManager"

                            class="com.jdon.framework.samples.jpetstore.service.bo.ProductManagerImp"/>

AccountServiceImp代码如下:

public class AccountServiceImp implements AccountService, Poolable {

    private AccountDao accountDao;

    private ProductManager productManager;

   

    public AccountServiceImp(AccountDao accountDao,

                             ProductManager productManager){

        this.accountDao = accountDao;

        this.productManager = productManager;

    }

 

AccountServiceImp需要两个构造方法实例,这两个中有一个是AccountDao

按照iBatis原来的AccountDao子类AccountSqlMapDao有一个构造方法参数是DaoManager,但是我们无法生成自己的DaoManager实例,因为DaoManager是由dao.xml配置文件读取后生成的,这是一个动态实例;那只有更改AccountSqlMapDao构造方法了。

根源在于BaseSqlMapDao类,BaseSqlMapDao是一个类似JDBC模板类,每个Dao都继承它,现在我们修改BaseSqlMapDao如下:

public class BaseSqlMapDao extends DaoTemplate implements SqlMapExecutor{

    …..

}

BaseSqlMapDaoXML配置和JDBC模板的结合体,在这个类中,这两者搭配在一起,在其中实现SqlMapExecutor各个子方法。

我们再创建一个DaoManagerFactory,专门根据配置文件创建DaoManager实例:

主要方法如下:

Reader reader = Resources.getResourceAsReader(daoResource);

daoManager = DaoManagerBuilder.buildDaoManager(reader);

其中daoResourcedao.xml配置文件,这个配置是在jdonframework.xml中配置:

<pojoService name="daoManagerFactory"

                            class="com.jdon.framework.samples.jpetstore.persistence.dao.DaoManagerFactory">

                            <constructor 

value="com/jdon/framework/samples/jpetstore/persistence/dao/sqlmapdao/sql/dao.xml"/>

         </pojoService>

这样,我们可以通过改变jdonframework.xml配置改变dao.xml配置。

Dao.xml配置如下:

<daoConfig>

  <context>

    <transactionManager type="SQLMAP">

      <property name="SqlMapConfigResource"       

value="com/jdon/framework/samples/jpetstore/persistence/dao/sqlmapdao/sql/sql-map-config.xml"/>

    </transactionManager>

 

    <dao interface="com.ibatis.sqlmap.client.SqlMapExecutor"  

implementation="com.jdon.framework.samples.jpetstore.persistence.dao.sqlmapdao.BaseSqlMapDao"/>

 

  </context>

</daoConfig>

dao.xml中,我们只配置一个JDBC模板,而不是将所有的如AccountDao配置其中,因为我们需要iBatis只是它的JDBC模板,实现持久层方便的持久化,仅此而已!

DaoManagerFactory为我们生产了DaoManager实例,那么如何赋值到BaseSqlMapDao中,我们设计一个创建BaseSqlMapDao工厂如下:

 

public class SqlMapDaoTemplateFactory {

 

    private DaoManagerFactory daoManagerFactory;

 

    public SqlMapDaoTemplateFactory(DaoManagerFactory daoManagerFactory) {

        this.daoManagerFactory = daoManagerFactory;

    }

   

    public SqlMapExecutor getSqlMapDaoTemp(){

        DaoManager daoManager = daoManagerFactory.getDaomanager();

        return (SqlMapExecutor)daoManager.getDao(SqlMapExecutor.class);

    }

 

}

通过getSqlMapDaoTemp方法,由DaoManager.getDao方法获得BaseSqlMapDao实例,BaseSqlMapDao的接口是SqlMapExecutor,这样我们通过DaoManager获得一个JDBC模板SqlMapExecutor的实例。

这样,在AccountDao各个子类AccountSqlMapDao中,我们只要通过SqlMapDaoTemplateFactory获得SqlMapExecutor实例,委托SqlMapExecutor实现JDBC操作,如下:

public class AccountSqlMapDao implements AccountDao {

  //iBatis数据库操作模板

  private SqlMapExecutor sqlMapDaoTemplate;

    //构造方法

  public AccountSqlMapDao(SqlMapDaoTemplateFactory sqlMapDaoTemplateFactory) {

      sqlMapDaoTemplate = sqlMapDaoTemplateFactory.getSqlMapDaoTemp();

  }

 //查询数据库

  public Account getAccount(String username) throws SQLException{

    return (Account)sqlMapDaoTemplate.queryForObject("getAccountByUsername", username);

  }

 

 

部署调试

当在JBossTomcat控制台 或者日志文件中出现下面字样标识Jdon框架安装启动成功:

<========  Jdon Framework started successfully! =========>

Jdon框架启动成功后,以后出现的错误基本是粗心大意的问题,仔细分析会很快找到原因,相反,如果编程时仔细慢一点,则后面错误出现概率很小。

使用Jdon框架开发Jpetstore 一次性调试通过率高,一般问题都是存在数据库访问是否正常,一旦正常,主要页面就出来了,其中常见问题是jsp页面和ActionForm的字段不对应,如jsp页面显示如下错误:

No getter method available for property creditCardTypes for bean under name orderForm

表示在OrderForm中没有字段creditCardTypes,或者有此字段,但是大小写错误等粗心问题。

如果jsp页面或后台log记录显示:

System error! please call system Admin.java.lang.Exception

一般这是由于前面出错导致,根据记录向前搜索,搜索到第一个出错记录:

2005-07-07 11:55:16,671 [http-8080-Processor25] DEBUG com.jdon.container.pico.PicoContainerWrapper - getComponentClass: name=orderService

2005-07-07 11:55:16,671 [http-8080-Processor25] ERROR com.jdon.aop.reflection.MethodConstructor -  no this method name:insertOrder

第一个出错是在MethodConstructor报错,没有insertOrder方法,根据上面一行是orderService,那么检查orderService代码看看有无insertOrder

public interface OrderService {

       void insertOrder(Order order);

       Order getOrder(int orderId);

       List getOrdersByUsername(String username);

}

OrderService接口中是有insertOrder方法,那么为什么报错没有呢?仔细检查一下,是不是insertOrder的方法参数有问题,哦, 因为orderService的调用是通过jdonframework.xml下面配置进行的:

         <model key="orderId"

                            class="com.jdon.framework.samples.jpetstore.domain.Order">

                            <actionForm name="orderForm"/>

                            <handler>

                                     <service ref="orderService">

                                               <createMethod name="insertOrder"/>

                                     </service>

                            </handler>

                   </model>

而根据Jdon框架要求,使用配置实现ModelHandler,则要求OrderServiceinsertOrder方法参数必须是EventModel,更改OrderServiceinsertOrder方法如下:

public interface OrderService {

    void insertOrder(EventModel em);

}

同时,修改OrderService的子类代码OrderServiceImp

  public void insertOrder(EventModel em) {

        Order order = (Order)em.getModel();

        try{

            orderDao.insertOrder(order);

        }catch(Exception daoe){

                Debug.logError(" Dao error : " + daoe, module);

                em.setErrors("db.error");

        }

    }

 

注意em.setErrors方法,该方法可向struts页面显示出错信息,db.error是在本项目的application.properties中配置的。

关于本次页面出错问题,还有更深缘由,因为我们在jdonframework.xml中中定义了createMethod,而根据前面已经有ModelHandler子类代码OrderHandler实现,所以,这里不用配置实现,jdonframework.xml的配置应该如下:

                   <model key="orderId"

                            class="com.jdon.framework.samples.jpetstore.domain.Order">

                            <actionForm name="orderForm"/>

                            <handler class="com.jdon.framework.samples.jpetstore.presentation.action.OrderHandler"/>

                   </model>

直接定义了hanlderclassOrderHandler,在OrderHandler中的serviceAction我们使用代码调用了OrderServiceinsertOrder方法,如果使用这样代码调用,无需要求OrderServiceinsertOrder的参数是EventModel了。

 

 

总结

Jpetstore整个开发大部分基于Jdon框架开发,特别是表现层,很少直接接触使用struts原来功能,Jdon框架的表现层架构基本让程序员远离了struts的烦琐开发过程,又保证了strutsMVC实现。

Jpetstore中只有SignonAction这个类是直接继承strutsDispatchAction,这个功能如果使用基于J2EE容器的安全认证实现(见JdonNews),那么Jpetstore全部没有用到strutsAction,无需编写Action代码;ActionForm又都是Model的拷贝,ActionActionFormstruts编码的两个主要部分,这两个部分被Jdon框架节省后,整个J2EEWeb层开发方便快速,而且容易得多。

这说明Jdon框架确实是一款快速开发J2EE工具,而且是非常轻量的。

纵观Jpetstore系统,主要有三个层的配置文件组成,持久层由iBatisProduct.xml等配置文件组成;服务层由jdon框架的jdonframework.xml组成;表现层由strutsstruts-config.xmljdonframework.xml组成;剩余代码基本是Model之类实现。

 

 



上一级 首页 下一级


更多Jdon框架专题讨论

JdonFramework作为一个免费开源软件开发平台,可以商用开发大多数数据库应用软件和管理软件: 电子商务软件 在线教育软件 税务软件 Web快速开发软件 财务软件 购物车软件 医院帐务软件 crm software medical software 人事薪资软件payroll software 在线购物软件 销售软件 项目管理软件 房产不动产管理软件 生产软件 PDM软件 制造业软件 仓库软件 采购软件 进销存软件 危险源监控软件 物流软件 超市软件 银行软件 保险软件 汽车软件 医疗软件 电子软件 自动化软件 服装软件 烟草软件 分销管理软件 供应商管理软件

下载源码

框架文档

框架应用系统

演示运行

JiveJdon3

性能测试

Q&A 问答

技术支持

 

 

更多标签...



Jdon框架演示

JiveJdon
源码下载

VIP收费区

历史热点讨论排行榜




google yahoo 新浪ViVi 365Key网摘 天极网摘 CSDN网摘 添加到百度搜藏 POCO网摘





手机 add to google add to yahoo
联系我们 | 关于我们 | 广告联系 | 网站地图 | 设为首页

沪ICP证08026060 如有意见请与我们联系 Powered by JdonFramework
_×
您有新消息