您的位置:首页 > 编程语言 > Java开发

spring + ibatis jpetstore 摘要

2008-05-26 17:07 337 查看
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.");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: