ibatis介紹
ibatis簡介:
ibatis一詞來源于“internet”和“abatis”的組合,是一個由Clinton Begin在2001年發起的開放源代碼項目。初側重于密碼軟件的開發,現在是一個基于Java的持久層框架。
將“Internet”中象征性的“i”和abatis中的“batis”組合所以暗示了抵御Internet的意思。
相對Hibernate和Apache OJB等“一站式”ORM解決方案而言,ibatis 是一種“半 自動化”的ORM實現。
ibatis需要手寫sql語句,也可以生成一部分,Hibernate則基本上可以自動生成,偶爾會寫一些Hql。同樣的需求, ibatis的工作量比Hibernate要大很多。類似的,如果涉及到數據庫字段的修改,Hibernate修改的地方很少,而ibatis要把那些sql mapping的地方一一修改。
可維護性方面,ibatis更好一些。因為 iBatis 的 sql 都保存到單獨的文件中。而 Hibernate 在有些情況下可能會在 java 代碼中保sql/hql。
如果你開發一個新系統,希望完全控制對象模型,進行數據庫設計,那么Hibernate是一個好的選擇;如果你工作在一個很古老的數據庫系統上,數據庫設計非常糟糕,你又沒有權利去改變數據庫設計,那么iBATIS更合適一些。
因為ibatis是以SQL的方式開發,所以ibatis自身是不能動態生成表的,當然可以把SQL預先寫入xml中,但是這種方式在開發中使用率并不高,并且以后不斷的建表會比較繁瑣,所以通常情況下,我們需要首先創建表,然后再使用ibatis來操作數據,在這方面,就沒有hibernate建表那么容易。
ibatis簡單入門
ibatis快速開發:
創建一個java項目,然后導入驅動包到lib文件夾下面,我們需要的驅動包有ibatis-2.3.4.726.jar和classes12.jar,然后添加一個SqlMapConfig.xml到src下面,在此xml中寫入以下內容:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<transactionManager type="JDBC" commitRequired="false">
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="JDBC.ConnectionURL" value="jdbc:oracle:thin:@localhost:1521:ORAC"/>
<property name="JDBC.Username" value="test"/>
<property name="JDBC.Password" value="test"/>
</dataSource>
</transactionManager>
<sqlMap resource="com/cn/thinkmore/bean/BuMen_SqlMap.xml"/>
</sqlMapConfig>
然后創建一個pojo,對應數據庫中的一個表,例如:
package com.cn.thinkmore.bean;
public class BuMen {
private String bid;
private String bumenname;
private String bumencode;
public BuMen(){}
public String getBumencode() {
return bumencode;
}
public void setBumencode(String bumencode) {
this.bumencode = bumencode;
}
public String getBid() {
return bid;
}
public void setBid(String bid) {
this.bid = bid;
}
public String getBumenname() {
return bumenname;
}
public void setBumenname(String bumenname) {
this.bumenname = bumenname;
}
}
接著創建此pojo的映射xml文件,名字自定義,盡量pojo類名_SqlMap命名,我在這里的創建的POJO類名叫BuMen,所以我創建此xml文件叫BuMen_SqlMap.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap>
<select id="selectAll" resultClass="com.cn.thinkmore.bean.BuMen">
select bid,bumenname,bumencode from BuMen
</select>
</sqlMap>
后,我們來創建一個帶有main方法的測試類:
package com.cn.thinkmore.bean;
import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;
import java.util.List;
import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
public class TestConsole {
private static SqlMapClient sqlMapClient;
static {
try {
Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close();
} catch (IOException e) {
// Fail fast.
throw new RuntimeException("Something bad happened while building the SqlMapClient instance." + e, e);
}
}
public static void main(String[] args) {
try {
List list = sqlMapClient.queryForList("selectAll");
System.out.println(list);
for(Object obj:list){
BuMen bm = (BuMen)obj;
System.out.println("bid="+bm.getBid()+" bumenname="+bm.getBumenname()+" bumencode="+bm.getBumencode());
}
}catch (SQLException e) {
e.printStackTrace();
}
}
}
后運行成功,這樣我們就快速搭配起來了ibatis的環境了。
ibatis核心技術
ibatis配置文件---sqlMapConfig.xml:
下面,我們來詳細介紹sqlMapConfig中的常用標簽:
<sqlMapConfig>
配置文件的父標簽,所有標簽必須寫在此標簽內部。
<properties/>
引用屬性文件,用于引用標準的propertis屬性文件。
在屬性文件中定義的屬性,可以作為變量在配置文件及其包含的所有映射文件中引用,例如:如果在屬性文件中包含如下屬性:
url=jdbc:oracle:thin:@localhost:1521:ORAC
那么就可以在xml文件中,這樣調用:
<property name=" JDBC.ConnectionURL" value="${url}"/>
<settings/>
設置屬性,用于配置和優化SqlMap實例的各種屬性,此標簽本身及其所有的屬性都是可選的。下面是它的內部屬性介紹:
maxRequests:同時執行SQL語句的大線程數。大于這個值的時候,后面的線程將被阻塞,直到有線程執行完成,不同的數據庫有不同的限制值,但是任何數據庫通常應該把此屬性值設置為maxTransactions的10倍,并且總是大于maxSessions和maxTransactions,減少此值可能會提高性能(默認值 512)
maxSessions:同一時間內活動的大Session數,一個Session可以是代碼請求的顯式Session,也可以是當線程使用SqlMapClient實例(執行一條語句)自動獲得的Session,它應該總是大于或者等于maxTransactions
并且小于maxRequests,減小這個參數值可能會減少內存使用(默認值 128)
maxTransactions:同時進入SqlMapClient.startTransaction()的大線程數,大于這個值的線程將阻塞直到另一個線程退出。不同的數據庫有不同的限制值,但任何數據庫都有這些限制,這個參數值應該總是小于或等于maxSessions,并總是遠遠小于maxRequests,減小這個參數值可能會提高性能(默認值 32)
cacheModelsEnabled:全局性的啟用或禁用SqlMapClient的所有緩存Model,在調試程序時使用(默認值 true)
lazyLoadingEnabled:全局性的啟用或禁用SqlMapClient的所有延遲加載,在調試程序時使用(默認值 true)
useStatementNamespaces:如果啟用本屬性,則必須使用全限定名來引用執行命令,例如:sqlMapClient.queryForObject("映射文件名.方法名");
(默認值 false)
<transactionManager/>
定義事務管理器,包含一個唯一的屬性type,用以指定使用的事務管理器類型。有三種不同的選擇:JDBC,JTA,EXTERNAL
JDBC:讓jdbc來管理事務(常用)
JTA:使用JTA全局事務
EXTERNAL:可以讓開發人員來管理事務
<dataSource/>
定義數據源,type="SIMPLE"定義一個基本的實現,基于ibatis的SimpleDataSource連接池實現,其別名為SIMPLE
<property/>
設置數據源參數
JDBC.Driver JDBC驅動程序類名
JDBC.ConnectionURL 數據庫URL
JDBC.Username 數據庫用戶名
JDBC.Password 數據庫密碼
JDBC.DefaultAutoCommit 連接池中所有連接默認的自動提交模式(默認為true)
Pool.MaximumActiveConnections 數據庫連接池可維持的大容量 默認值為10
Pool.MaximumIdleConnections 數據庫連接池中允許的掛起連接數,默認值為5
<sqlMap/>
引用映射文件
POJO映射文件---SqlMap.xml:
<sqlMap namespace="BuMen">
此配置文件的父標簽,所有標簽都需要配置在此標簽內部,它可以設定一個命名空間,但是通常沒有作用。
<typeAlias alias="BuMen" type="com.cn.thinkmore.bean.BuMen"/>
為POJO設置一個別名,為了在使用SQL標簽的時候引用,例如:
<select id="selectOne" resultClass="BuMen">
type屬性是指定POJO的包名+類名,alias屬性是給此POJO定義一個別名,為以后調用
<resultMap id="BuMenResult" class="BuMen">
<result property="bid" javaType="java.lang.String" column="bid" jdbcType="varchar2" nullValue="null"/>
<result property="bumenname" column="bumenname"/>
<result property="bumencode" column="bumencode"/>
</resultMap>
設置結果集映射,優先選用resultClass而不是resultMap,此標簽大多時候用于存儲過程的處理
id:引用的值
class:映射的POJO
result標簽是指定每一個屬性
property:指定關聯的屬性名
javaType:指定屬性的類型
column:指定此屬性關聯的字段名
jdbcType:指定字段的類型
nullValue:當此屬性沒有取到值的時候,指定的默認值
通常情況下,只需要設置property和column屬性。
下面介紹在ibatis中怎么操作數據庫的增刪改查,對應的標簽分別是insert, delete,update,select,在這四個標簽中,都有id,resultClass,parameterClass常用屬性:
id:指定要調用的名字
parameterClass:指定傳入參數的類型,例如:parameterClass="com.cn.thinkmore.bean.BuMen",也可以設定為基本類型
parameterMap:指定傳入參數的類型,(很少使用)
resultClass:指定返回的類型,例如:
resultClass="com.cn.thinkmore.bean.BuMen",也可以設定為基本類型
resultMap:指定返回的類型,(很少使用)
注意:為了避免特殊符號與XML的標簽沖突,所以有時候需要使用
<![CDATA[select * from BuMen where bid<#bid#]]>的方式來編寫SQL
<select id="selectAll" resultClass="com.cn.thinkmore.bean.BuMen">
select bid,bumenname,bumencode from BuMen
</select>
定義一個SQL的插入語句,ibatis會自動根據select語句中的字段名,調用對應POJO 的set方法設定屬性值,如上例中,ibatis會調用setBid,setBumenname,setBumencode 方法將Select語句返回的數據,裝載到相應的POJO實例屬性中。
<insert id="insertObj" parameterClass="com.cn.thinkmore.bean.BuMen">
insert into BuMen(bid,bumenname,bumencode) values (#bid#,#bumenname#,#bumencode#)
</insert>
定義一個SQL的插入語句,ibatis會自動根據select語句中的字段名,調用對應POJO 的get方法設定屬性值,如上例中,ibatis會調用getBid,getBumenname,getBumencode 方法將POJO實例屬性中的值插入了SQL語句中。
<delete id="deleteObj" parameterClass="int">
delete from BuMen where bid=#bid#
</delete>
定義一個SQL的刪除語句,parameterClass屬性定義傳入的類型為Integer類型,它會自動添加到#bid#位置,現在的#bid#不會再調用set或者get,ibatis會自動把它當做填充符使用。
<update id="updateObj" parameterClass="bumen">
update bumen set bid=#bid#,bumenname=#bumenname#,bumencode=#bumencode# where bumencode=#bumencode#
</update>
定義一個SQL的更新語句。
使用SqlMapClient操作
SqlMapClient的開發:
每一個Dao或者Service都會有一個SqlMapClient的實例,通常情況,我們并不希望外部能訪問此實例對象,所以我們需要這樣做:
private static SqlMapClient sqlMapper;
為了讓程序在開始的時候就可以操作數據庫,所以,我們還需要一個靜態方法來自動創建這個實例:
static {
try {
Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
sqlmapClient = SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close();
} catch (IOException e) {
// Fail fast.
throw new RuntimeException("Something bad happened while building the SqlMapClient instance." + e, e);
}
}
這樣做了以后,我們就可以創建我們想操作數據庫的方法了。
我們在第二章里面已經學習怎樣快速搭建ibatis并且把它使用起來了,現在來說說,開發中,有哪些方法可以讓我們操作,這些方法都需要SqlMapClient的實例來調用。
1. sqlmapClient.queryForList(String id) 基于一個id指定的sql來執行查詢,返回一個List
2. sqlmapClient.queryForList(String id,Object param) 基于一個id和一個字符串參數來執行查詢,返回一個List
3. sqlmapClient.queryForList(String id,Object pojo) 基于一個id和一個pojo參數來執行查詢,返回一個List
4. sqlmapClient.queryForList(String id,Object map) 基于一個id和一個map參數來執行查詢(適合于多參查詢的時候使用),返回一個List
5. sqlmapClient.queryForObject(String id) 基于一個id來執行查詢,返回一個Object,此方法也有4種常用方式,用法和queryForList一樣,只是返回值只有一個。
6. sqlmapClient.update(String id) 基于一個id來執行更新,返回一個Integer值決定執行了多少條記錄,此方法也有4種常用方式,和queryForList類似,只是返回值是執行的結果個數
7. sqlmapClient.insert(String id) 和update類似
8. sqlmapClient.delete(String id) 和update類似
事務管理
SqlMapClient中的事務:
sqlMapClient.startTransaction();
開始事務
sqlMapClient.commitTransaction();
提交事務
sqlMapClient.endTransaction();
結束事務,操作失敗的時候,整個事務就會在endTransaction時回滾。
新版的ibatis中,不再有rollbackTransaction方法,只能選擇endTransaction代替。
如果代碼沒有顯式的調用SqlMapClient.startTransaction()方法,則ibatis會將當前的數據庫操作視為自動提交模式(AutoCommit=true),不過,值得注意的是,這里的所謂“自動判定”,實際上ibatis并沒有去檢查當前是否已經有事務開啟。
實際上,在執行update語句時,sqlMap會檢查當前的Session是否已經關聯了某個
數據庫連接,如果沒有,則取一個數據庫連接,將其AutoCommit屬性設為true,然后
執行update 操作,執行完之后又將這個連接釋放。這樣,上面兩次update 操作實際上
先后獲取了兩個數據庫連接,而不是我們通常所認為的兩次update 操作都基于同一個
JDBC Connection。這點在開發時需特別注意。
對于多條SQL 組合而成的一個JDBC 事務操作而言,必須使用startTransaction、commit和endTransaction操作以實現整體事務的原子性。
注意!事務不能嵌套。在調用commit或end方法之前,從同一線程多次調用startTransaction將引起拋出例外。換句話說,對于每個SqlMap實例,每個線程多只能打開一個事務。
深入SqlMap.xml中的SQL開發
增刪改查4種SQL的深入化開發:
<select id="selectLike" parameterClass="java.lang.String" resultClass="bumen">
select * from BuMen where bumencode like '%$bumencode$%'
</select>
當我們需要使用模糊查詢的時候,通配符需要特殊處理,否則無法查詢到想要的數據
<select id="selectUser" parameterClass="bumen" resultClass="int">
select count(*) from BuMen where bid=#bid# and bumenname=#bumenname#
</select>
我們可以使用聚集函數來查詢,返回類型定義為int類型,也就是java.lang.Integer類型,這里我們傳入的參數是一個POJO類型,ibatis會自動get需要的屬性值,不用的屬性值不會get出來,這樣做雖然可以實現查詢,但是看來有點浪費資源。
<resultMap id="BuMenResult" class="BuMen">
<result property="bid" javaType="java.lang.String" column="bid" jdbcType="varchar2" nullValue="null"/>
<result property="bumenname" column="bumenname"/>
<result property="bumencode" column="bumencode"/>
</resultMap>
<select id="selectUser" parameterClass="bumen" resultMap="BuMenResult">
select count(*) from BuMen where bid=#bid# and bumenname=#bumenname#
</select>
和上面的查詢是一樣的效果,只是返回的方式有所改變,我們這次選用了resultMap來返回,這樣做的好處是可以讓其他開發人員來看懂POJO和數據庫中表的對應關系。
<select id="selectAll2" parameterClass="int" resultClass="bumen">
<![CDATA[select bid as id,bumenname as name,bumencode as code from BuMen where bid<#bid#]]>
</select>
數據庫表中的字段名和POJO中的屬性名不相同的時候,我們希望字段映射到POJO時,我們可以通過Select的as 字句對字段名進行轉義,這樣就會調用POJO的setId,setName,SetCode方法裝載數據。并且小于符號是xml中的關鍵字,使用會報錯,所以,我們還需要<![CDATA[……]]>的方式來編寫SQL語句。
<update id="updateUser" parameterClass="java.util.Map">
update bumen
set
bumenname=#bumenname#,
bumencode=#bumencode#
where bid = #bid#
</update>
這里傳入的參數就是一個Map對象,ibatis將以key "bid"、"bumenname"、"bumencode"從中提取對應的參數值,
此方式大部分時候用于嵌套查詢,或者多表查詢,把要查詢的條件以Map拼接。
此方式還可以用于任意的增刪改查SQL中,例如,我們要查詢一個用戶的名字和密碼是否存在的時候。
<select id="getUsers" parameterClass="bumen" resultClass="bumen">
select bid,bumenname,bumencode from bumen
<dynamic prepend="where">
<isNotEmpty prepend="and" property="bumenname">
(bumenname=#bumenname#)
</isNotEmpty>
<isNotEmpty prepend="and" property="bumencode">
(bumencode=#bumencode#)
</isNotEmpty>
</dynamic>
</select>
在很多時候,可能我們需要一些變化的sql語句,比如說,當用戶輸入幾個關鍵字查詢的時候,我們需要判斷用戶到底輸入了幾個關鍵字,然后通過if-else的方式,組建不同的SQL語句來操作數據庫,但是這樣做的話,會產生大量的代碼或者配置文件內容,但是我們在使用ibatis的時候,可以避免這個問題,我們通過dynamic節點,定義了一個動態的where子句。此where子句中將可能包含兩個針對bumenname和bumencode 字段的判斷條件。而這兩個字段是否加入查詢取決于用戶所提供的查詢條件(字段是否為空[isNotEmpty])。
由于ibatis會自動判定是否需要追加prepend前綴,這里(bumenname=#bumenname#)是where 子句中的第一個條件子句,無需and前綴,所以自動省略。
當用戶沒有傳入參數的時候,我們可以讓ibatis使用全表查詢,也就是上面配置中的select bid,bumenname,bumencode from bumen語句,如果用戶傳入了一個參數查詢的時候,我們可以讓ibatis基于一個條件查詢,也就是上面配置中的
select bid,bumenname,bumencode from bumen where bumenname=#username#語句,如果用戶傳入了兩個參數查詢的時候,我們可以讓ibatis基于兩個條件查詢,也就是上面配置中的
select bid,bumenname,bumencode from bumen where bumenname=#username# and bumencode=#bumencode#語句,通過這樣的方式,我們就可以靈活的操作用戶的查詢規則,而不必要去增加太多的代碼和配置內容,下面還有幾種判定標簽可以使用。
<isPropertyAvailable> 參數類中是否提供了此屬性
<isNotPropertyAvailable> 與<isPropertyAvailable>相反
<isNull> 屬性值是否為NULL
<isNotNull> 與<isNull>相反
<isNotEmpty> 屬性值是否為空。
<isEmpty> 與<isNotEmpty>相反
二元判斷(使用方式和dynamic節點一樣,只是加入了一個屬性compareValue,它可以指定一個比較值):
例如:
<isEqual prepend="AND" property="bid" compareValue="18">
(bid=#bid#)
</isEqual>
上面的作用是如果bid屬性等于18(compareValue),則在SQL中加入(bid=#bid#)條件。
同樣,二元判斷也有及各種判斷標簽供我們選擇。
<isEqual> 相等。
<isNotEqual> 不等。
<isGreaterThan> 大于
<isGreaterEqual> 大于等于
<isLessThan> 小于
<isLessEqual> 小于等于