ibatis介紹
作者:紫龍
在開(kāi)發(fā)過(guò)程中最能幫助你的是什么?是框架,一個(gè)優(yōu)秀的框架可以極大的提高你的效率。struts給了我們什么?MVC的實(shí)現,國際化、靈活。還有很多。不過(guò),在一個(gè)通常的WEB應該中,是不能缺少數據庫的,而struts在這方面并沒(méi)有給我們提供什么有效的幫助。通常情況下我們做這個(gè)的時(shí)候有幾個(gè)選擇。
最直接的當然是JDBC啊,自己寫(xiě)connect、statment和resultset等等的代碼,結果是累死自己。
然后一種方法是EJB,EJB確實(shí)是一個(gè)好東西,可惜在很多場(chǎng)合用不上,起碼它很煩,速度很慢
還有一種選擇就是JDO及類(lèi)似的東西。最著(zhù)名是free的應該是castor,hibernate等。
現在我們又多了一種選擇,就是ibatis Db Layer,它的主頁(yè)是http://www.ibatis.com,為什么說(shuō)它好,讓我們來(lái)看看作者自己的說(shuō)明吧,使用ibatis的理由
10、知道怎樣操作10種以上的數據庫
9 、可配置的caching(包括從屬)
8、支持DataSource、local transaction managemen和global transaction
7、簡(jiǎn)單的XML配置文檔
6、支持Map, Collection, List和簡(jiǎn)單類(lèi)型包裝(如Integer, String)
5、支持JavaBeans類(lèi)(get/set 方法)
4、支持復雜的對象映射(如populating lists, complex object models)
3、對象模型從不完美(不需要修改)
2、數據模型從不完美(不需要修改)
1、你已經(jīng)知道SQL,為什么還要學(xué)習其他東西
另外一點(diǎn)它是100% Open Source Freeware
下面我們就來(lái)看一看,做一個(gè)簡(jiǎn)單的ibatis需要哪一些工作。然后一步一步深入探索它的強大功能。在實(shí)踐中來(lái)看它的好處在哪里。
在ibatis的網(wǎng)站上有一個(gè)它自己的petstore,在我個(gè)人看來(lái)是最簡(jiǎn)潔的petstore了,跟struts1.0結合。應該說(shuō)是一個(gè)不錯的教程。希望大家能夠好好研究。當然,作為入門(mén)。我們先來(lái)做一個(gè)簡(jiǎn)單的程序。所采用的庫嘛,就仍然是用petstore的了。數據庫也是選擇Oracle(為什么選擇Oracle,很多朋友不理解,怎么不用mysql之類(lèi)的呢,一個(gè)主要的原因是個(gè)人愛(ài)好,Oracle畢竟是商業(yè)數據庫,有它的強大之處,另外在linux下它也是免費的,:)。廢話(huà)少說(shuō),先用jpetstore3.1提供的ddl建立一個(gè)庫吧。
然后在eclipse里建立一個(gè)ibatisDemo的工程。加入ibatis提供的庫,建立相就的目錄??匆幌乱粋€(gè)最簡(jiǎn)單的程序需要哪一些文件。我們選擇一個(gè)簡(jiǎn)單表,即Category這個(gè)表的操作來(lái)演示功能
文件路徑 功能說(shuō)明 備注
config\properties\petstore.properties 可變參數配置文件,所有根據環(huán)境不同的參數都放在這里
config\properties\simple\dao.xml dao配置文件,主要存放dao對象和數據池設置
config\properties\simple\sql-map-config-storedb.xml 真正的核心配置文件
config\sqlmap\Category.xml 存放Category的數據操作的SQL
com.ewuxi.champion.exception.DaoException.java 自定義的Exception類(lèi),不用說(shuō)了吧
com.ewuxi.champion.Service.java 一個(gè)服務(wù)類(lèi),用于初始化
com.ewuxi.champion.persistence.dao.DaoCommon Dao層的統一操作類(lèi),提供一些公共函數
com.ewuxi.champion.persistence.dao.CategoryDb Category的操作類(lèi)
com.ewuxi.champion.persistence.vo.Category valueObject 值對象
com.ewuxi.champion.persistence.dao.CategoryDbTest 單元測試類(lèi)
下面一個(gè)一個(gè)文件詳細說(shuō)明
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
這個(gè)不用解釋?zhuān)褪菙祿斓倪B接串,如果你在自己的機器上運行,當然這些都是需要改的。
dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dao-config
PUBLIC "-//iBATIS.com//DTD DAO Configuration 1.0//EN"
"http://www.ibatis.com/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>
上面這一段也是很簡(jiǎn)單的,連一個(gè)dao也沒(méi)有配置,也就是說(shuō),用的是默認的Dao。其中<context name="StoreDaoManager" default="true">表示它是默認的數據庫配置(它可以根據名字不同同時(shí)連接幾個(gè)數據庫的)。
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.ibatis.com/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>
這里真正實(shí)現了數據庫連接,我們使用的是dbcp的連接池。JDBC的配置大家都很熟了。${SimpleDriver}就是指的前面petstore.properties中的SimpleDriver的內容。
而<sql-map resource="sqlmap/Category.xml" />則表示包含Category.xml這個(gè)文件。
Category.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sql-map
PUBLIC "-//iBATIS.com//DTD SQL Map 1.0//EN"
"http://www.ibatis.com/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,也就是我們值對象。當執行查詢(xún)的時(shí)候,dblay會(huì )封裝出一個(gè)Category對象或者一個(gè)Category的list集合。其中數據列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"表示返回結果以后,就會(huì )參照前面的result來(lái)返回對象。select CATID, NAME, DESCN from CATEGORY where CATID = #categoryId#標準的SQL,只不過(guò)這一點(diǎn)CATID = #categoryId#有些不同,#categoryId#表示傳遞一個(gè)Category對象時(shí),Dblay會(huì )自動(dòng)取得categoryId的值來(lái)執行SQL
再來(lái)看一個(gè)
<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>
這個(gè)地方就體現了dblayer的強大之處,動(dòng)態(tài)SQL。平常我們經(jīng)常碰到的情況是根據不同的情況,執行的SQL有一點(diǎn)點(diǎn)不一樣。寫(xiě)在程序里,要寫(xiě)不少的if then之類(lèi)的,在這里,dbLayer給你一下解決了。比如在這里,我們三個(gè)值都是String對象,所以通過(guò)isNotNull就可以實(shí)現幾種不同的update了,比如,如果我只想修改DESCN這個(gè)字段,只要傳過(guò)去的Category對象只有categoryId和description有值,就會(huì )生成update CATEGORY set DESCN = #description# where CATID =#categoryId#。同樣如果傳遞的對象只有categoryId和name有值,就會(huì )生成update CATEGORY set NAME = #name# where CATID =#categoryId#。是否很強大?:)
前面這兩種,參數的傳遞方式是內置參數,也就是CATID =#categoryId#這種,大家可能不太習慣,那就看一看標準的寫(xiě)法吧。
<!-- =============================================
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語(yǔ)句想來(lái)大家都很熟了吧?這個(gè)時(shí)候怎么取得參數呢?關(guān)鍵在于這里parameter-map="insert-params",表示會(huì )讀取<parameter-map name="insert-params">的設置,而這個(gè)設置也不用多解釋了吧,就是按順序,三個(gè)?分別對應三個(gè)值。還能指明他們的數據類(lèi)型。
下面來(lái)看看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.ibatis.common.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);
}
}
}
一個(gè)靜態(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);
}
}
三個(gè)主要的函數,第一個(gè)是得到默認的DAO對象,后兩個(gè)是根據一個(gè)dao對象或者一個(gè)參數(也就是前面<context name="StoreDaoManager" >中是name值)。取得SqlMap對象,這個(gè)對象是主要的數據操作接口。
/**
* @throws Exception
* 開(kāi)始事務(wù),所在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
* 放棄事務(wù)
*/
public static void rollBack() {
try {
DaoManager.getInstance().rollbackTransaction();
} catch (Exception e) {
LogFactory.getLog(DaoCommon.class).error(e, e);
}
}
/**
* @throws Exception
* 提交事務(wù)
*/
public static void commit() throws Exception {
DaoManager.getInstance().commitTransaction();
}
下面的一些函數是對事務(wù)的一些封裝。想必也很容易理解。
然后讓我們來(lái)看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數據庫操作對象
*/
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);
}
}
}
每一個(gè)函數都很類(lèi)似的。關(guān)鍵就在這一句(Category) sqlMap.executeQueryForList("findByPrimaryKeyCategoryDao",vo);??吹?findByPrimaryKeyCategoryDao",這個(gè)對應于前面Category.xml中的名字。而vo則是一個(gè)Category對象。
最后是CategoryDbTest類(lèi),這個(gè)是我們的單元測試程序
/*
* 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數據庫操作對象
*/
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);
}
}
}
運行結果,測試通過(guò)。原代碼全部
下載