iBatis的簡單增刪改查操作非常容易掌握,下面我們來看看iBatis的詳細(xì)配置。雖然iBatis小巧靈活,但是其可擴(kuò)展性也非常強(qiáng)。iBatis的核心配置文件就是SqlMapConfig.xml了,下面我們來看看iBatis的核心配置結(jié)構(gòu)。
SqlMapConfig配置文件在前面我們知道要配置JDBC連接,SqlMap映射文件等信息,當(dāng)然這都是非?;镜呐渲茫F(xiàn)在我們要探究一下SqlMapConfig的詳細(xì)配置。主要包括以下內(nèi)容:
<properties>元素的配置,它提供了允許在主配置文件之外的一個“名值對”列表,可以將其中的配置信息加載進(jìn)來,而這些配置信息可以放在任何一個地方。使用properties元素,其中有兩個屬性,分別是:resource和url。
使用resource屬性時,類加載器會從類路徑開始定位該資源;而使用url屬性時,則是用java.net.URL類來處理的,提供一個有效的URL即可。之前的示例中,我們使用了resource屬性來配置數(shù)據(jù)庫連接信息,如:
<properties resource="jdbc.properties" />
而在jdbc.properties中,我們配置了數(shù)據(jù)庫的驅(qū)動屬性,連接url,用戶名和密碼,這樣它們就可以被properties元素加載進(jìn)來,使用起來非常方便。而properties配置文件中是以“名值對”的方式存儲的,那么我們使用名稱即可引用,這是很多人都熟悉的語法,比如:${driver}就能獲取到com.mysql.jdbc.Driver,這里我們使用的是MySQL數(shù)據(jù)庫。
<settings>元素的配置,這個元素即設(shè)置iBatis的全局配置信息。下面我們逐一來看其中可配置的屬性信息。
lazyLoadingEnabled,從名字即可看出是否進(jìn)行延遲加載。通俗來說,延遲加載就是只加載必要信息而推遲加載其他未明確請求的數(shù)據(jù),這里要和Hibernate中延遲加載區(qū)分開。那也就是說,除非絕對必須,否則程序加載的數(shù)據(jù)越少越好。iBatis默認(rèn)使用了延遲加載,即不配置時也是默認(rèn)為true的。
cacheModelsEnabled,這是數(shù)據(jù)緩存的配置,緩存可以提高程序的性能,這是顯而易見的。和延遲加載一樣,緩存也是默認(rèn)啟用的。
enhancementEnabled,該配置是來說明是否使用cglib中那些優(yōu)化的類來提高延遲加載的性能,默認(rèn)值為true,也就是啟用。但是之前的示例中,并沒有在lib中加入cglib的類庫,那么iBatis沒有在類路徑上發(fā)現(xiàn)cglib時,該功能也就不能起作用了。這里多說一點(diǎn),對于增強(qiáng)框架,除非必須,盡量避免使用。
useStatementNamespaces,該配置說明是否使用語句的命名空間,默認(rèn)是不使用的,但是在大型應(yīng)用時,使用命名空間來作為限定就比較清楚了。使用方法是在<sql-map>標(biāo)記上加namespace屬性即可,在程序中就使用“命名空間.SQL映射語句”這種語法來執(zhí)行。
settings還可以設(shè)置maxRequests,maxSessions和maxTransactions信息,但它們都已經(jīng)被廢棄了,也就是說我們可以不用設(shè)置它們,使用默認(rèn)的就可以了。如果必須要進(jìn)行設(shè)置,那就要保證maxRequests大于maxSessions,而maxSessions要大于maxTransactions。
下面我們來看<typeAlias>元素,就是起別名,很容易理解,我們不想使用過長的類名時,可以用它來起個別名,之后我們使用別名就可以了。比如:
<typeAlias alias="User" type="ibatis.model.User" />
很容易看出type屬性是原始的類名,而alias屬性配置我們希望使用的名字即可。
iBatis已經(jīng)為我們設(shè)置了一些類型的別名,我們就可以直接使用了,比如事務(wù)管理器的JDBC,JTA和EXTERNAL;數(shù)據(jù)類型的string,byte,long,short,int等;數(shù)據(jù)源工廠的SIMPLE,DBCP,JNDI;高速緩存控制器的FIFO,LRU,MEMORY,OSCACHE和XML結(jié)果類型的DOM,domCollection,Xml,XmlCollection,它們是可以直接使用的。
transactionManager元素,沒錯,它就是來做事務(wù)的。iBatis內(nèi)置的事務(wù)管理器有JDBC,JTA和EXTERNAL。EXTERNAL表示事務(wù)管理器是應(yīng)用程序本身負(fù)責(zé),而不是iBatis。使用type屬性就能在transactionManager元素中配置事務(wù)管理器了。比如:
<transactionManager type="JDBC"></transactionManager>
它還有一個可以配置的屬性是commitRequired,來配置在某個連接釋放之前必須提交或者回滾的情況。
在transactionManager元素中還可以繼續(xù)配置<properties>元素和<dataSource>元素。properties元素用于指定transactionManager的配置項(xiàng),而后者用于配置數(shù)據(jù)源工廠,默認(rèn)提供三種SIMPLE,DBCP和JNDI。
typeHandler元素,即類型處理器,用于將數(shù)據(jù)庫中的數(shù)據(jù)類型轉(zhuǎn)換成應(yīng)用程序中使用的數(shù)據(jù)類型。假如數(shù)據(jù)庫中不支持布爾值,那么只能以0/1來代表,而Java應(yīng)用程序中支持布爾值,這里就需要一個類型處理的過程。使用時需要創(chuàng)建兩個類,一個是類型處理類,一個是類型處理回調(diào)類。iBatis預(yù)先設(shè)置了大量的類型處理器,如果不是必須,為了程序的簡單,那么盡量不要使用。
最后來看一下sqlMap元素,它就是配置SQL語句的了,是我們最常用到的一個標(biāo)簽??梢允褂胣amespace來確定一個命名空間,這在之前已經(jīng)說到了,可以將同一流程的SQL語句寫在一起,放到一個命名空間下,在程序中使用更加清晰。在sqlMap之中,就是對數(shù)據(jù)庫具體操作的實(shí)現(xiàn)了,包括增刪改查等標(biāo)記。
下面是iBatis的配置關(guān)系圖:可以加深對iBatis的理解。
先說點(diǎn)基礎(chǔ)的內(nèi)容,iBatis并不是真正意義上的ORM,官方文檔中稱其為dataMapper,是數(shù)據(jù)映射器,也就是一種映射查詢工具。iBatis不是萬能的,在某些它不能處理的問題時,不能放棄使用JDBC API,那才是根本中的根本。
在iBatis中,建議使用JavaBean,因?yàn)槲覀兪敲嫦驅(qū)ο蟮脑O(shè)計,那么在系統(tǒng)設(shè)計時肯定創(chuàng)建了很多刻畫具體對象的類,使用JavaBean就可以直接操作getter方法來獲取內(nèi)容。就像是hibernate中的PO一樣。下面來說一種如何獲取Bean中屬性名稱和屬性類型的方法,這在開發(fā)時可能會用到。
先定義一個JavaBean,刻畫用戶模型嗎,如下:
package ibatis.model;
public class User implements java.io.Serializable {
private Integer userId;
private String userName;
private String password;
private String mobile;
private String email;
public User() {
super();
}
public User(Integer userId, String userName, String password,
String mobile, String email) {
super();
this.userId = userId;
this.userName = userName;
this.password = password;
this.mobile = mobile;
this.email = email;
}
// 省略getter和setter方法
@Override
public String toString() {
return "User [email=" + email + ", mobile=" + mobile + ", password="
+ password + ", userId=" + userId + ", userName=" + userName
+ "]";
}
}
寫一個方法來測試,如下:public static void main(String[] args) {
try {
PropertyDescriptor[] pd = Introspector.getBeanInfo(User.class).getPropertyDescriptors();
for (int i = 0; i < pd.length; i++) {
System.out.println(pd[i].getName() + " ("
+ pd[i].getPropertyType().getName() + ")");
}
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
在控制臺,我們獲得如下輸出:class (java.lang.Class)
email (java.lang.String)
mobile (java.lang.String)
password (java.lang.String)
userId (java.lang.Integer)
userName (java.lang.String)
在定位BUG時,這是很好的一種手段。
接下來,我們來說一下三個常用的查詢方法,它們的命名和Spring的JdbcTemplate/SqlMapClientTemplate很像,但是要區(qū)分開。
首先是queryForObject()方法,它返回數(shù)據(jù)庫查詢的一條結(jié)果,并放入到Java對象中,這里的一條記錄可以是一個JavaBean,也可以是Java的集合類型。它可以根據(jù)<select>標(biāo)簽中配置的resultClass屬性來確定的,如果不指定resultClass屬性,那么查詢結(jié)果就是null了,因?yàn)閕Batis不知道怎么處理這個結(jié)果,而且我們也沒有配置結(jié)果映射(resultMap)。
首先我們根據(jù)上面的User類型,將resultClass設(shè)置為User,代碼如下:
<sqlMap namespace="User">
<typeAlias alias="User" type="ibatis.model.User" />
<select id="getUserByName" parameterClass="java.lang.String"
resultClass="User">
select *
from users
where USERNAME=#VARCHAR#
</select>
</sqlMap>
這時要求User類中必須要有一個默認(rèn)的構(gòu)造方法,否則將不能實(shí)例化這個對象,拋出異常,這一點(diǎn)不能忘記(如果重載了構(gòu)造方法的話)。我們寫一個程序:System.out
.println(sqlMap.queryForObject("User.getUserByName", "sarin").getClass().getName());
此時,輸出內(nèi)容為:ibatis.model.User,這就很清楚的看到了,查詢的結(jié)果類型是由<select>中的resultClass來確定的。
queryForObject()的另外一個重載方法是Object queryForObject(String id, Object parameter, Object resultObject) throws Exception,這種方法是為對象不能輕易創(chuàng)建的情況使用的(如沒有默認(rèn)的構(gòu)造方法的對象),那么使用前面那種格式就會拋出異常,就需要使用這種方法,看下面代碼:(這里去掉User類中的默認(rèn)構(gòu)造方法)
User user=new User(null, null, null, null, null);
user = (User) sqlMap.queryForObject("User.getUserByName", "sarin",
user);
System.out.println(user);
這樣才能獲得user對象。
第二個方法是queryForMap()方法,返回結(jié)果可以是一條,也可以是多條。它的方法簽名有兩種形式:第一種是Map queryForMap(String id, Object parameter, String key) throws SQLException,第二種是再多一個參數(shù)String value。前面兩個參數(shù)好理解,就是select標(biāo)簽的id和傳入的參數(shù),而后面的key和value是什么意思呢?key指定了Map中存儲的鍵,而value確定了存儲的值,不設(shè)置value時則存儲查詢的一個對象,如下面代碼(此時已經(jīng)將select的resultClass設(shè)置為hashmap了):
Map map = sqlMap.queryForMap("User.getAllUsers", null,"userId");
System.out.println(map);
正如你所想,這段代碼的輸出為: {1={email=gmail@gmail.com, userId=1, userName=sarin, password=123, mobile=15940912345}, 2={email=gmail@gmail.com, userId=2, userName=sarin, password=123, mobile=15940912345}}
這里的1和2就是key,是鍵,那么它們是什么類型的呢?我們使用如下代碼來看看: System.out.println(map.keySet().iterator().next().getClass());
得到結(jié)果:class java.lang.Integer,說明這是字段相對應(yīng)的,因?yàn)檫@里我們沒有將查詢結(jié)果和JavaBean相關(guān)聯(lián)。那么HashMap中存儲的value是什么類型呢?我們來看,代碼如下:System.out.println(map.get(1).getClass());
打印得到:class java.util.HashMap,說明存儲的還是HashMap。而queryForMap()的第二個重載方法則是指定了value的內(nèi)容,我們來看: Map map = sqlMap.queryForMap("User.getAllUsers", null, "userId",
"mobile");
System.out.println(map);
這將打?。簕1=15940912345, 2=15940912345},這回就清楚了吧,而且得到的mobile的類型是String,也就容易理解了。記住一點(diǎn),queryForMap()方法返回的可以是一條也可以是多條記錄。但是在實(shí)踐中往往用它來獲取一條完整的記錄,那么使用Map的get()方法就能獲取到其中的值了,非常方便。
下面來看queryForList()方法,同樣,該方法的方法簽名也有兩類形式:第一類是queryForList(String id, Object parameter) throws SQLException,或者不需要參數(shù),這很好理解了。看個例子:(SqlMap中的resultClass設(shè)置為hashmap)
List users = sqlMap.queryForList("User.getAllUsers");
System.out.println(users);
打印的結(jié)果是:[{email=gmail@gmail.com, userId=1, userName=sarin, password=123, mobile=15940912345}, {email=gmail@gmail.com, userId=2, userName=sarin, password=123, mobile=15940912345}]
就是List中裝入的是HashMap對象,在SqlMap中將hashmap換為User,那么得到:[User [email=gmail@gmail.com, mobile=15940912345, password=123, userId=1, userName=nanlei], User [email=gmail@gmail.com, mobile=15940912345, password=123, userId=2, userName=sarin]]
queryForList()的第二類方法是queryForList(String id, Object parameter, int skip, int max) throws SQLException,可以看出后面多了兩個int類型的參數(shù),那么SQL中使用兩個int類型的參數(shù)能干什么?分頁,沒錯,這是主要應(yīng)用。iBatis在queryForList()中提供了為分頁提供支持的方法。記著skip是從0開始計算的,而max就是取出的條數(shù),那么取前10條就是(0,10),取11~20條就是(10,10),以此類推。
更多建議: