免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
J2EE應(yīng)用系統(tǒng)JPetstore

 J2EE應(yīng)用系統(tǒng)JPetstore

IBatis.comiBATIS-Jpetstore為例,我們使用Jdon框架對(duì)其重構(gòu)成為Jdon-JPetstore,本章開(kāi)發(fā)環(huán)境是Eclipse(本章案也適用其他開(kāi)發(fā)工具),部署運(yùn)行環(huán)境是JBoss。

Eclipse是一個(gè)非常不錯(cuò)的開(kāi)源開(kāi)發(fā)工具,使用Eclipse開(kāi)發(fā)和使用JBuilder將有完全不同的開(kāi)發(fā)方式。我們使用Eclipse基于Jdon框架開(kāi)發(fā)一個(gè)完全Web應(yīng)用,或者可以說(shuō),開(kāi)發(fā)一個(gè)輕量(lightweight)的J2EE應(yīng)用系統(tǒng)。

通過(guò)這個(gè)輕量系統(tǒng)開(kāi)發(fā),說(shuō)明Jdon框架對(duì)完全POJO架構(gòu)的支持,因?yàn)?/span>EJB分布式集群計(jì)算能力,隨著訪問(wèn)量提升,可能需要引入EJB架構(gòu),這時(shí)只要使用EJB session Bean包裝POJO服務(wù)則可以無(wú)縫升級(jí)到EJB。使用Jdon框架可實(shí)現(xiàn)方便簡(jiǎn)單地架構(gòu)升遷。

Eclipse安裝簡(jiǎn)要說(shuō)明

1.下載Eclipse:http://www.eclipse.org 的下載點(diǎn)中選擇tds ISP 比較快。

2.安裝免費(fèi)插件:

編輯Jsp需要lomboz http://www.objectlearn.com/projects/download.jsp

注意對(duì)應(yīng)的Eclipse版本。

編輯XML,使用Xmlbuddy: http://xmlbuddy.com/

基本上這兩個(gè)插件就夠了。

 

如果希望開(kāi)發(fā) Hibernate,插件:

http://www.binamics.com/hibernatesync

 

代碼折疊

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

 

3. 關(guān)鍵學(xué)習(xí)ant的編寫(xiě)build.xml,在build.xml將你的jsp javaclass打包成warjarear就可以。都可以使用antjar打包,build.xml只要參考一個(gè)模板就可以:SimpleJdonFrameworkTest.rar 有一個(gè)現(xiàn)成的,可拷貝到其它項(xiàng)目后修改后就可用.

然后在這個(gè)模板上修改,參考 ant的命令參考:

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

網(wǎng)上有中文版的ant參考,在google搜索就能找到。

關(guān)鍵是學(xué)習(xí)antbuild.xml編輯,SimpleJdonFrameworkTest.rar 有一個(gè)現(xiàn)成的,可拷貝到其它項(xiàng)目后修改后就可用.

ant編譯替代Eclipse的缺省編譯:選擇項(xiàng)目屬性-->Builders ---> new --> Ant Builder --->選擇本項(xiàng)目的build.xml workspace 選擇本項(xiàng)目

eclipse開(kāi)發(fā)就這些,非常簡(jiǎn)單,不象Jbuilder那樣智能化,導(dǎo)致項(xiàng)目目錄很大。eclipse只負(fù)責(zé)源碼開(kāi)發(fā),其它都由ant負(fù)責(zé)

架構(gòu)設(shè)計(jì)要點(diǎn)

Jdon-JPetstore除了保留iBATIS-JPetstore 4.0.5的域模型、持久層ibatis實(shí)現(xiàn)以及Jsp頁(yè)面外,其余部分因?yàn)槭褂昧?/span>Jdon框架而和其有所不同。

保留域模型和Jsp頁(yè)面主要是在不更改系統(tǒng)需求的前提下,重構(gòu)其架構(gòu)實(shí)現(xiàn)為Jdon框架,通過(guò)對(duì)比其原來(lái)的實(shí)現(xiàn)或SpringJPetstore實(shí)現(xiàn),可以發(fā)現(xiàn)Jdon框架的使用特點(diǎn)。

在原來(lái)jpetstore iBatis包會(huì)延伸到表現(xiàn)層,例如它的分頁(yè)查詢(xún)PaginatedListiBatis只是持久層框架,它的作用范圍應(yīng)該只限定在持久層,這是它的專(zhuān)業(yè)范圍,如果超過(guò)范圍,顯得 ….。所以,在Jdon-Jpetstore中將iBatis封裝在持久層(砍掉PaginatedList這只太長(zhǎng)的手),Jdon框架是一種中間層框架,聯(lián)系前后臺(tái)的工作應(yīng)該由Jdon這樣的中間層框架完成。

iBatis 4.0.5版本中,它使用了一個(gè)利用方法映射Reflection的小框架,這樣,將原來(lái)需要在Action實(shí)現(xiàn)方法整入了ActionForm中實(shí)現(xiàn),ActionForm變成了一個(gè)復(fù)雜的對(duì)象:頁(yè)面表單抽象以及與后臺(tái)Service交互,在ActionForm中調(diào)用后臺(tái)服務(wù)是通過(guò)單態(tài)模式實(shí)現(xiàn),這是在一般J2EE開(kāi)發(fā)中忌諱的一點(diǎn),關(guān)于單態(tài)模式的討論可見(jiàn):http://www.jdon.com/jive/article.jsp?forum=91&thread=17578

Jdon框架使用了微容器替代單態(tài),消除了Jpetstore的單態(tài)隱患,而且也簡(jiǎn)化了ActionForm和服務(wù)層的交互動(dòng)作(通過(guò)配置實(shí)現(xiàn))。

用戶(hù)注冊(cè)登陸模塊實(shí)現(xiàn)

用戶(hù)域建模(Model

首先,我們需要從域建模開(kāi)始,建立正確的領(lǐng)域模型,以用戶(hù)賬號(hào)為例,根據(jù)業(yè)務(wù)需求我們確立用戶(hù)賬號(hào)的域模型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是主鍵。

域模型建立好之后,就可以花開(kāi)兩朵各表一支,表現(xiàn)層和持久層可以同時(shí)開(kāi)發(fā),先談?wù)劤志脤雨P(guān)于用戶(hù)模型的CRUD功能實(shí)現(xiàn)。

持久層Account CRUD實(shí)現(xiàn)

主要是用戶(hù)的新增和修改,主要用于注冊(cè)新用戶(hù)和用戶(hù)資料修改。

public interface AccountDao {

  Account getAccount(String username);  //獲得一個(gè)Account

  void insertAccount(Account account);  //新增

  void updateAccount(Account account); //修改

}

持久層可以使用多種技術(shù)實(shí)現(xiàn),例如Jdon框架的JdbcTemp代碼實(shí)現(xiàn)比較方便,如果你的sql語(yǔ)句可能經(jīng)常改動(dòng),使用iBatissql語(yǔ)句XML定義有一定好處,本例程使用Jpetstore原來(lái)的持久層實(shí)現(xiàn)iBatis。見(jiàn)源碼包中的Account.xml

 

表現(xiàn)層Account表單創(chuàng)建(ModelForm

這是在Domain Model建立后最重要的一步,是前臺(tái)表現(xiàn)層Struts開(kāi)發(fā)的起步,表單創(chuàng)建有以下注意點(diǎn):

表單類(lèi)必須繼承com.jdon.model.ModelForm

表單類(lèi)基本是Domain Model的影子,每一個(gè)Model對(duì)應(yīng)一個(gè)ModelForm實(shí)例,所謂對(duì)應(yīng):就是字段名稱(chēng)一致。ModelForm實(shí)例是由Model實(shí)例復(fù)制獲得的。

 

public class AccountForm extends ModelForm {

 

  private String username;

  private String password;

  private String email;

  private String firstName;

  private String lastName;

  ……

 

}

當(dāng)然AccountForm可能有一些與顯示有關(guān)的字段,例如注冊(cè)時(shí)有英文和中文選擇,以及類(lèi)別的選擇,那么增加兩個(gè)字段在AccountForm中:

  private List languages;

  private List categories;

這兩個(gè)字段需要初始化值的,因?yàn)樵?/span>AccountForm對(duì)應(yīng)的Jsp的頁(yè)面中要顯示出來(lái),這樣用戶(hù)才可能進(jìn)行選擇。選擇后的值將放置在專(zhuān)門(mén)的字段中。

有兩種方式初始化這兩個(gè)字段:

1. AccountForm構(gòu)造方法中初始化,前提是:這些初始化值是常量,如:

public AccountForm() {

    languages new ArrayList();

    languages.add("english");

    languages .add("japanese");

}

2.如果初始化值是必須從數(shù)據(jù)庫(kù)中獲取,那么采取前面章節(jié)介紹的使用ModelHandler來(lái)實(shí)現(xiàn),這部分又涉及配置和代碼實(shí)現(xiàn),缺省時(shí)我們考慮通過(guò)jdonframework.xml配置實(shí)現(xiàn)。

 

Account CRUDstruts-config.xml的配置

第一步配置ActionForm

上節(jié)編寫(xiě)了ModelForm代碼,ModelForm也就是strutsActionForm,在struts-config.xml中配置ActionForm如下:

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

第二步配置Action

這需要根據(jù)你的CRUD功能實(shí)現(xiàn)需求配置,例如本例中用戶(hù)注冊(cè)和用戶(hù)修改分開(kāi),這樣,配置兩套ModelViewActionModelSaveAction,具體配置見(jiàn)源碼包中的struts-config-security.xml,這里將struts-config.xml根據(jù)模塊劃分成相應(yīng)的模塊配置,實(shí)現(xiàn)多模塊開(kāi)發(fā),本模塊是用戶(hù)注冊(cè)登陸系統(tǒng),因此取名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> 

.其中有一個(gè)initMethod主要用于AccuntForm對(duì)象的初始化。其他都是增刪改查的常規(guī)實(shí)現(xiàn)。

Account CRUD Jsp頁(yè)面實(shí)現(xiàn)

在編輯頁(yè)面EditAccountForm.jsp中加入:

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

在新增頁(yè)面NewAccountForm.jsp加入:

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

所有的字段都是直接來(lái)自accountFrom

 

整理模塊配置

商品模塊功能完成,struts提供了多模塊開(kāi)發(fā),因此我們可以將這一模塊單獨(dú)保存在一個(gè)配置中:/WEB-INF/struts-config-security.xml,這樣以后擴(kuò)展修改起來(lái)方便。

 

商品查詢(xún)模塊實(shí)現(xiàn)

iBATIS-JPetstore中沒(méi)有單獨(dú)的CategoryForm,而是將三個(gè)ModelCategory、Product、,Item合并在一個(gè)CatalogBean中,這樣做的缺點(diǎn)是拓展性不強(qiáng),將來(lái)這三個(gè)Model也許需要單獨(dú)的ActionForm

由于我們使用Jdon框架的CRUD功能配置實(shí)現(xiàn),因此,不怕細(xì)分這三個(gè)Model帶來(lái)代碼復(fù)雜和瑣碎。

由于原來(lái)的Jpetstore“偷懶”,沒(méi)有實(shí)現(xiàn)Category Product等的CRUD功能,只實(shí)現(xiàn)它們的查詢(xún)功能,因此,我們使用Jdon框架的批量查詢(xún)來(lái)實(shí)現(xiàn)查詢(xún)。

持久層 Product批量查詢(xún)實(shí)現(xiàn)

商品查詢(xún)主要有兩種批量查詢(xún),根據(jù)其類(lèi)別IDCategoryId查詢(xún)所有該商品目錄下所有的商品;根據(jù)關(guān)鍵字搜索符合條件的所有商品,下面以前一個(gè)功能為例子:

iBatis-jpetstore使用PaginatedList作為分頁(yè)的主要對(duì)象,該對(duì)象需要保存到HttpSession中,然后使用PaginatedListNextPage等直接遍歷,這種方法只適合在小數(shù)據(jù)量合適,J2EE編程中不推薦向HttpSession放入大量數(shù)據(jù),不利于cluster

根據(jù)Jdon批量查詢(xún)的持久層要求,批量查詢(xún)需要兩種SQL語(yǔ)句實(shí)現(xiàn):符合條件的ID集合和符合條件的總數(shù):以及單個(gè)Model查詢(xún)。

  //獲得ID集合

 List getProductIDsListByCategory(String categoryId, int pagessize);

  //獲得總數(shù)

  int getProductIDsListByCategoryCount(String categoryId);

  //單個(gè)Model查詢(xún)

  Product getProduct(String productId) ;

這里我們需要更改一下iBatis原來(lái)的Product.xml配置,原來(lái),它設(shè)計(jì)返回的是符合條件的所有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實(shí)現(xiàn),讀取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();

    }  

這樣,結(jié)合配置的iBatis DAOJdon框架批量查詢(xún),在ProductManagerImp中創(chuàng)建PageIterator,當(dāng)然這部分代碼也可以在ProductDao實(shí)現(xiàn),創(chuàng)建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;

表現(xiàn)層Product批量查詢(xún)實(shí)現(xiàn)

    根據(jù)批量查詢(xún)的編程步驟,在表現(xiàn)層主要是實(shí)現(xiàn)ModelListAction繼承、配置和Jsp編寫(xiě),下面分步說(shuō):

    第一步,創(chuàng)建一個(gè)ModelListAction子類(lèi)ProductListAction,實(shí)現(xiàn)兩個(gè)方法:getPageIteratorfindModelByKey,getPageIterator方法如下:

      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);

    }

    由于我們實(shí)現(xiàn)的是查詢(xún)一個(gè)商品目錄下所有商品功能,因此,需要顯示商品目錄名稱(chēng),而前面操作的都是Product模型,所以在顯示頁(yè)面也要加入商品目錄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>

  第三步,編寫(xiě)Category.jsp

  productListForm中取出我們要顯示兩個(gè)模型,一個(gè)是oneModel中的Category;另外一個(gè)是Product Model集合list,Jsp語(yǔ)法如下:

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

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

我們可以顯示商品目錄名稱(chēng)如下:

<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>

加上分頁(yè)標(biāo)簽庫(kù)如下:

  <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>

至此,一個(gè)商品目錄下的所有商品批量查詢(xún)功能完成,由于是基于框架的模板化編程,直接上線運(yùn)行成功率高。

商品搜索批量查詢(xún):

參考上面步驟,商品搜索也可以順利實(shí)現(xiàn),從后臺(tái)到前臺(tái)按照批量查詢(xún)這條線索分別涉及的類(lèi)有:

持久層實(shí)現(xiàn):ProductDao中的三個(gè)方法:

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

 

int searchProductIDsListCount(String keywords); //總數(shù)

 

Product getProduct(String productId) ; //單個(gè)Model

表現(xiàn)層:建立ProductSearchAction類(lèi),配置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>

與前面使用的都是同一個(gè)ActionFormproductListForm

編寫(xiě)SearchProducts .jsp,與Category.jsp類(lèi)似,相同的是ActionForm;不同的是action。

商品條目Item批量查詢(xún)

條目Item批量實(shí)現(xiàn)與Product批量查詢(xún)類(lèi)似:

持久層:ItemDao提供三個(gè)方法:

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

 

  int getItemIDsListByProductCount(String productId);//總數(shù)

 

  Item getItem(String itemId); //單個(gè)Model

表現(xiàn)層:創(chuàng)建一個(gè)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相比,非常類(lèi)似,不同的是Model名稱(chēng)不一樣,一個(gè)是Product一個(gè)是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的配置,非常類(lèi)似,其實(shí)itemListFormproductListForm是同一個(gè)ModelListForm類(lèi)型,可以合并起來(lái)統(tǒng)一命名為listForm,節(jié)省ActionForm的配置。

Product.jsp頁(yè)面與前面的Category.jsp SearchProdcuts.jsp類(lèi)似。

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

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

分頁(yè)顯示:

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

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

    …..

商品條目Item單條查詢(xún)

單個(gè)顯示屬于CRUD中的一個(gè)查詢(xún)功能,我們需要建立Model對(duì)應(yīng)的ModelForm,將Item的字段拷貝到ItemForm中。配置這個(gè)ActionForm如下:

    <form-bean name="itemForm"

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

第二步:因?yàn)檫@個(gè)功能屬于CRUD一種,無(wú)需編程,但是需要配置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>

配置中只要一個(gè)方法getMethod就可以,因?yàn)橹挥玫?/span>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,現(xiàn)在開(kāi)始發(fā)現(xiàn)一個(gè)問(wèn)題,Item.jsp中不只是顯示Item信息,還有Product信息,而前面我們定義的是Item信息,如果使得Item.jsp顯示Product信息呢,這就從設(shè)計(jì)起源Domain Model上考慮,在ItemModel中有Product引用:

  private Product product;

  public Product getProduct() { return product; }

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

ItemProduct的多對(duì)一關(guān)系其實(shí)應(yīng)該在域建模開(kāi)始就考慮到了。

那么,我們只要在持久層查詢(xún)Item時(shí),能夠?qū)⑵渲械?/span>Product字段查詢(xún)就可以。在持久層的iBatisProduct.xml實(shí)現(xiàn)有下列SQL語(yǔ)句:

  <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>

這段語(yǔ)法實(shí)際在查詢(xún)Item時(shí),已經(jīng)將Product查詢(xún)出來(lái),這樣Item Model中已經(jīng)有Product數(shù)據(jù),因?yàn)?/span>ActionFormModel映射,因此,前臺(tái)Jsp也可以顯示Product數(shù)據(jù)。

Item.jsp中,進(jìn)行下面定義:

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

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

itemFormproduct屬性定義為product即可;這樣不必大幅度修改原來(lái)的Item.jsp了。

整理模塊配置

商品模塊功能完成,struts提供了多模塊開(kāi)發(fā),因此我們可以將這一模塊單獨(dú)保存在一個(gè)配置中:/WEB-INF/struts-config-catalog.xml,這樣以后擴(kuò)展修改起來(lái)方便。

 

購(gòu)物車(chē)模塊實(shí)現(xiàn)

購(gòu)物車(chē)屬于一種有狀態(tài)數(shù)據(jù),也就是說(shuō),購(gòu)物車(chē)的scope生命周期是用戶(hù),除非這個(gè)用戶(hù)離開(kāi),否則購(gòu)物車(chē)一直在內(nèi)存中存在。

有態(tài)POJO服務(wù)

現(xiàn)在有兩種解決方案:

第一,將購(gòu)物車(chē)狀態(tài)作為數(shù)據(jù)類(lèi),保存到ActionForm中,設(shè)置scopesession,這種形式下,對(duì)購(gòu)物車(chē)的數(shù)據(jù)操作如加入條目等實(shí)現(xiàn)不很方便,iBatis-jpetstore 4.0.5就采取這個(gè)方案,在數(shù)據(jù)類(lèi)Cart中存在大量數(shù)據(jù)操作方法,那么Cart這個(gè)類(lèi)到底屬于數(shù)據(jù)類(lèi)Model?還是屬于處理服務(wù)類(lèi)呢?

在我們J2EE編程中,通常使用兩種類(lèi)來(lái)實(shí)現(xiàn)功能,一種是數(shù)據(jù)類(lèi),也就是我們?cè)O(shè)計(jì)的Model;一種是服務(wù)類(lèi),如POJO服務(wù)或EJB服務(wù),服務(wù)屬于一種處理器,處理過(guò)程。使用這兩種分類(lèi)比較方便我們來(lái)解析業(yè)務(wù)需求,EJB中實(shí)體BeanSession Bean也是屬于這兩種類(lèi)型。

iBatis-jpetstore 4.0.5則是將服務(wù)和數(shù)據(jù)類(lèi)混合在一個(gè)類(lèi)中,這也屬于一種設(shè)計(jì),但是我們認(rèn)為它破壞了解決問(wèn)題的規(guī)律性,而且造成數(shù)據(jù)和操作行為耦合性很強(qiáng),在設(shè)計(jì)模式中我們還使用橋模式來(lái)分離抽象和行為,因此這種做法可以說(shuō)是反模式的。那么我們采取數(shù)據(jù)類(lèi)和服務(wù)分離的方式方案來(lái)試試看:

第二.購(gòu)物車(chē)功能主要是對(duì)購(gòu)物車(chē)這個(gè)ModelCRUD,與通常的CRUD區(qū)別是,數(shù)據(jù)是保存到HttpSession,而不是持久化到數(shù)據(jù)庫(kù)中,是數(shù)據(jù)狀態(tài)保存不同而已。所以如果我們實(shí)現(xiàn)一個(gè)CartService,它提供addupdatedelete等方法,只不過(guò)操作對(duì)象不是數(shù)據(jù)庫(kù),而是其屬性為購(gòu)物車(chē)Cart,然后將該CarService實(shí)例保存到HttpSession,實(shí)現(xiàn)每個(gè)用戶(hù)一個(gè)CartService實(shí)例,這個(gè)我們成為有狀態(tài)的POJO服務(wù)。

這種處理方式類(lèi)似EJB架構(gòu)處理,如果我們業(yè)務(wù)服務(wù)層使用EJB,那么使用有態(tài)會(huì)話Bean實(shí)現(xiàn)這個(gè)功能。

現(xiàn)在問(wèn)題是,Jdon框架目前好像沒(méi)有提供有狀態(tài)POJO服務(wù)實(shí)例的獲得,那么我們自己在WebAppUtil.getService獲得實(shí)例后,保存到HttpSession中,下次再到HttpSession中獲得,這種有狀態(tài)處理需要表現(xiàn)層更多代碼,這就不能使用Jdon框架的CRUD配置實(shí)現(xiàn)了,需要我們代碼實(shí)現(xiàn)ModelHandler子類(lèi)。

考慮到可能在其他應(yīng)用系統(tǒng)還有這種需求,那么能不能將有狀態(tài)的POJO服務(wù)提煉到Jdon框架中呢?關(guān)鍵使用什么方式加入框架,因?yàn)檫@是設(shè)計(jì)目標(biāo)服務(wù)實(shí)例的獲得,框架主要流程代碼又不能修改,怎么辦?

Jdon框架的AOP功能在這里顯示了強(qiáng)大靈活性,我們可以將有狀態(tài)的POJO服務(wù)實(shí)例獲得作為一個(gè)攔截器,攔截在原來(lái)POJO服務(wù)實(shí)例獲得之前。在Jdon框架設(shè)計(jì)中,目標(biāo)服務(wù)實(shí)例的獲得一般只有一次。

創(chuàng)建有狀態(tài)POJO服務(wù)攔截器com.jdon.aop.interceptor. StatefulInterceptor,再創(chuàng)建一個(gè)空接口:com.jdon.controller.service.StatefulPOJOService,需要實(shí)現(xiàn)有狀態(tài)實(shí)例的POJO類(lèi)只要繼承這個(gè)接口就可以。

配置aspect.xml,加入這個(gè)攔截器:

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

pointcut="pojoServices" />      

這里需要注意的是:你不能讓一個(gè)POJO服務(wù)類(lèi)同時(shí)繼承Poolable,然后又繼承Stateful,因?yàn)檫@是兩種不同的類(lèi)型,前者適合無(wú)狀態(tài)POJO;后者適合CartService這樣有狀態(tài)處理;這種選擇和EJB的有態(tài)/無(wú)態(tài)選擇是一樣的。

ModelService設(shè)計(jì)

購(gòu)物車(chē)模塊主要圍繞域模型Cart展開(kāi),需要首先明確Cart是一個(gè)什么樣的業(yè)務(wù)模型,購(gòu)物車(chē)頁(yè)面是類(lèi)似商品條目批量查詢(xún)頁(yè)面,不過(guò)購(gòu)物車(chē)中顯示的不但是商品條目,還有數(shù)量,那么我們專(zhuān)門(mén)創(chuàng)建一個(gè)Model來(lái)指代它,取名為CartItemCartItemItem父集,多了一個(gè)數(shù)量。

這樣購(gòu)物車(chē)頁(yè)面就是CartItem的批量查詢(xún)頁(yè)面,然后還有CartItemCRUD操作,所以購(gòu)物車(chē)功能主要是CartItemCRUD和批量查詢(xún)功能。

iBatis 4.0.5原來(lái)設(shè)計(jì)了專(zhuān)門(mén)Cart Model,其實(shí)這個(gè)Cart主要是一個(gè)功能類(lèi),因?yàn)樗臄?shù)據(jù)項(xiàng)只有一個(gè)MapList,這根本不能代表業(yè)務(wù)需求中的一個(gè)模型。雖然iBatis 4..0.5也可以自圓其說(shuō)實(shí)現(xiàn)了購(gòu)物車(chē)功能,但是這種實(shí)現(xiàn)是隨心所欲,無(wú)規(guī)律性可遵循,因而以后維護(hù)起來(lái)也是困難,維護(hù)人員理解困難,修改起來(lái)也沒(méi)有章程可循,甚至亂改一氣。

CartItem可以使用iBatis原來(lái)的CartItem,這樣也可保持Cart.jsp頁(yè)面修改量降低。刪除原來(lái)的Cart這個(gè)Model,建立對(duì)應(yīng)的CartService,實(shí)現(xiàn)原來(lái)的Cart一些功能。

public interface CartService {

       CartItem getCartItem(String itemId);

       void addCartItem(EventModel em);

       void updateCartItem(EventModel em);

       void deleteCartItem(EventModel em);

       PageIterator getCartItems();

}

CartServiceImpCartService子類(lèi),它是一個(gè)有狀態(tài)POJO服務(wù),代碼簡(jiǎn)要如下:

public class CartServiceImp implements CartService, Stateful{

    private ProductManager productManager;

    //將原來(lái)iBatis Cart類(lèi)中兩個(gè)屬性移植到CartServiceImp

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

    private  List itemList = new ArrayList();

 

    public CartServiceImp(ProductManager productManager) {

        super();

        this.productManager = productManager;

    }

    ……

}

itemMap是裝載CartItem的一個(gè)Map,是類(lèi)屬性,由于CartServiceImp是有狀態(tài)的,每個(gè)用戶(hù)一個(gè)實(shí)例,那么也就是每個(gè)用戶(hù)有自己的itemMap列表,也就是購(gòu)物車(chē)。

CartServiceImp中的 getCartItemIDs是查詢(xún)購(gòu)物車(chē)當(dāng)前頁(yè)面的購(gòu)物條目,屬于批量分頁(yè)查詢(xún)實(shí)現(xiàn),這里有一個(gè)需要考量的地方,是getCartItems方法還是getCartItemIDs方法?也就是返回CartIem的實(shí)例集合還是CartItemItemId集合?按照前面標(biāo)準(zhǔn)的Jdon框架批量分頁(yè)查詢(xún)實(shí)現(xiàn),應(yīng)該返回CartItemItemId集合,然后由Jdon框架的ModelListAction根據(jù)ItemId首先從緩存中獲得CartItem實(shí)例,但是本例CartItem本身不是持久化在數(shù)據(jù)庫(kù),而也是內(nèi)存HttpSession中,所以ModelListAction這種流程似乎沒(méi)有必要。

如果將來(lái)業(yè)務(wù)需求變化,購(gòu)物車(chē)狀態(tài)不是保存在內(nèi)存而是數(shù)據(jù)庫(kù),這樣,用戶(hù)下次登陸時(shí),可以知道他上次購(gòu)物車(chē)?yán)锏纳唐窏l目,那么采取Jdon框架標(biāo)準(zhǔn)查詢(xún)方案還是有一定擴(kuò)展性的。

這里,我們就事論事,采取返回CartIem的實(shí)例集合,展示一下如何靈活應(yīng)用Jdon框架的批量查詢(xún)功能。下面是CartServiceImp getCartItems方法詳細(xì)代碼:

 public PageIterator getCartItems(int start, int count) {

        int offset = itemList.size() - start; //獲得未顯示的總個(gè)數(shù)

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

        List pageList = new ArrayList(pageCount); //當(dāng)前頁(yè)面記錄集合

        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方法是從購(gòu)物車(chē)所有條目itemList中查詢(xún)獲得當(dāng)前頁(yè)面的條目,并創(chuàng)建一個(gè)PageIterator。

注意,現(xiàn)在這個(gè)PageIteratorkeys屬性中裝載的不是數(shù)據(jù)ID集合,而是完整的CartItem集合,因?yàn)樯厦娲a中pageList中對(duì)象是從itemList中獲得,而itemList中裝載的都是CartItem。

表現(xiàn)層購(gòu)物車(chē)顯示功能

由于PageIterator中封裝的是完整Model集合,而不是ID集合,所以現(xiàn)在表現(xiàn)層有兩種方案,繼承框架的ModelListAction;或重新自己實(shí)現(xiàn)一個(gè)Action,替代ModelListAction。

這里使用繼承框架的ModelListAction方案,巧妙地實(shí)現(xiàn)我們的目的,省卻編碼:

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; //因?yàn)?/span>key不是主鍵,而是完整的Model,直接返回

    }

   

    protected boolean isEnableCache(){

        return false;  //無(wú)需緩存,CartItem本身實(shí)際是在內(nèi)存中。

    }

 

}

配置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>

上面是購(gòu)物車(chē)顯示實(shí)現(xiàn),只要調(diào)用/shop/viewCart.shtml就可以顯示購(gòu)物車(chē)了。

Cart.jsp頁(yè)面插入下面標(biāo)簽:

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

    ….

</logic:iterate>

分頁(yè)顯示標(biāo)簽如下:

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

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

<MultiPages:index />

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

</MultiPages:pager>

 

 

購(gòu)物車(chē)新增刪除條目功能

前面完成了購(gòu)物車(chē)顯示功能,下面是設(shè)計(jì)購(gòu)物車(chē)的新增和刪除、修改功能。

參考Jdon框架的CRUD功能實(shí)現(xiàn),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>

在這個(gè)配置中,只有新增和刪除方法,修改方法沒(méi)有,因?yàn)橘?gòu)物車(chē)修改主要是其中商品條目的數(shù)量修改,它不是逐條修改,而是一次性批量修改,這里的ModelCartItem,這是購(gòu)物車(chē)?yán)锏囊粋€(gè)條目,因此如果這里寫(xiě)修改,也只是CartItem一個(gè)條目的修改,不符合我們要求。下面專(zhuān)門(mén)章節(jié)實(shí)現(xiàn)這個(gè)修改。

表現(xiàn)層主要是配置,沒(méi)有代碼,代碼都依靠cartService中的addCartItemdeleteCartItem實(shí)現(xiàn):例如:

    public void addCartItem(EventModel em) {

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

        String workingItemId = cartItem.getWorkingItemId();

        ……

    }

注意addCartItem中從EventModel實(shí)例中獲取的ModelCartItem,這與我們?cè)?/span>jdonframework.xml中上述定義的Model類(lèi)型是統(tǒng)一的。

Struts-config.xml中定義是CRUD的標(biāo)準(zhǔn)定義,注意,這里只有ModelSaveAction無(wú)需ModelViewAction,因?yàn)閷⑸唐窏l目加入或刪除購(gòu)物車(chē)這個(gè)功能沒(méi)有專(zhuān)門(mén)的顯示頁(yè)面:

    <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>

注意,調(diào)用刪除功能時(shí),需要附加action參數(shù):

/shop/removeItemFromCart.shtml?action=delete

/shop/addItemToCart.shtml是新增屬性,缺省后面無(wú)需跟參數(shù)。

購(gòu)物車(chē)條目批量修改功能

上面基本完成了購(gòu)物車(chē)主要功能;購(gòu)物車(chē)功能一個(gè)復(fù)雜性在于其顯示功能和修改功能合并在一起,修改功能是指修改購(gòu)物車(chē)?yán)锼猩唐窏l目的數(shù)量。

既然有修改功能,而且這個(gè)修改功能比較特殊,我們需要設(shè)計(jì)一個(gè)獨(dú)立的ActionForm,用來(lái)實(shí)現(xiàn)商品條目數(shù)量的批量修改。

首先設(shè)計(jì)一個(gè)ActionFormModelForm),該ModelForm主要用來(lái)實(shí)現(xiàn)購(gòu)物車(chē)條目數(shù)量的更改,取名為CartItemsForm,其內(nèi)容如下:

public class CartItemsForm extends ModelForm {

    private String[] itemId;

    private int[]quantity;

    private BigDecimal totalCost;

    …..

}

itemIdquantity設(shè)計(jì)成數(shù)組,這樣,Jsp頁(yè)面可以一次性提交多個(gè)itemIdquantity數(shù)值。

現(xiàn)在CartItemsForm已經(jīng)包含前臺(tái)jsp輸入的數(shù)據(jù),我們還是將其傳遞遞交到服務(wù)層實(shí)現(xiàn)處理。因此建立一個(gè)Model,內(nèi)容與CartItemsForm類(lèi)似,這里的Model名為CartItems,實(shí)際是一個(gè)傳輸對(duì)象。

public class CartItems extends Model{

    private String[] itemId;

    private int[] quantity;

    private BigDecimal totalCost;

    ……

}

表現(xiàn)層在jdonframework.xml定義配置就無(wú)需編碼,配置如下:

                   <model key=" "

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

                            <actionForm name="cartItemsForm"/>

                            <handler>

                                     <service ref="cartService">

                                               <updateMethod name="updateCartItems"/>

                                     </service>

                            </handler>

                   </model>

上面配置中,ModelCartItems,ActionFormcartItemsForm,這兩個(gè)是專(zhuān)門(mén)為批量修改設(shè)立的。只有一個(gè)方法updateMethod。因?yàn)樵谶@個(gè)更新功能中,沒(méi)有根據(jù)主鍵從數(shù)據(jù)庫(kù)查詢(xún)Model的功能,因此,這里modelkey可以為空值。

服務(wù)層CartServiceImpupdateCartItems方法實(shí)現(xiàn)購(gòu)物車(chē)條目數(shù)量更新:

    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]);//逐條更新購(gòu)物車(chē)中的數(shù)量

            }

        } catch (Exception ex) {

            logger.error(ex);

        }

    }

 

注意updateCartItems中從EventModel取出的是CartItems,和前面addCartItem方法中取出的是CartItem Model類(lèi)型不一樣,這是因?yàn)檫@里我們?cè)?/span>jdonframework.xml中定義與updateCartItems相對(duì)應(yīng)的ModelCartItems.

最后一步工作是Cat.jsp中加入CartItemsForm,能夠在購(gòu)物車(chē)顯示頁(yè)面有一個(gè)表單提交,客戶(hù)按提交按鈕,能夠立即實(shí)現(xiàn)當(dāng)前頁(yè)面購(gòu)物車(chē)數(shù)量的批量修改。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實(shí)際是ModelSaveAction時(shí),框架才知道操作性質(zhì)。

購(gòu)物車(chē)總價(jià)顯示功能

最后,還有一個(gè)功能需要完成,在購(gòu)物車(chē)顯示時(shí),需要顯示當(dāng)前購(gòu)物車(chē)的總價(jià)格,注意不是顯示當(dāng)前頁(yè)面的總價(jià)格,所以無(wú)法在Cart.jsp直接實(shí)現(xiàn),必須遍歷購(gòu)物車(chē)?yán)锼?/span>CartItem計(jì)算總數(shù)。

該功能是購(gòu)物車(chē)顯示時(shí)一起實(shí)現(xiàn),購(gòu)物車(chē)顯示是通過(guò)CartListAction實(shí)現(xiàn)的,這個(gè)CartListAction實(shí)際是生成一個(gè)ModelListForm,如果ModelListForm能夠增加一個(gè)getTotalPrice方法就可以,因此有兩種實(shí)現(xiàn)方式:繼承ModelListForm加入自己的getTotalPrice方法;第二種無(wú)需再實(shí)現(xiàn)自己的ModelListForm,ModelListForm可以攜帶一個(gè)Model,通過(guò)setOneModel即可,這個(gè)方法是在ModelListAction的子類(lèi)CartListAction可以override覆蓋實(shí)現(xiàn)的,在CartListAction加入下列方法:

  protected Model setOneModel(HttpServletRequest request){

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

        CartItems cartItems = new CartItems();

        cartItems.setTotalCost(cartService.getSubTotal());       

        return cartItems;

    }

我們使用空的CartItems作為攜帶價(jià)格總數(shù)的Model,然后在Cart.jsp中再取出來(lái)顯示:

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

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

將當(dāng)前頁(yè)面listForm中屬性oneModel定義為cartItems,它實(shí)際是我們定義的CartItems

下一行取出總價(jià)即可。

用戶(hù)喜歡商品列表功能

在顯示購(gòu)物車(chē)時(shí),需要一起顯示該用戶(hù)喜歡的商品列表,很顯然這是一個(gè)批量分頁(yè)查詢(xún)實(shí)現(xiàn),但是它有些特殊,它首先顯示的第一頁(yè)不是由URL調(diào)用的,而是嵌入在購(gòu)物車(chē)顯示中,那么只能在購(gòu)物車(chē)顯示頁(yè)面的ModellistForm中做文章。

在上節(jié)中,在CartListActionsetOneModel方法中,使用CartItems作為價(jià)格總數(shù)的載體,現(xiàn)在恐怕我們也要將之作為本功能實(shí)現(xiàn)載體。

還有一種實(shí)現(xiàn)載體,就是其他scopesessionActionForm,AccountForm很適合做這樣的載體,而且和本功能意義非常吻合,所以在AccountForm/Account中增加一個(gè)myList字段,在myList字段中,放置的是該用戶(hù)喜歡的商品Product集合,注意不必放置Product的主鍵集合,因?yàn)槲覀冎灰@示用戶(hù)喜歡商品的第一頁(yè),這一頁(yè)是嵌入購(gòu)物車(chē)顯示頁(yè)面中,所以第一頁(yè)顯示的個(gè)數(shù)是由程序員可事先在程序中定義。

這樣在Account獲得時(shí),一起將myList集合值獲得。

訂單模塊實(shí)現(xiàn)

我們還是從域模型開(kāi)始,Order是訂單模塊的核心實(shí)體,其內(nèi)容可以確定如下:

public class Order extends Model {

 

  /* Private Fields */

 

  private int orderId;

  private String username;

  private Date orderDate;

  private String shipAddress1;

  private String shipAddress2;

  …..

}

第二步,建立與Model對(duì)應(yīng)的ModelForm,我們可以稱(chēng)之為邊界模型,代碼從Order拷貝過(guò)來(lái)即可。當(dāng)然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的業(yè)務(wù)服務(wù)接口,如下:

public interface OrderService {

       void insertOrder(Order order);

       Order getOrder(int orderId);

       List getOrdersByUsername(String username);

}

第四步,實(shí)現(xiàn)OrderServicePOJO子類(lèi):OrderServiceImp

第五步,表現(xiàn)層實(shí)現(xiàn),本步驟可和第四步同時(shí)進(jìn)行。

OrderService中有訂單的插入創(chuàng)建功能,我們使用Jdon框架的CRUDcreate配置實(shí)現(xiàn),配置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>

第六步:根據(jù)逐個(gè)實(shí)現(xiàn)界面功能,訂單的第一個(gè)功能創(chuàng)建一個(gè)新的訂單,在新訂單頁(yè)面NewOrderForm.jsp推出之前,這個(gè)頁(yè)面的ActionForm已經(jīng)被初始化,是根據(jù)購(gòu)物車(chē)等Cart其他Model數(shù)據(jù)初始化合成的。

新訂單頁(yè)面初始化

根據(jù)Jdon框架中CRUD功能實(shí)現(xiàn),初始化一個(gè)ActionForm有兩種方法:一繼承ModelHandler實(shí)現(xiàn)initForm方法;第二通過(guò)jdonframework.xmlinitMethod方法配置。

這兩個(gè)方案選擇依據(jù)是根據(jù)用來(lái)初始化的數(shù)據(jù)來(lái)源什么地方。

訂單表單初始化實(shí)際是來(lái)自購(gòu)物車(chē)信息或用戶(hù)賬號(hào)信息,這兩個(gè)都事先保存在HttpSession中,購(gòu)物車(chē)信息是通過(guò)有態(tài)CartService實(shí)現(xiàn)的,所以這些數(shù)據(jù)來(lái)源是和request相關(guān),那么我們選擇第一個(gè)方案。

繼承ModelHandler之前,我們可以考慮首先繼承ModelHandler的子類(lèi)XmlModelHandler,只要繼承initForm一個(gè)方法即可,這樣節(jié)省其他方法編寫(xiě)實(shí)現(xiàn)。

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繼承后,因?yàn)檫@使用了JdonCRUD功能實(shí)現(xiàn),這里我們只使用到CRUD中的創(chuàng)建功能,因此,findModelBykey方法就無(wú)需實(shí)現(xiàn),或者可以在jdonframework.xml中配置該方法實(shí)現(xiàn)。

考慮到在initForm執(zhí)行后,需要推出一個(gè)NewOrderForm.jsp頁(yè)面,這是一個(gè)新增性質(zhì)的頁(yè)面。所以在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>      

訂單確認(rèn)流程

新的訂單頁(yè)面推出后,用戶(hù)需要經(jīng)過(guò)兩個(gè)流程才能確認(rèn)保存,這兩個(gè)流程是填寫(xiě)送貨地址以及再次完整確認(rèn)。這兩個(gè)流程實(shí)現(xiàn)的動(dòng)作非常簡(jiǎn)單,就是將OrderForm中的shippingAddressRequired字段和confirm字段賦值,相當(dāng)于簡(jiǎn)單的開(kāi)關(guān),這是一個(gè)很簡(jiǎn)單的動(dòng)作,可以有兩種方案:直接在jsp表單中將這兩個(gè)值賦值;直接使用strutsAction實(shí)現(xiàn)。后者需要編碼,而且不是非有這個(gè)必要,只有第一個(gè)方案行不通時(shí)才被迫實(shí)現(xiàn)。

第一步:填寫(xiě)送貨地址

使用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>

這是實(shí)現(xiàn)送貨地址頁(yè)面的填寫(xiě),使用的還是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"/>

第二步:確認(rèn)訂單

類(lèi)似上述步驟,配置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>

第三步:下面是創(chuàng)建數(shù)據(jù)保存功能實(shí)現(xiàn):

    <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實(shí)現(xiàn)的,這里有兩種方式:配置方式:在jdonframework.xml中配置了方法插入;第二種是實(shí)現(xiàn)代碼,這里原本可以使用配置方式實(shí)現(xiàn),但是因?yàn)樵诠δ苌嫌幸螅涸谟唵伪4婧?,需要清除?gòu)物車(chē)數(shù)據(jù),因此只能使用代碼實(shí)現(xiàn)方式,在ModelHandler中實(shí)現(xiàn)子類(lèi)方法serviceAction

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

   try {

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

cartService.clear(); //清楚購(gòu)物車(chē)數(shù)據(jù)

 

      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);

   }

}

用戶(hù)訂單列表

用戶(hù)查詢(xún)自己的訂單列表功能很明顯可以使用Jdon框架的批量查詢(xún)事先。

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

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

建立繼承ModelListAction子類(lèi)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)

….

}

根據(jù)Jdon批量查詢(xún)要求,使用iBatis實(shí)現(xiàn)返回ID集合以及符合條件的總數(shù)。

最后編寫(xiě)ListOrders.jsp,兩個(gè)語(yǔ)法:logic:iterator MultiPages

配置jdon框架啟動(dòng)

目前我們有四個(gè)struts-config.xml,前面每個(gè)模塊一個(gè)配置:

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

/WEB-INF/struts-config-catalog.xml  商品相關(guān)配置

/WEB-INF/struts-config-security.xml 用戶(hù)相關(guān)配置

/WEB-INF/struts-config-cart.xml 購(gòu)物車(chē)相關(guān)配置

/WEB-INF/struts-config-order.xml 訂單相關(guān)配置

本項(xiàng)目只有一個(gè)jdonframework.xml,當(dāng)然我們也可以創(chuàng)建多個(gè)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中原來(lái)的配置過(guò)于擴(kuò)張(從持久層擴(kuò)張到業(yè)務(wù)層),AccountDao每個(gè)實(shí)例獲得都需要通過(guò)daoManager.getDao這樣形式,而使用Jdon框架后,AccountDaoDAO實(shí)例獲得無(wú)需特別語(yǔ)句,我們只要在AccountService直接引用AccountDao接口,至于AccountDao的具體實(shí)例,通過(guò)Ioc注射進(jìn)入即可。

因此,在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需要兩個(gè)構(gòu)造方法實(shí)例,這兩個(gè)中有一個(gè)是AccountDao。

按照iBatis原來(lái)的AccountDao子類(lèi)AccountSqlMapDao有一個(gè)構(gòu)造方法參數(shù)是DaoManager,但是我們無(wú)法生成自己的DaoManager實(shí)例,因?yàn)?/span>DaoManager是由dao.xml配置文件讀取后生成的,這是一個(gè)動(dòng)態(tài)實(shí)例;那只有更改AccountSqlMapDao構(gòu)造方法了。

根源在于BaseSqlMapDao類(lèi),BaseSqlMapDao是一個(gè)類(lèi)似JDBC模板類(lèi),每個(gè)Dao都繼承它,現(xiàn)在我們修改BaseSqlMapDao如下:

public class BaseSqlMapDao extends DaoTemplate implements SqlMapExecutor{

    …..

}

BaseSqlMapDaoXML配置和JDBC模板的結(jié)合體,在這個(gè)類(lèi)中,這兩者搭配在一起,在其中實(shí)現(xiàn)SqlMapExecutor各個(gè)子方法。

我們?cè)賱?chuàng)建一個(gè)DaoManagerFactory,專(zhuān)門(mén)根據(jù)配置文件創(chuàng)建DaoManager實(shí)例:

主要方法如下:

Reader reader = Resources.getResourceAsReader(daoResource);

daoManager = DaoManagerBuilder.buildDaoManager(reader);

其中daoResourcedao.xml配置文件,這個(gè)配置是在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>

這樣,我們可以通過(guò)改變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中,我們只配置一個(gè)JDBC模板,而不是將所有的如AccountDao配置其中,因?yàn)槲覀冃枰?/span>iBatis只是它的JDBC模板,實(shí)現(xiàn)持久層方便的持久化,僅此而已!

