在開發(fā)過程中最能幫助你的是什么?是框架,一個優(yōu)秀的框架可以極大的提高你的效率。struts給了我們什么?MVC的實現(xiàn),國際化、靈活。還有很多。不過,在一個通常的WEB應該中,是不能缺少數(shù)據(jù)庫的,而struts在這方面并沒有給我們提供什么有效的幫助。通常情況下我們做這個的時候有幾個選擇。 最直接的當然是JDBC啊,自己寫connect、statment和resultset等等的代碼,結果是累死自己。 然后一種方法是EJB,EJB確實是一個好東西,可惜在很多場合用不上,起碼它很煩,速度很慢 還有一種選擇就是JDO及類似的東西。最著名是free的應該是castor,hibernate等。
現(xiàn)在我們又多了一種選擇,就是ibatis Db Layer,它的主頁是http://www.,為什么說它好,讓我們來看看作者自己的說明吧,使用ibatis的理由
10、知道怎樣操作10種以上的數(shù)據(jù)庫 9 、可配置的caching(包括從屬) 8、支持DataSource、local transaction managemen和global transaction 7、簡單的XML配置文檔 6、支持Map, Collection, List和簡單類型包裝(如Integer, String) 5、支持JavaBeans類(get/set 方法) 4、支持復雜的對象映射(如populating lists, complex object models) 3、對象模型從不完美(不需要修改) 2、數(shù)據(jù)模型從不完美(不需要修改) 1、你已經(jīng)知道SQL,為什么還要學習其他東西
另外一點它是100% Open Source Freeware
下面我們就來看一看,做一個簡單的ibatis需要哪一些工作。然后一步一步深入探索它的強大功能。在實踐中來看它的好處在哪里。
在ibatis的網(wǎng)站上有一個它自己的petstore,在我個人看來是最簡潔的petstore了,跟struts1.0結合。應該說是一個不錯的教程。希望大家能夠好好研究。當然,作為入門。我們先來做一個簡單的程序。所采用的庫嘛,就仍然是用petstore的了。數(shù)據(jù)庫也是選擇Oracle(為什么選擇Oracle,很多朋友不理解,怎么不用mysql之類的呢,一個主要的原因是個人愛好,Oracle畢竟是商業(yè)數(shù)據(jù)庫,有它的強大之處,另外在linux下它也是免費的,:)。廢話少說,先用jpetstore3.1提供的ddl建立一個庫吧。
然后在eclipse里建立一個ibatisDemo的工程。加入ibatis提供的庫,建立相就的目錄??匆幌乱粋€最簡單的程序需要哪一些文件。我們選擇一個簡單表,即Category這個表的操作來演示功能
| 文件路徑 |
功能說明 |
備注 |
| config\properties\petstore.properties |
可變參數(shù)配置文件,所有根據(jù)環(huán)境不同的參數(shù)都放在這里 |
|
| config\properties\simple\dao.xml |
dao配置文件,主要存放dao對象和數(shù)據(jù)池設置 |
|
| config\properties\simple\sql-map-config-storedb.xml |
真正的核心配置文件 |
|
| config\sqlmap\Category.xml |
存放Category的數(shù)據(jù)操作的SQL |
|
| com.ewuxi.champion.exception.DaoException.java |
自定義的Exception類,不用說了吧 |
|
| com.ewuxi.champion.Service.java |
一個服務類,用于初始化 |
|
| com.ewuxi.champion.persistence.dao.DaoCommon |
Dao層的統(tǒng)一操作類,提供一些公共函數(shù) |
|
| com.ewuxi.champion.persistence.dao.CategoryDb |
Category的操作類 |
|
| com.ewuxi.champion.persistence.vo.Category |
valueObject 值對象 |
|
| com.ewuxi.champion.persistence.dao.CategoryDbTest |
單元測試類 |
|
下面一個一個文件詳細說明
petstore.properties
################################################################## SIMPLE CONFIGURATION SECTION ##################################################################
## SimpleDataSource properties ## Use only if useSimpleConfiguration=true
SimpleDriver=oracle.jdbc.OracleDriver SimpleUrl=jdbc:oracle:thin:@10.0.0.5:1521:champion SimpleUsername=pet SimplePassword=pet
|
這個不用解釋,就是數(shù)據(jù)庫的連接串,如果你在自己的機器上運行,當然這些都是需要改的。
dao.xml
| <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dao-config PUBLIC "-//iBATIS.com//DTD DAO Configuration 1.0//EN" "http://www./dtd/dao.dtd">
<dao-config>
<context name="StoreDaoManager" default="true"> <!-- Configure the transaction pool. --> <transaction-pool implementation="com.ibatis.db.dao.jdbc.SqlMapDaoTransactionPool"> <property name="sql-map-config-file" value="properties/simple/sql-map-config-storedb.xml"/> </transaction-pool>
</context>
</dao-config>
|
上面這一段也是很簡單的,連一個dao也沒有配置,也就是說,用的是默認的Dao。其中<context name="StoreDaoManager" default="true">表示它是默認的數(shù)據(jù)庫配置(它可以根據(jù)名字不同同時連接幾個數(shù)據(jù)庫的)。
sql-map-config-storedb.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sql-map-config PUBLIC "-//iBATIS.com//DTD SQL Map Config 1.0//EN" "http://www./dtd/sql-map-config.dtd">
<sql-map-config>
<properties resource="properties/petstore.properties" />
<settings maxExecute="0" maxExecutePerConnection="0" maxTransactions="0" cacheModelsEnabled="true" statementCacheSize="175" useBeansMetaClasses="false" useGlobalTransactions="false" />
<datasource name="jpestoreSimple" factory-class="com.ibatis.db.sqlmap.datasource.DbcpDataSourceFactory" default="true" > <property name="JDBC.Driver" value="${SimpleDriver}"/> <property name="JDBC.ConnectionURL" value="${SimpleUrl}"/> <property name="JDBC.Username" value="${SimpleUsername}"/> <property name="JDBC.Password" value="${SimplePassword}"/> <property name="Pool.MaximumActiveConnections" value="15"/> <property name="Pool.MaximumIdleConnections" value="15"/> <property name="Pool.MaximumWait" value="1000"/> </datasource>
<sql-map resource="sqlmap/Category.xml" />
</sql-map-config>
|
這里真正實現(xiàn)了數(shù)據(jù)庫連接,我們使用的是dbcp的連接池。JDBC的配置大家都很熟了。${SimpleDriver}就是指的前面petstore.properties中的SimpleDriver的內容。
而<sql-map resource="sqlmap/Category.xml" />則表示包含Category.xml這個文件。
Category.xml
|
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sql-map PUBLIC "-//iBATIS.com//DTD SQL Map 1.0//EN" "http://www./dtd/sql-map.dtd">
<sql-map name="Category">
<result-map name="result" class="com.ewuxi.champion.persistence.vo.Category"> <property name="categoryId" column="CATID" columnIndex="1" /> <property name="name" column="NAME" columnIndex="2"/> <property name="description" column="DESCN" columnIndex="3"/> </result-map>
<mapped-statement name="findByPrimaryKeyCategoryDao" result-map="result"> select CATID, NAME, DESCN from CATEGORY where CATID = #categoryId# </mapped-statement>
<dynamic-mapped-statement name="findCategoryDao" result-map="result"> select CATID, NAME, DESCN from CATEGORY <dynamic prepend="where"> <isNotNull prepend="and" property="categoryId" > CATID = #categoryId# </isNotNull> <isNotNull prepend="and" property="name" > NAME = #name# </isNotNull> <isNotNull prepend="and" property="description"> DESCN = #description# </isNotNull> </dynamic> </dynamic-mapped-statement>
<mapped-statement name="findCategoryDaoCount" result-class="java.lang.Integer"> select count(1) as value from CATEGORY </mapped-statement>
<!-- ============================================= mapped-statement ============================================= -->
<dynamic-mapped-statement name="updateByPrimaryKeyCategoryDao"> update CATEGORY <dynamic prepend="set"> <isNotNull prepend="," property="name" > NAME = #name# </isNotNull> <isNotNull prepend="," property="description"> DESCN = #description# </isNotNull> </dynamic> where CATID =#categoryId# </dynamic-mapped-statement>
<!-- ============================================= mapped-statement ============================================= -->
<mapped-statement name="deleteByPrimaryKeyCategoryDao"> delete from CATEGORY where CATID =#categoryId# </mapped-statement>
<!-- ============================================= OPTIONAL EXPLICIT PARAMETER MAP ============================================= -->
<parameter-map name="insert-params"> <property name="categoryId"/> <property name="name" type="VARCHAR"/> <property name="description" type="VARCHAR"/> </parameter-map>
<!-- ============================================= MAPPED STATEMENTS - w/Explicit Parameter Map ============================================= -->
<mapped-statement name="insertCategoryDao" parameter-map="insert-params" > insert into CATEGORY ( CATID,NAME,DESCN) values ( ,?,? ) </mapped-statement> </sql-map>
|
上述文件就是真正的SQL所存在的地方。
|
<result-map name="result" class="com.ewuxi.champion.persistence.vo.Category"> <property name="categoryId" column="CATID" columnIndex="1" /> <property name="name" column="NAME" columnIndex="2"/> <property name="description" column="DESCN" columnIndex="3"/> </result-map>
這一段的內容表示返回的對象是com.ewuxi.champion.persistence.vo.Category,也就是我們值對象。當執(zhí)行查詢的時候,dblay會封裝出一個Category對象或者一個Category的list集合。其中數(shù)據(jù)列CATID就對象javabean的categoryId值。name是自定義的
|
|
<mapped-statement name="findByPrimaryKeyCategoryDao" result-map="result"> select CATID, NAME, DESCN from CATEGORY where CATID = #categoryId# </mapped-statement>
此處result-map="result"表示返回結果以后,就會參照前面的result來返回對象。select CATID, NAME, DESCN from CATEGORY where CATID = #categoryId#標準的SQL,只不過這一點CATID = #categoryId#有些不同,#categoryId#表示傳遞一個Category對象時,Dblay會自動取得categoryId的值來執(zhí)行SQL
|
再來看一個
|
<dynamic-mapped-statement name="updateByPrimaryKeyCategoryDao"> update CATEGORY <dynamic prepend="set"> <isNotNull prepend="," property="name" > NAME = #name# </isNotNull> <isNotNull prepend="," property="description"> DESCN = #description# </isNotNull> </dynamic> where CATID =#categoryId# </dynamic-mapped-statement>
這個地方就體現(xiàn)了dblayer的強大之處,動態(tài)SQL。平常我們經(jīng)常碰到的情況是根據(jù)不同的情況,執(zhí)行的SQL有一點點不一樣。寫在程序里,要寫不少的if then之類的,在這里,dbLayer給你一下解決了。比如在這里,我們三個值都是String對象,所以通過isNotNull就可以實現(xiàn)幾種不同的update了,比如,如果我只想修改DESCN這個字段,只要傳過去的Category對象只有categoryId和description有值,就會生成update CATEGORY set DESCN = #description# where CATID =#categoryId#。同樣如果傳遞的對象只有categoryId和name有值,就會生成update CATEGORY set NAME = #name# where CATID =#categoryId#。是否很強大?:)
|
前面這兩種,參數(shù)的傳遞方式是內置參數(shù),也就是CATID =#categoryId#這種,大家可能不太習慣,那就看一看標準的寫法吧。
|
<!-- ============================================= OPTIONAL EXPLICIT PARAMETER MAP ============================================= -->
<parameter-map name="insert-params"> <property name="categoryId"/> <property name="name" type="VARCHAR"/> <property name="description" type="VARCHAR"/> </parameter-map>
<!-- ============================================= MAPPED STATEMENTS - w/Explicit Parameter Map ============================================= -->
<mapped-statement name="insertCategoryDao" parameter-map="insert-params" > insert into CATEGORY ( CATID,NAME,DESCN) values ( ,?,? ) </mapped-statement> </sql-map>
|
這里面的insert語句想來大家都很熟了吧?這個時候怎么取得參數(shù)呢?關鍵在于這里parameter-map="insert-params",表示會讀取<parameter-map name="insert-params">的設置,而這個設置也不用多解釋了吧,就是按順序,三個?分別對應三個值。還能指明他們的數(shù)據(jù)類型。
下面來看看Service.java
package com.ewuxi.champion;
import java.io.Reader; import java.util.Properties;
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;
import com.mon.resources.Resources; import com.ibatis.db.dao.DaoManager;
/** * @author champion * * To change the template for this generated type comment go to * Window - Preferences - Java - Code Generation - Code and Comments */ public class Service { static Log log = LogFactory.getLog(Service.class); public static void initSet() {
try {
String resource = null;
resource = "properties/simple/dao.xml"; log.info("Using SIMPLE configuration. (" + resource + ")");
Reader reader = Resources.getResourceAsReader(resource); DaoManager.configure(reader);
} catch (Exception e) { throw new RuntimeException( "Could not initialize BaseLogic. Cause: " + e); } }
}
|
一個靜態(tài)方法,從resource文件中讀出配置,最后用DaoManager.configure(reader);完成配置。
DaoCommon
public static Dao getDefautDao(){ return DaoManager.getInstance().getDao(""); } public static SqlMap getSqlMap(Dao c) throws DaoException { try { DaoManager daoManager = DaoManager.getInstance(c);
if (daoManager == null) { daoManager = DaoManager.getInstance(); }
SqlMapDaoTransaction trans = (SqlMapDaoTransaction) daoManager.getLocalTransaction(); SqlMap sqlMap = trans.getSqlMap();
return sqlMap; } catch (Exception e) { throw new DaoException(e); } }
public static SqlMap getSqlMap(String c) throws DaoException { try { DaoManager daoManager = DaoManager.getInstance(c); SqlMapDaoTransaction trans = (SqlMapDaoTransaction) daoManager.getLocalTransaction(); SqlMap sqlMap = trans.getSqlMap();
return sqlMap; } catch (Exception e) { throw new DaoException(e); } }
|
三個主要的函數(shù),第一個是得到默認的DAO對象,后兩個是根據(jù)一個dao對象或者一個參數(shù)(也就是前面<context name="StoreDaoManager" >中是name值)。取得SqlMap對象,這個對象是主要的數(shù)據(jù)操作接口。
/** * @throws Exception * 開始事務,所在session層必須使用它 */ public static void startTransaction() throws Exception { if (!DaoCommon.inTransaction()) { DaoManager.getInstance().startTransaction(); } }
public static boolean inTransaction() throws Exception { try { DaoManager.getInstance().getLocalTransaction();
return true; } catch (Exception e) { return false; } }
/** * @throws Exception * 放棄事務 */ public static void rollBack() {
try { DaoManager.getInstance().rollbackTransaction(); } catch (Exception e) { LogFactory.getLog(DaoCommon.class).error(e, e); } }
/** * @throws Exception * 提交事務 */ public static void commit() throws Exception { DaoManager.getInstance().commitTransaction(); }
|
下面的一些函數(shù)是對事務的一些封裝。想必也很容易理解。
然后讓我們來看CategoryDb的內容
/* * Created on 2003-10-11 * * To change the template for this generated file go to * Window - Preferences - Java - Code Generation - Code and Comments */ package com.ewuxi.champion.persistence.dao;
import com.ewuxi.champion.exception.DaoException; import com.ewuxi.champion.persistence.vo.Category; import com.ibatis.db.sqlmap.SqlMap;
/** * @author champion * *category數(shù)據(jù)庫操作對象 */
public class CategoryDb {
/** * @param vo * @throws DaoException * 插入一條記錄 */ public void insert(Category vo) throws DaoException{ try { SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao()); sqlMap.executeUpdate("insertCategoryDao",vo); } catch (Exception e) { throw new DaoException(e); } } /** * @param vo * @throws DaoException * 刪除一條記錄 */ public void delete(Category vo) throws DaoException{ try { SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao()); sqlMap.executeUpdate("deleteByPrimaryKeyCategoryDao",vo); } catch (Exception e) { throw new DaoException(e); } } /** * @param vo * @throws DaoException * 修改一條記錄 */ public void update(Category vo) throws DaoException{ try { SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao()); sqlMap.executeUpdate("updateByPrimaryKeyCategoryDao",vo); } catch (Exception e) { throw new DaoException(e); } } /** * @param vo * @return * @throws DaoException * 查找一條記錄 */ public Category findByPk(Category vo) throws DaoException{ try { SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao()); return (Category) sqlMap.executeQueryForObject("findByPrimaryKeyCategoryDao",vo); } catch (Exception e) { throw new DaoException(e); } }
}
|
每一個函數(shù)都很類似的。關鍵就在這一句(Category) sqlMap.executeQueryForList("findByPrimaryKeyCategoryDao",vo);??吹?findByPrimaryKeyCategoryDao",這個對應于前面Category.xml中的名字。而vo則是一個Category對象。
最后是CategoryDbTest類,這個是我們的單元測試程序
|
/* * Created on 2003-10-11 * * To change the template for this generated file go to * Window - Preferences - Java - Code Generation - Code and Comments */ package com.ewuxi.champion.persistence.dao;
import com.ewuxi.champion.exception.DaoException; import com.ewuxi.champion.persistence.vo.Category; import com.ibatis.db.sqlmap.SqlMap;
/** * @author champion * *category數(shù)據(jù)庫操作對象 */
public class CategoryDb {
/** * @param vo * @throws DaoException * 插入一條記錄 */ public void insert(Category vo) throws DaoException{ try { SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao()); sqlMap.executeUpdate("insertCategoryDao",vo); } catch (Exception e) { throw new DaoException(e); } } /** * @param vo * @throws DaoException * 刪除一條記錄 */ public void delete(Category vo) throws DaoException{ try { SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao()); sqlMap.executeUpdate("deleteByPrimaryKeyCategoryDao",vo); } catch (Exception e) { throw new DaoException(e); } } /** * @param vo * @throws DaoException * 修改一條記錄 */ public void update(Category vo) throws DaoException{ try { SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao()); sqlMap.executeUpdate("updateByPrimaryKeyCategoryDao",vo); } catch (Exception e) { throw new DaoException(e); } } /** * @param vo * @return * @throws DaoException * 查找一條記錄 */ public Category findByPk(Category vo) throws DaoException{ try { SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao()); return (Category) sqlMap.executeQueryForObject("findByPrimaryKeyCategoryDao",vo); } catch (Exception e) { throw new DaoException(e); } }
}
|
運行結果,測試通過。原代碼全部下載
|