前一阵在研究O/R MAPPING,觉得HIBERNATE和IBATIS都是好东西,但IBATIS更容易上手,而且跟数据库打交道的时候只需把SQL 语句配置在XML里即可,不象HIBERNAMTE要写复合其规范的HQL。当然现在HIBERNATE大行其道肯定是有道理的,我过一阵子也打算好好学习一下。
技术发展的太快,学习方法是很重要的。(看来开源确实促进了生产力的发展啊,由此推断,共产主义社会肯定会实现的啦。)我认为必须摒弃抱本书按部就班的学习方法,而应该是STUDY IN ACTION,在实践中学习,缺啥补啥,大不了不断重构好了。当然前提是有一定的技术积累了。
为了学习IBATIS,除了大致看了一下IBATIS的官方文档,我把主要精力放在研究作者的范例JPETSTORE上了,大家可以道WWW.IBATIS.COM去下载4.0版本的例子,3.X的不要下了,代码写的没有4.0的优美。
然后再去下载MYSQL,TOMCAT,安装调试让JETSTORE跑起来。这个过程我就不讲了,大家动动脑筋找点成就感吧。
工欲善其事,必先利其器。我用的是JBUILDER9,因为我以前用DELPHI的对BORLAND的产品有感情啊(当然BORLAND不会对我有感情,因为。。。。。。^_^,中国特色)。
建一个新项目,保存后关掉PROJECT,然后把JPETSTORE的代码包拷贝到:project\src\下,再打开刚才的项目,顺利的话你就能看到项目下的一堆包啦。现在编译肯定是有问题的,别急别急,在项目里加载上JPETSTORE源码LIB目录下的所有.JAR和SERVLET.JAR,为了连MYSQL,你还得加载MYSQL的JDBC驱动。具体做法,你可以在JBUILDER9 Tools\config libraries 新建三个LIB,比如IBATISLIB里放上JPETSTORE源码LIB目录下的所有.JAR,servlet就不要建了,JB9里带了,再建一个MYSQLLIB放上MYSQL的JDBC驱动的.jar,然后修改JPETSTORE源码SRC\PROPERTIES\database.properties的内容,例如,我的是
driver=org.gjt.mm.mysql.Driver
url=jdbc:mysql://localhost/jpetstore
username=jpetstore
password=ibatis9977
现在MAKE PROJECT应该可以通过了。好啦,打开com.ibatis.jpetstore底下的所有.java文件吧。
Domain目录下放的是JAVABEAN,一般是跟表相对应的。
Persistence目录下放的是DAO接口和具体实现类,DAO即数据访问对象(DATA ACCESS OBJECT),是用来跟数据库打交道的。IBATIS的O/R MAPPING的使用方法也就在这个目录里面。
Presentation目录下放的是FORMBEAN,后面还要说到JPETSTORE范例对STRUTS的创造性使用。
Service目录下放的是SERVICE类,通过SERVICE对象调用DAO对象的方法来访问数据库的,这是一种常用的设计模式。
常见的STRUTS编程应该是一个动作对应一个ACTION,例如写一个交友网站,用户登录和用户查询就得写分别写两个ACTION来处理。而JPETSTORE的作者克林顿(Clinton Begin,名字够响亮的)写了一个ACTION动态的调用FORMBEAN的不同方法来处理不同的动作。
代码如下,注意execute()的写法!
public class BeanAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response)
throws Exception {
String forward = "success";
try {
ActionContext.initialize(request, response);
if (form != null) {
// Explicit Method Mapping
Method method = null;
String methodName = mapping.getParameter();
if (methodName != null && !"*".equals(methodName)) {
try {
method = form.getClass().getMethod(methodName,
null);
forward = (String) method.invoke(form, null);
} catch (Exception e) {
throw new BeanActionException("Error dispatching
bean action via method parameter ('" + methodName + "').
Cause: " + e, e);
}
}
// Path Based Method Mapping
if (method == null && !"*".equals(methodName)) {
methodName = mapping.getPath();
if (methodName.length() 1) {
int slash = methodName.lastIndexOf("/") + 1;
methodName = methodName.substring(slash);
if (methodName.length() 0) {
try {
method = form.getClass().getMethod
(methodName, null);
forward = (String) method.invoke(form, null);
} catch (Exception e) {
throw new BeanActionException("Error dispatching
bean action via URL pattern ('" + methodName + "').
Cause: " + e, e);
}
}
}
}
}
} catch (Exception e) {
request.setAttribute("BeanActionException", e);
throw e;
}
return mapping.findForward(forward);
}
}
也就是通过对struts-config.xml的配置信息的读取来决定调用的方法,或者是不调用任何方法。
例如:
当ACTIONSERVLET收到请求/shop/viewCategory.shtml,(JPETSTORE里后缀定义的是“.shtml”)调用catalogBean的viewCategory方法。
怎么样,有创意吧,如果你愿意,就再也不用写那么多的ACTION了。
下面谈一谈IBATIS的O/R MAPPING吧。请大家打开目录your project\com\ibatis\jpetstore\persistence\sqlmapdao\sql,看到了什么?除了配置文件sql-map-config.xml以外,是一些和JAVABEAN相对应的XML,打开sql-map-config.xml和Acount.xml看看吧,再返到your project\com\ibatis\jpetstore\persistence目录,打开dao.xml发现他们的关系了吧。可能这时你搞不清这些XML有什么用处,别急,IBATIS官方文档里说的很清楚(有中文版的哦)。我大致说一下跟JAVABEAN对应的那些XML文件的作用吧,比如Acount.xml,是映射JAVABEAN和数据库的表的文件。你可以按照IBATIS的规范在里面写任意的SQL语句,参数和返回值可以是基本类型及其包装类,MAP,自定义的类。最常用的当然是返回自定义类实例了。不过如果返回值是自定义的类实例的话,你就需要先定义resultMap,比如:
<resultMap id="accountResult" class="account">
<result property="username" column="USERID"/>
<result property="email" column="EMAIL"/>
<result property="firstName" column="FIRSTNAME"/>
<result property="lastName" column="LASTNAME"/>
<result property="status" column="STATUS"/>
<result property="address1" column="ADDR1"/>
<result property="address2" column="ADDR2"/>
<result property="city" column="CITY"/>
<result property="state" column="STATE"/>
<result property="zip" column="ZIP"/>
<result property="country" column="COUNTRY"/>
<result property="phone" column="PHONE"/>
<result property="languagePreference" column="LANGPREF"/>
<result property="favouriteCategoryId" column="FAVCATEGORY" />
<result property="listOption" column="MYLISTOPT" />
<result property="bannerOption" column="BANNEROPT" />
<result property="bannerName" column="BANNERNAME" />
<resultMap>
然后再引用:
<select id="getAccountByUsername" resultMap="accountResult" parameterClass="string">
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 = #value#
and SIGNON.USERNAME = ACCOUNT.USERID
and PROFILE.USERID = ACCOUNT.USERID
and PROFILE.FAVCATEGORY = BANNERDATA.FAVCATEGORY
<select>
再打开AccountSqlMapDao.java看看:
public Account getAccount(String username) {
return (Account) queryForObject("getAccountByUsername", username);
}
思考一下,看明白其中的关联了吧。
打开AccountService.JAVA:
public Account getAccount(String username) {
return accountDao.getAccount(username);
}
打开AccountBean.java:
public String newAccount() {
try {
accountService.insertAccount(account);
account = accountService.getAccount
(account.getUsername());//在这调用的
myList = catalogService.getProductListByCategory
(account.getFavouriteCategoryId());
authenticated = true;
repeatedPassword = null;
return "success";
} catch (Exception e) {
throw new BeanActionException ("There was a problem
creating your Account Information. Cause: " + e, e);
}
}
看了半天,写个例子试试吧
package com.ibatis.jpetstore.service;
import com.ibatis.dao.client.DaoManager;
import com.ibatis.jpetstore.domain.*;
import com.ibatis.jpetstore.persistence.DaoConfig;
import com.ibatis.common.util.PaginatedList;
import com.ibatis.jpetstore.persistence.iface.*;
import java.util.*;
public class IbatisTest {
private DaoManager daoManager =
DaoConfig.getDaomanager();
private AccountDao accountDao;
public IbatisTest() {
accountDao = (AccountDao) daoManager.getDao
(AccountDao.class);
Account account=accountDao.getAccount("j2ee", "j2ee");
System.out.println(account.getFirstName());
}
public static void main(String[] args) {
IbatisTest ibatisTest=new IbatisTest();
}
}
你还可以试试insert,update,delete。
IBATIS里可是支持分页的,用起来很爽的,大家有空还是花点功夫钻研一下吧。
关于DEBUG:
我前两天照搬JPETSTORE的架构试着写了一个交友网站,基本上挺顺利,只是感觉DEBUG比较麻烦,通常为了找一个错花上几个小时。JBUILDER带的DEBUG我没用过,但可是肯定肯定和DELPHI上的DEBUG是有天壤之别的。而且JUNIT我也是刚开始学,也没用上。这里,我把自己比较土的办法介绍一下:
一,当你写了新的O/R MAPPING的时候,可以先写个包含MAIN方法的类测试一下,就向我上面的测试例子一样。
二,当你想跟踪变量值的时候,你可以利用IBATIS带的工具类ActionContext把变量写到SESSION里,比如你有你个FORMBEAN:UserBean.java,其中有一个方法是处理用户登录的,你为了跟踪用户的用户名和密码,可以这样写:
public String logon() {
try {
user = userService.getUser(user.getUsername(),
user.getPassword());
if (user== null ) {
clear();
return "failure";
} else {
user.setPassword(null);
authenticated = true;
user.setMessageCount
(messageService.getMessageNewCount(user.getUserId()));
ActionContext.getActionContext().setSimpleMessage
("newCount="+newCount.toString()+"
toUserId="+user.getUserId());//写入SESSION
return " failure ";}//导入到Error.jsp
}catch (Exception e){
throw new BeanActionException ("There was a problem
logoning your Account Information. Cause: " + e, e);
}
}
当然你需要包含下面的语句
import com.ibatis.struts.ActionContext;
Error.jsp:
<%@ taglib uri="struts-logic" prefix="logic" %>
<%@ taglib uri="struts-bean" prefix="bean" %>
<%@ taglib uri="struts-html" prefix="html" %>
<%@ page import="java.io.PrintWriter"%>
<logic:present name="message">
<bean:write
name="message" />
</logic:present>
//输出你需要的信息
<logic:present name="errors">
<logic:iterate id="error" name="errors">
<bean:write name="error" />
</logic:iterate>
</logic:present>
<logic:notPresent name="BeanActionException"
<logic:notPresent name="message">
Something happened...
But no further information was provided.
</logic:notPresent>
</logic:notPresent>
<logic:present name="BeanActionException">
Error!
name="BeanActionException"
property="class.name"/>
<bean:write name="BeanActionException"
property="message"/>
</logic:present>
<logic:present name="BeanActionException">
Stack
Exception e = (Exception)request.getAttribute
("BeanActionException");
e.printStackTrace(new PrintWriter(out));
%>
</logic:present>
三,为跟踪变量,你还可以利用LOG4J把变量值写到LOG里去,这种方法很常见,不明白就去www.google.com查吧。
四,为避免写javabean出现大小写或其他的错误,强烈建议不要把BEAN当做普通CLASS来写,要用IDE 新建一个BEAN,这样IDE会帮你找出很多错误。我就吃了不少苦头。
入门了吗?有问题请留言。
为了贴上XML费了老鼻子劲,不容易啊。
我要开始研究SPRING和HIBERNATE啦。