spring + ibatis jpetstore 摘要

来源:互联网 发布:上海灵娱网络怎么样 编辑:程序博客网 时间:2024/05/12 06:38
 
spring + ibatis jpetstore 摘要
 
DB layer涉及的package:
org.springframework.samples.jpetstore.dao 包含dao interface
org.springframework.samples.jpetstore.dao.ibatis 包含dao interface ibatis implement
org.springframework.samples.jpetstore.dao.ibatis.maps (!重要)定义了ibatis pojo与db table的mapping xml file
org.springframework.samples.jpetstore.dao.domain 包含dao ibatis implement使用的pojo
 
其中比较特别的是org.springframework.samples.jpetstore.dao.ibatis.maps package,里面定义了ibatis pojodb tablemappingSQL语句的xml file。以其中的Account为例:
 
Account pojo:
public class Account implements Serializable {
 private String username;
 private String password;
 private String address1;
 ....
}
 
注意:Account pojo不只对应一个table,而是包含了4tabledata: account, profile, signon, bannerdata table.
 
 
SqlMapAccountDao (account ibatis dao implement):
public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao {
 public Account getAccount(String username, String password) throws DataAccessException {
    Account account = new Account();
    account.setUsername(username);
    account.setPassword(password);
 
    //queryForObject方法第一个参数对应的是ibatis mapping xml<select>/<insert>/<update>id属性值,见下面的Account.xml
    //第二个参数包含了传给ibatis mapping xml的对应的SQL的参数值
    return (Account) getSqlMapClientTemplate().queryForObject("getAccountByUsernameAndPassword", account);
 }
 ....
}
 
 
Account.xml (in org.springframework.samples.jpetstore.dao.ibatis.maps folder) 定义了ibatis pojodb tablemappingSQL语句
 
<sqlMap namespace="Account">
 <resultMap id="result" class="org.springframework.samples.jpetstore.domain.Account">
    <result property="username" column="userid" columnIndex="1"/>
    <result property="email" column="email" columnIndex="2"/>
    <result property="firstName" column="firstname" columnIndex="3"/>
    <result property="lastName" column="lastname" columnIndex="4"/>
    <result property="status" column="status" columnIndex="5"/>
    <result property="address1" column="addr1" columnIndex="6"/>
    <result property="address2" column="addr2" columnIndex="7"/>
    <result property="city" column="city" columnIndex="8"/>
    <result property="state" column="state" columnIndex="9"/>
    <result property="zip" column="zip" columnIndex="10"/>
    <result property="country" column="country" columnIndex="11"/>
    <result property="phone" column="phone" columnIndex="12"/>
    <result property="languagePreference" column="langpref" columnIndex="13"/>
    <result property="favouriteCategoryId" column="favcategory" columnIndex="14"/>
    <result property="listOption" column="mylistopt" columnIndex="15"/>
    <result property="bannerOption" column="banneropt" columnIndex="16"/>
    <result property="bannerName" column="bannername" columnIndex="17"/>
 </resultMap>
 <select id="getAccountByUsernameAndPassword" resultMap="result">
    select
      signon.username as userid,
      account.email,
      account.firstname,
      account.lastname,
      account.status,
      account.addr1,
      account.addr2,
      account.city,
      account.state,
      account.zip,
      account.country,
      account.phone,
      profile.langpref,
      profile.favcategory,
      profile.mylistopt,
      profile.banneropt,
      bannerdata.bannername
    from account, profile, signon, bannerdata
    where account.userid = #username#
      and signon.password = #password#
      and signon.username = account.userid
      and profile.userid = account.userid
      and profile.favcategory = bannerdata.favcategory
 </select>
 。。。。
 <update id="updateSignon">
    update signon set password = #password# where username = #username#
 </update>
 
 <insert id="insertSignon">
    insert into signon (password,username) values (#password#,#username#)
 </insert>
 
</sqlMap>
 
最后在spring config xml file里的相关定义为
            <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
                        <property name="driverClassName" value="${jdbc.driverClassName}"/>
                        <property name="url" value="${jdbc.url}"/>
                        <property name="username" value="${jdbc.username}"/>
                        <property name="password" value="${jdbc.password}"/>
            </bean>
 
            <!-- Transaction manager for a single JDBC DataSource -->
            <!-- (see dataAccessContext-jta.xml for an alternative) -->
            <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                        <property name="dataSource" ref="dataSource"/>
            </bean>
 
            <!-- SqlMap setup for iBATIS Database Layer -->
            <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
                        <!-- sql map config xml-->
                        <property name="configLocation" value="WEB-INF/sql-map-config.xml"/>
                        <property name="dataSource" ref="dataSource"/>
            </bean>
 
 
            <!-- ========================= DAO DEFINITIONS: IBATIS IMPLEMENTATIONS ========================= -->
            <bean id="accountDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapAccountDao">
                        <property name="sqlMapClient" ref="sqlMapClient"/>
            </bean>
 
WEB-INF/sql-map-config.xml:
 
<sqlMapConfig>
            <sqlMap resource="org/springframework/samples/jpetstore/dao/ibatis/maps/Account.xml"/>
            <sqlMap resource="org/springframework/samples/jpetstore/dao/ibatis/maps/Category.xml"/>
            <sqlMap resource="org/springframework/samples/jpetstore/dao/ibatis/maps/Product.xml"/>
            <sqlMap resource="org/springframework/samples/jpetstore/dao/ibatis/maps/Item.xml"/>
            <sqlMap resource="org/springframework/samples/jpetstore/dao/ibatis/maps/Order.xml"/>
            <sqlMap resource="org/springframework/samples/jpetstore/dao/ibatis/maps/LineItem.xml"/>
            <sqlMap resource="org/springframework/samples/jpetstore/dao/ibatis/maps/Sequence.xml"/>
</sqlMapConfig>
 
 
要特别提及的一个dao是SqlMapSequenceDao,它只有实现类,没有接口,这是因为it is only for ibatis。dao是用来获取各个table(如order, item)的递增id。在jpetstore里只有order table使用递增id。该dao实现的做法是:
 
1. 在db方面,table sequence有两个columnsname and nextidname用来标记对应哪个tablenextid标记当前的递增id的值。例如对于order table,对应的sequence table就有一个record:name='ordernum', nextid的初始化值为1。
 
2. 在pojo方面,Sequence pojo对应table sequence, mapping xml file is sequence.xml
 
3. 在orderDao insertOrder时,就会先调用SqlMapSequenceDaogetNextId(name)方法,该方法会返回当前的递增id,并update nextId value + 1
 
 
* spring mvc你的FormController对应的form class通常与FormController放在同一个package。如AccountFormController and AccountForm class
 
 
org.springframework.beans.support.PagedListHolder,这是一个有用的类,用来把list可以分页显示。
class的构造方法以List为参数,如:
            PagedListHolder itemList = new PagedListHolder(petStore.getItemListByProduct(productId));
            itemList.setPageSize(4); //设置把list分成几个page
 
如果要显示上一个page/下一个page,使用下列代码:
            itemList.previousPage();
            itemList.nextPage();
 
那么要获取current pagelist,使用代码:
Class:
itemList.getPageList()
 
JSP:
<c:forEach var="item" items="${itemList.pageList}">
 
在对list进行分页显示时,会把PagedListHolder object存在session里,然后不论是查看哪一页,都是先从session里获取PagedListHolder object。详见ViewProductController
 
 
add cart item core codes:
                        //get cart session, if not exist, create a cart session
                        Cart cart = (Cart) WebUtils.getOrCreateSessionAttribute(request.getSession(), "sessionCart", Cart.class);
 
                        //checkadditem是否已经存在,如果已经存在的话,则在原来存在于session cart的该item的数量+1
                        String workingItemId = request.getParameter("workingItemId");
                        if (cart.containsItemId(workingItemId)) {
                                    cart.incrementQuantityByItemId(workingItemId);
                        }
                        //如果要additem不存在,就把它add to session cart
                        else {
                                    // isInStock is a "real-time" property that must be updated
                                    // every time an item is added to the cart, even if other
                                    // item details are cached.
                                    boolean isInStock = this.petStore.isItemInStock(workingItemId);
                                    Item item = this.petStore.getItem(workingItemId);
                                    cart.addItem(item, isInStock);
                        }
 
 
spring WebUtils有2个有用的方法for get session
                        //get session. 它相当于request.getSession().getAttribute("userSession");
                        UserSession userSession = (UserSession) WebUtils.getRequiredSessionAttribute(request, "userSession");
                        //get session. If not exist, create it
                        Cart cart = (Cart) WebUtils.getOrCreateSessionAttribute(request.getSession(), "sessionCart", Cart.class);
 
 
 
jpetstore并不是所有的page都需要log in之后才能够见到,只有edit account and order相关的page才需要log on之后才可以access。因此在access这些page之前要check是否有log injpetstore使用的是interceptor方式(见SignonInterceptor),那么下列代码就是在config xml file设置部分page通过interceptor来check是否log in:
            <bean id="secureHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
                        <property name="interceptors">
                                    <list>
                                                <ref bean="signonInterceptor"/>
                                    </list>
                        </property>
                        <property name="urlMap">
                                    <map>
                                                <entry key="/shop/editAccount.do" value-ref="secure_editAccount"/>
                                                <entry key="/shop/listOrders.do" value-ref="secure_listOrders"/>
                                                <entry key="/shop/newOrder.do" value-ref="secure_newOrder"/>
                                                <entry key="/shop/viewOrder.do" value-ref="secure_viewOrder"/>
                                    </map>
                        </property>
            </bean>
 
 
            <bean id="secure_viewOrder" class="org.springframework.samples.jpetstore.web.spring.ViewOrderController">
                        <property name="petStore" ref="petStore"/>
            </bean>
            。。。。。
 
上述设置表示有4个page在access之前会插入signonInterceptor来check log in。
 
public class SignonInterceptor extends HandlerInterceptorAdapter {
 
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                                    throws Exception {
                        UserSession userSession = (UserSession) WebUtils.getSessionAttribute(request, "userSession");
                        if (userSession == null) {
                                    //获得在log in之后要forwardlink
                                    String url = request.getServletPath();
                                    String query = request.getQueryString();
                                    ModelAndView modelAndView = new ModelAndView("SignonForm");
                                    if (query != null) {
                                                modelAndView.addObject("signonForwardAction", url+"?"+query);
                                    }
                                    else {
                                                modelAndView.addObject("signonForwardAction", url);
                                    }
                                   
                                    //关键方法
                                    throw new ModelAndViewDefiningException(modelAndView);
                        }
                        else {
                                    return true;
                        }
            }
 
}
 
 
AccountFormController同时用于add account and edit account,但edit account需要先log in,而add account不需要,怎么办?
方案就是对于AccountFormController定义2bean
 
            <bean name="/shop/newAccount.do" class="org.springframework.samples.jpetstore.web.spring.AccountFormController">
                        <property name="petStore" ref="petStore"/>
                        <property name="validator" ref="accountValidator"/>
                        <property name="successView" value="index"/>
            </bean>
 
            <bean id="secure_editAccount" class="org.springframework.samples.jpetstore.web.spring.AccountFormController">
                        <property name="petStore" ref="petStore"/>
                        <property name="validator" ref="accountValidator"/>
                        <property name="successView" value="index"/>
            </bean>
 
AbstractWizardFormController的运用
当你收集的info需要多个page的form来submit才行的话,就要用到AbstractWizardFormController。在jpetstore里,submit new order的Controller就是扩展AbstractWizardFormController。
 
submit new order的流程是:首先输入一些基本信息(如credit card no, billing address),同时还有一个check box “Ship to different address...”,如果钩上这个选项,按next,就会转到输入shipping address的form page,最后submit。如果没有钩上这个选项则跳过shipping address form page,直接submit.
 
下面看看OrderFormController的主要代码:
 
public class OrderFormController extends AbstractWizardFormController {
            public OrderFormController() {
                        //设置多page form的所有pagename
                        setPages(new String[] {"NewOrderForm", "ShippingForm", "ConfirmOrder"});
            }
 
            。。。
           
            //每个form page submit时,就都会调用该方法
            //返回的是下一个pagepages array里的index
            protected int getTargetPage(HttpServletRequest request, Object command, Errors errors, int currentPage) {
                        OrderForm orderForm = (OrderForm) command;
                        //如果钩上"Ship to different address..."选项,则跳到page 1,否则就跳到page 2
                        if (currentPage == 0 && orderForm.isShippingAddressRequired()) {
                                    return 1;
                        }
                        else {
                                    return 2;
                        }
            }
           
            //对于多form pagecontroller里如果使用validator,则应该在validatePage里进行(普通formController validate是在onBindAndValidate方法里进行)       
            protected void validatePage(Object command, Errors errors, int page) {
                        OrderForm orderForm = (OrderForm) command;
                        OrderValidator orderValidator = (OrderValidator) getValidator();
                        errors.setNestedPath("order");
                        switch (page) {
                                    case 0:
                                                orderValidator.validateCreditCard(orderForm.getOrder(), errors);
                                                orderValidator.validateBillingAddress(orderForm.getOrder(), errors);
                                                break;
                                    case 1:
                                                orderValidator.validateShippingAddress(orderForm.getOrder(), errors);
                        }
                        errors.setNestedPath("");
            }
 
            //最后一个form page submit时,调用该方法
            protected ModelAndView processFinish(
                                    HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) {
                        OrderForm orderForm = (OrderForm) command;
                        this.petStore.insertOrder(orderForm.getOrder());
                        request.getSession().removeAttribute("sessionCart");
                        Map model = new HashMap();
                        model.put("order", orderForm.getOrder());
                        model.put("message", "Thank you, your order has been submitted.");
                        return new ModelAndView("ViewOrder", model);
            }
}
 
spring如何check form validate?
很简单,自己定义validator。在jpetstore里,AccountFormController and OrderFormController就会用到自定义的实现了validator接口的AccountValidator and OrderValidator
 
public class AccountValidator implements Validator {
 
            public boolean supports(Class clazz) {
                        return Account.class.isAssignableFrom(clazz);
            }
 
            public void validate(Object obj, Errors errors) {
                        ValidationUtils.rejectIfEmpty(errors, "firstName", "FIRST_NAME_REQUIRED", "First name is required.");
                        。。。。
            }
}
 
那么FormController如何插入validator?见bean定义:
            <bean id="secure_editAccount" class="org.springframework.samples.jpetstore.web.spring.AccountFormController">
                        <property name="petStore" ref="petStore"/>
                        <property name="validator" ref="accountValidator"/>
                        <property name="successView" value="index"/>
            </bean>
 
其中validator property不需要你的formController定义,而是扩展的SimpleFormController/AbstractWizardFormController本身就包含的property
 
那么FormController如何使用validator?见下列代码:
 
            protected void onBindAndValidate(HttpServletRequest request,
                                    Object command, BindException errors) throws Exception {
 
                        AccountForm accountForm = (AccountForm) command;
                        Account account = accountForm.getAccount();
 
                        if (request.getParameter("account.listOption") == null) {
                                    account.setListOption(false);
                        }
                        if (request.getParameter("account.bannerOption") == null) {
                                    account.setBannerOption(false);
                        }
 
                        errors.setNestedPath("account");
                        getValidator().validate(account, errors);
                        errors.setNestedPath("");
 
                        if (accountForm.isNewAccount()) {
                                    account.setStatus("OK");
                                    ValidationUtils.rejectIfEmpty(errors, "account.username",
                                                            "USER_ID_REQUIRED", "User ID is required.");
                                    if (account.getPassword() == null
                                                            || account.getPassword().length() < 1
                                                            || !account.getPassword().equals(
                                                                                    accountForm.getRepeatedPassword())) {
                                                errors
                                                                        .reject(
                                                                                                "PASSWORD_MISMATCH",
                                                                                                "Passwords did not match or were not provided. Matching passwords are required.");
                                    }
                        } else if (account.getPassword() != null
                                                && account.getPassword().length() > 0) {
                                    if (!account.getPassword()
                                                            .equals(accountForm.getRepeatedPassword())) {
                                                errors
                                                                        .reject("PASSWORD_MISMATCH",
                                                                                                "Passwords did not match. Matching passwords are required.");
                                    }
                        }
            }