spring + ibatis jpetstore 摘要

 
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 pojo db table mapping SQL 语句的 xml file。以其中的Account为例:
 
Account pojo:
public class Account implements Serializable {
 private String username;
 private String password;
 private String address1;
 ....
}
 
注意:Account pojo 不只对应一个table ,而是包含了4 table data: 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 pojo db table mapping SQL 语句
 
< 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 有两个 columns name and nextid name 用来标记对应哪个 table nextid 标记当前的递增 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 时,就会先调用 SqlMapSequenceDao getNextId(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 page list,使用代码:
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);
 
                        //check add item 是否已经存在,如果已经存在的话,则在原来存在于 session cart 的该 item 的数量 +1
                        String workingItemId = request.getParameter("workingItemId");
                        if (cart.containsItemId(workingItemId)) {
                                    cart.incrementQuantityByItemId(workingItemId);
                        }
                        // 如果要 add item 不存在,就把它 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 in jpetstore 使用的是 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 之后要 forward link
                                    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 定义 2 bean
 
            < 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 的所有 page name
                        setPages(new String[] {"NewOrderForm", "ShippingForm", "ConfirmOrder"});
            }
 
            。。。
           
            // 每个 form page submit 时,就都会调用该方法
            // 返回的是下一个 page pages 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 page controller 里如果使用 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.");
                                    }
                        }
            }
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值