DaoManagerFactory為我們生產(chǎn)了DaoManager實(shí)例,那么如何賦值到BaseSqlMapDao中,我們?cè)O(shè)計(jì)一個(gè)創(chuàng)建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);

    }

 

}

通過(guò)getSqlMapDaoTemp方法,由DaoManager.getDao方法獲得BaseSqlMapDao實(shí)例,BaseSqlMapDao的接口是SqlMapExecutor,這樣我們通過(guò)DaoManager獲得一個(gè)JDBC模板SqlMapExecutor的實(shí)例。

這樣,在AccountDao各個(gè)子類(lèi)AccountSqlMapDao中,我們只要通過(guò)SqlMapDaoTemplateFactory獲得SqlMapExecutor實(shí)例,委托SqlMapExecutor實(shí)現(xiàn)JDBC操作,如下:

public class AccountSqlMapDao implements AccountDao {

  //iBatis數(shù)據(jù)庫(kù)操作模板

  private SqlMapExecutor sqlMapDaoTemplate;

    //構(gòu)造方法

  public AccountSqlMapDao(SqlMapDaoTemplateFactory sqlMapDaoTemplateFactory) {

      sqlMapDaoTemplate = sqlMapDaoTemplateFactory.getSqlMapDaoTemp();

  }

 //查詢(xún)數(shù)據(jù)庫(kù)

  public Account getAccount(String username) throws SQLException{

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

  }

 

 

部署調(diào)試

當(dāng)在JBossTomcat控制臺(tái) 或者日志文件中出現(xiàn)下面字樣標(biāo)識(shí)Jdon框架安裝啟動(dòng)成功:

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

Jdon框架啟動(dòng)成功后,以后出現(xiàn)的錯(cuò)誤基本是粗心大意的問(wèn)題,仔細(xì)分析會(huì)很快找到原因,相反,如果編程時(shí)仔細(xì)慢一點(diǎn),則后面錯(cuò)誤出現(xiàn)概率很小。

使用Jdon框架開(kāi)發(fā)Jpetstore, 一次性調(diào)試通過(guò)率高,一般問(wèn)題都是存在數(shù)據(jù)庫(kù)訪問(wèn)是否正常,一旦正常,主要頁(yè)面就出來(lái)了,其中常見(jiàn)問(wèn)題是jsp頁(yè)面和ActionForm的字段不對(duì)應(yīng),如jsp頁(yè)面顯示如下錯(cuò)誤:

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

表示在OrderForm中沒(méi)有字段creditCardTypes,或者有此字段,但是大小寫(xiě)錯(cuò)誤等粗心問(wèn)題。

如果jsp頁(yè)面或后臺(tái)log記錄顯示:

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

一般這是由于前面出錯(cuò)導(dǎo)致,根據(jù)記錄向前搜索,搜索到第一個(gè)出錯(cuò)記錄:

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

第一個(gè)出錯(cuò)是在MethodConstructor報(bào)錯(cuò),沒(méi)有insertOrder方法,根據(jù)上面一行是orderService,那么檢查orderService代碼看看有無(wú)insertOrder

public interface OrderService {

       void insertOrder(Order order);

       Order getOrder(int orderId);

       List getOrdersByUsername(String username);

}

OrderService接口中是有insertOrder方法,那么為什么報(bào)錯(cuò)沒(méi)有呢?仔細(xì)檢查一下,是不是insertOrder的方法參數(shù)有問(wèn)題,哦, 因?yàn)?/span>orderService的調(diào)用是通過(guò)jdonframework.xml下面配置進(jìn)行的:

         <model key="orderId"

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

                            <actionForm name="orderForm"/>

                            <handler>

                                     <service ref="orderService">

                                               <createMethod name="insertOrder"/>

                                     </service>

                            </handler>

                   </model>

而根據(jù)Jdon框架要求,使用配置實(shí)現(xiàn)ModelHandler,則要求OrderServiceinsertOrder方法參數(shù)必須是EventModel,更改OrderServiceinsertOrder方法如下:

public interface OrderService {

    void insertOrder(EventModel em);

}

同時(shí),修改OrderService的子類(lèi)代碼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頁(yè)面顯示出錯(cuò)信息,db.error是在本項(xiàng)目的application.properties中配置的。

關(guān)于本次頁(yè)面出錯(cuò)問(wèn)題,還有更深緣由,因?yàn)槲覀冊(cè)?/span>jdonframework.xml中中定義了createMethod,而根據(jù)前面已經(jīng)有ModelHandler子類(lèi)代碼OrderHandler實(shí)現(xiàn),所以,這里不用配置實(shí)現(xiàn),jdonframework.xml的配置應(yīng)該如下:

                   <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我們使用代碼調(diào)用了OrderServiceinsertOrder方法,如果使用這樣代碼調(diào)用,無(wú)需要求OrderServiceinsertOrder的參數(shù)是EventModel了。

 

 

總結(jié)

Jpetstore整個(gè)開(kāi)發(fā)大部分基于Jdon框架開(kāi)發(fā),特別是表現(xiàn)層,很少直接接觸使用struts原來(lái)功能,Jdon框架的表現(xiàn)層架構(gòu)基本讓程序員遠(yuǎn)離了struts的煩瑣開(kāi)發(fā)過(guò)程,又保證了strutsMVC實(shí)現(xiàn)。

Jpetstore中只有SignonAction這個(gè)類(lèi)是直接繼承strutsDispatchAction,這個(gè)功能如果使用基于J2EE容器的安全認(rèn)證實(shí)現(xiàn)(見(jiàn)JdonNews),那么Jpetstore全部沒(méi)有用到strutsAction,無(wú)需編寫(xiě)Action代碼;ActionForm又都是Model的拷貝,ActionActionFormstruts編碼的兩個(gè)主要部分,這兩個(gè)部分被Jdon框架節(jié)省后,整個(gè)J2EEWeb層開(kāi)發(fā)方便快速,而且容易得多。

這說(shuō)明Jdon框架確實(shí)是一款快速開(kāi)發(fā)J2EE工具,而且是非常輕量的。

縱觀Jpetstore系統(tǒng),主要有三個(gè)層的配置文件組成,持久層由iBatisProduct.xml等配置文件組成;服務(wù)層由jdon框架的jdonframework.xml組成;表現(xiàn)層由strutsstruts-config.xmljdonframework.xml組成;剩余代碼基本是Model之類(lèi)實(shí)現(xiàn)。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Java企業(yè)系統(tǒng)架構(gòu)選擇考量
數(shù)據(jù)通用性操作框架的工作原理
Struts2教程
第一節(jié)---配置struts運(yùn)行環(huán)境
Struts2的配置文件——struts.xml
struts2.xml
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服