ibatis 使用文件

LarryHai6發表於2015-06-17

(程式碼下載地址:http://download.csdn.net/detail/partner4java/4760043

iBATIS一詞來源於“internet”和“abatis”的組合,是一個基於Java的持久層框架。
建立於2002年,後很快加入了Apache,但是2010年又易主到google code,並改名為MyBatis。
本篇文章主要是基於iBATIS來進行展示,後面會再發一篇和MyBatis的對比。
(本文大多摘自《iBATIS in action》一書,若有時間學習此書,建議不要看本文--因為書中更詳細,且本文可能存在技術點上的錯誤分析)

==================================================================================================
第1章 iBATIS的理念

(導讀:本章主要介紹iBATIS框架的設計理念,可能會隨帶幾個iBATIS的展示,目前不需要關心如何去書寫或程式碼規範,只需要對概念有所瞭解即可)

SQL(Structured Query Language,結構化查詢語言)已經存在很長一段時間了。而且在接下來的很長一段時間內還會繼續存在。
iBATIS的建立正是基於這樣的思想:關聯式資料庫和SQL仍然很有價值,在整個產業範圍內對SQL投資仍然是一個非常好的注意。
我們可能都曾有過這樣的經歷,應用程式的原始碼(即使發展了很多版本)隨著時間的流逝最終還是過時了,但他的資料庫甚至是SQL本身卻仍然很有價值。
在某些情況下我們也會看到一個應用程式已經被其他的語句重寫了,但背後的SQL和資料庫卻基本上保持不變。

正是基於這些原因,iBATIS並不試圖去隱藏SQL或者避免使用SQL。
相反,正是iBATIS這個持久層框架廣泛使用了SQL,他使得SQL更容易使用、更容易整合到現代的物件導向軟體中。

混合型解決方案在IT領域同樣被證明是非常有效的。iBATIS就是這樣一個混合型的持久層解決方案。

一:探索iBATIS的根源
iBATIS是一個混合型解決方案。他吸取了SQL、老式儲存過程(“兩層應用程式”)、現代儲存過程、內聯SQL、動態SQL、ORM這些解決方案中最有價值的思想並將他們融會貫通。

iBATIS提供的與其他解決方案相同的優點:
-------------------------------------------------------------------------------------------------------------------------


iBATIS框架的核心價值:外部化SQL和封裝SQL

二:外部化SQL
“總是將一個大系統設計為多個子系統,每個子系統的功能都相對集中”。
應該儘可能的降那些由不同的開發角色處理的任何分離開。
外部化將SQL從應用程式的原始碼中分離出來,從而使得兩者都更加清晰。
這樣就保證了SQL語句與任何特定的語言或平臺都想對的獨立。
在以前的書寫方式中,在程式碼中拼寫SQL,容易引起空格等錯誤,特別是如果你有一條複雜的SQL語句。
這時iBATIS的一個主要優點就體現出來了:使用iBATIS你就擁有按照最自然的方式寫SQL的能力。

  1. select   
  2.     name,  
  3.     age  
  4. from user  
  5. where address=#address#  


三:封裝化的SQL
計算機程式設計領域一個最古老的概念就是模組化。
iBATIS使用XML來封裝SQL。
[html] view plaincopy
  1. <select id="categoryById"   
  2.     parameterClass="string" resultClass="category">  
  3.     select categoryid,name,description  
  4.     from category  
  5.     where categoryid=#categoryId#  
  6. </select>  

四:iBATIS適合應用在何處
幾乎所有結構良好的軟體都使用了分層設計。
iBATIS是一個持久層框架。持久層位於應用程式的業務邏輯層和資料庫之間。這種分離非常重要,他保證了持久化策略不會混入業務邏輯程式碼,反之亦然。
這種分離的好處在於你的程式碼更將加容易維護,因為他允許物件模型的轉化不依賴於資料庫設計。

==================================================================================================
第2章 iBATIS是什麼

(導讀:本章主要介紹iBATIS框架是什麼,並簡單附帶了示例,目前仍不需要關心如何去書寫或程式碼規範,只需要對概念有所瞭解即可)

概述:
iBATIS就是我們通常所說的資料對映器(data mapper)。
所謂對映器層,是用於在物件和資料庫之間搬運資料,同時保證物件、資料庫以及對映器本身都相互獨立。

iBATIS與O/RM不同,他不是直接把類對映為資料庫表或者說把類的欄位對映為資料庫列,而是把SQL語句的引數和結果對映為類。

iBATIS在資料庫和類之間建立了一個額外的間接層,這就為在類和資料庫直接建立對映關係帶來了更大的靈活性,使得不用改變資料庫模型或者物件模型的情況下改變他們的對映關係成為可能。(這裡的間接層就是SQL)

一:對映SQL語句(我們來觀賞下iBATIS的簡單對映方式)
任何一條SQL語句都可以看做是一組輸入和輸出。輸入即引數(parameter),通常可以在SQL語句的WHERE子句中找到。輸出是SELECT子句中指定的那些列。

iBATIS使用一個簡單的XML描述符檔案來對映SQL語句的輸入和輸出。

示例:
[html] view plaincopy
  1. <select id="getAddress" parameterClass="int" resultClass="Address">  
  2.     select   
  3.         ADR_ID              as  id,  
  4.         ADR_DESCRIPTION     as  description  
  5.     from address  
  6.     where ADR_ID = #id#  
  7. </select>  

示例程式碼中包含一條SQL SELECT語句,他返回地址資料。
從<select>元素中可以看出一個Integer物件作為引數,該引數是通過WHERE子句中的#id#符號標記的。
結果是一個Address類的物件例項,假設Address類的所有欄位的名與SELECT語句中指定的各個列的別名相同,會被自動對映為相應欄位。
Address address = (Address)sqlMap.queryForObject("getAddress",new Integer(8));

SQL對映這個概念具有很好的可移植性,可應用於任何一個功能完備的程式語言。

二:iBATIS如何工作(上面簡單示例是如何工作的呢?)
Address address = (Address)sqlMap.queryForObject("getAddress",new Integer(8));
這行程式碼會執行相應的SQL語句,設定其引數,並以一個真實的Java物件的形式作為結果返回。
SQL語句被“乾乾淨淨”地封裝在Java程式碼之外的一個XML檔案中。iBATIS負責管理所有的幕後資源。

三:為何使用iBATIS
簡單性、生產效率、效能、關注點分離、明確分工、可移植性、開源和誠實。

四:何時不該使用iBATIS
1.當對資料庫永遠擁有完全控制權時
當你對資料庫具有完全控制權時,就有充分的理由使用一個完全的物件/關係對映方案,如Hibernate。

2.當應用程式需要完全動態的SQL時

3.當沒有使用關聯式資料庫時

4.當iBATIS不起作用時

==================================================================================================
第3章 iBATIS HelloWorld

(導讀:目前你只需要跟著我做每一步,能寫個helloworld,至於具體每個配置是什麼含義,後面章節我們會一一道來)

一:HelloWorld

我們建立一個依賴於架構的專案,在腦海裡應該很快的想到應該需要幾個重要步驟:匯入相關jar包;配置需要配置檔案;如果和資料庫相關還需要建立相應資料庫。

第一步:建立專案並匯入相關jar
建立一個專案(建議建立一個Maven專案 -- eclipse中可安裝maven外掛即可,具體操作請查閱相關資料)
所需jar:
ibatis2-common.jar -- 共享的iBATIS類
該檔案包含SQL對映框架和DAO框架中要用到的公共元件。
ibatis2-sqlmap.jar -- iBATIS的SQL對映類
該檔案包含SQL對映框架的所有元件
ibatis2-dao.jar -- 該檔案包含DAO框架的所有元件
mysql-connector-java -- 具體要根據你使用的資料庫而定

如果你建立的是一個非Maven專案,可直接匯入jar:
(下載地址)
http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22ibatis2-common%22
http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22ibatis2-dao%22
http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22ibatis2-sqlmap%22
http://search.maven.org/#search%7Cga%7C1%7Cmysql-connector-java

Maven專案:
[html] view plaincopy
  1. <dependencies>  
  2.     <dependency>  
  3.         <groupId>com.ibatis</groupId>  
  4.         <artifactId>ibatis2-common</artifactId>  
  5.         <version>2.1.7.597</version>  
  6.     </dependency>  
  7.   
  8.   
  9.     <dependency>  
  10.         <groupId>com.ibatis</groupId>  
  11.         <artifactId>ibatis2-dao</artifactId>  
  12.         <version>2.1.7.597</version>  
  13.     </dependency>  
  14.   
  15.   
  16.     <dependency>  
  17.         <groupId>com.ibatis</groupId>  
  18.         <artifactId>ibatis2-sqlmap</artifactId>  
  19.         <version>2.1.7.597</version>  
  20.     </dependency>  
  21.   
  22.   
  23.     <dependency>  
  24.         <groupId>mysql</groupId>  
  25.         <artifactId>mysql-connector-java</artifactId>  
  26.         <version>5.1.21</version>  
  27.     </dependency>  
  28. </dependencies>  

第二步:建立配置檔案
在我們的類路徑下建立xml檔案:sql-map-config.xml
這是一個主配置檔案(大家想下是不是我們平時用到的框架也需要一個主的配置檔案?iBAITS也不例外)

(你先考進去或對比敲進去,然後改下資料庫配置 -- 後面會詳細介紹各配置的作用)
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2.   
  3.   
  4. <!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"  
  5.     "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">  
  6.   
  7.   
  8. <sqlMapConfig>  
  9.     <transactionManager type="JDBC">  
  10.         <!-- SIMPLE是一個iBATIS內建事務管理器的名字 -->  
  11.         <dataSource type="SIMPLE">  
  12.             <property value="com.mysql.jdbc.Driver" name="JDBC.Driver" />  
  13.             <property  
  14.                 value="jdbc:mysql://localhost:3306/ibatis_test?autoReconnect=true"  
  15.                 name="JDBC.ConnectionURL" />  
  16.             <property value="root" name="JDBC.Username" />  
  17.             <property value="123456" name="JDBC.Password" />  
  18.         </dataSource>  
  19.     </transactionManager>  
  20. </sqlMapConfig>  

第三步:建立表
我這裡用的mysql
  1. CREATE TABLE `user_account` (  
  2.   `userid` int(11) NOT NULL AUTO_INCREMENT,  
  3.   `username` varchar(20) DEFAULT NULL,  
  4.   `passwordvarchar(20) DEFAULT NULL,  
  5.   `groupname` varchar(20) DEFAULT NULL,  
  6.   PRIMARY KEY (`userid`)  
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  8.   
  9.   
  10. INSERT INTO `user_account` (`userid`, `username`, `password`, `groupname`) VALUES   
  11.   (1, 'LMEADORS''PICKLE''EMPLOYEE'),  
  12.   (2, 'JDOE''TEST''EMPLOYEE');  

第四步:編寫程式碼 -- 資料庫對應的POJO
像Hibernate一樣寫個資料庫對應的POJO
[java] view plaincopy
  1. public class Account implements Serializable {  
  2.     private int userid;  
  3.     private String username;  
  4.     private String password;  
  5.     private String groupname;  
  6. ...  

第五步:建立另一個SQL對映檔案
我們是不是前面強調了很多次,iBATIS主要功能是把SQL和程式碼進行分離?所以,我們需要一個地方存放SQL。
這裡我們繼續建立一個xml,這個xml可與具體表對應,也可與具體業務對應,總之,它就是一組相關SQL存放的位置。

本實體具體建立Account.xml(我們現在也是把此檔案放在了類路徑)
繼續考入或參考敲入到你的xml檔案中
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2.   
  3.   
  4. <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"  
  5.     "http://ibatis.apache.org/dtd/sql-map-2.dtd">  
  6.   
  7.   
  8. <sqlMap namespace="Account">  
  9.     <!-- 給我們的返回型別定義了一個短名稱 -- account  -->  
  10.     <typeAlias alias="account" type="com.partner4java.demo.entity.Account" />  
  11.       
  12.     <!-- 我們寫了一個select,查詢語句就是被<select>名稱對應的標籤所包圍;parameterClass為我們的傳入引數值型別(string為系統預設定義的String短名稱);resultClass返回型別;然後就是id,沒什麼可說的,具體SQL總要有標識id。 -->  
  13.     <select id="getAllUsers" parameterClass="string" resultClass="account">  
  14.         select * from user_account where groupname=#groupname#  
  15.     </select>  
  16. </sqlMap>  

然後在sql-map-config.xml中加入
[html] view plaincopy
  1. <sqlMapConfig>  
  2.     ...  
  3.     <sqlMap resource="Account.xml"/>  
  4. </sqlMapConfig>  


第六步:編寫程式碼 -- iBATIS的具體呼叫
[java] view plaincopy
  1. public static void main(String[] args) throws Exception {  
  2.     //先建立一個Reader,為讀取的配置的封裝  
  3.     Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
  4.   
  5.   
  6.     //iBATIS API的核心是SqlMapClient介面。  
  7.     //SqlMapClient大致相當於Hibernate的Session或者JPA的EntityManager,用於執行全部的資料訪問操作。  
  8.     SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);  
  9.   
  10.   
  11.     //queryForList第一個引數為我們定義的<select>的id,第二個為傳入的查詢引數#groupname#  
  12.     List list = sqlMap.queryForList("getAllUsers""EMPLOYEE");  
  13.   
  14.   
  15.     //對獲取的結果遍歷(我對Account的toString進行了重寫)  
  16.     for (int i = 0; i < list.size(); i++) {  
  17.         System.out.println(list.get(i));  
  18.     }  
  19. }  

這樣我們就完成了一個簡單的iBATIS

二:簡述SQL Map配置檔案
(這裡只是簡述,後面章節會有詳細介紹)
SqlMapConfig.xml檔案位於最上方,我們在此檔案中定義那些全域性配置選項以及對各個獨立SqlMap檔案的引用。
SqlMap檔案則用於定義那些已對映語句(mapped statement),這些已對映語句結合應用程式提供的輸入,來完成與資料庫的互動。

SQL Map配置檔案:
SQL Map配置檔案(就是前前面的SqlMapConfig.xml)是iBATIS配置的核心(因此也成為主配置檔案)。從資料庫連線到實際所用的SqlMap檔案都是通過此檔案中的配置提供給框架的。
我們來看一個SqlMapConfig.xml檔案(比我們前面hello裡面更加完整)
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2.   
  3.   
  4. <!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"  
  5.     "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">  
  6.   
  7.   
  8. <sqlMapConfig>  
  9.   
  10.   
  11.   <properties resource="properties/database.properties"/>  
  12.     
  13.   <settings   
  14.     useStatementNamespaces="false"   
  15.     cacheModelsEnabled="true"   
  16.     enhancementEnabled="true"   
  17.     lazyLoadingEnabled="true"   
  18.     maxRequests="32"   
  19.     maxSessions="10"   
  20.     maxTransactions="5"  
  21.     />  
  22.       
  23.   <transactionManager type="JDBC">  
  24.     <dataSource type="SIMPLE">  
  25.       <property value="${driver}" name="JDBC.Driver"/>  
  26.       <property value="${url}" name="JDBC.ConnectionURL"/>  
  27.       <property value="${username}" name="JDBC.Username"/>  
  28.       <property value="${password}" name="JDBC.Password"/>  
  29.     </dataSource>  
  30.   </transactionManager>  
  31.   
  32.   
  33.   <sqlMap resource="org/apache/ibatis/jgamestore/persistence/sqlmapdao/sql/Account.xml"/>  
  34.   
  35.   
  36. </sqlMapConfig>  

settings為全域性配置選項
transactionManager為事務管理器
最下面會存在很多sqlMap,是對各個Sql Map檔案的引用

<properties>元素:
<properties>元素允許在主配置檔案之外提供一個properties格式對應檔案,從而使得主配置檔案更加通用。這樣對部署非常有用。
兩個屬性
resource--類路徑上的一個資源
url -- 統一資源定位符
最後通過名稱來引用這些屬性。

<settings>元素:
<settings>元素新增若干屬性來設定這些可選項。iBATIS有許多配置項可用,並且每個配置項對於這個SQLMap例項來說都是全域性的。
1.lazyLoadingEnabled
延遲載入是一種只載入必要資訊而推遲載入其他未明確請求的資料的技術。也就是說,除非絕對需要,否則應用程式載入的資料越少越好。
lazyLoadingEnabled配置項用於指定當存在相關聯的已對映語句時,是否使用延遲載入。其有效值為true或false,預設值為true。
2.cacheModelsEnabled
資料告訴快取是一種提高程式效能的技術,他基於近期使用過的資料往往很快又會被用到這樣一個假設,將近期使用過的資料儲存到記憶體中。
cacheModelsEnabled配置項用於指定是否想讓iBATIS使用該技術。有效值也是true或false。
為了充分利用快取記憶體技術,還必須為已對映語句配置快取記憶體模型(後面會介紹)。
3.enhancementEnabled
enhancementEnabled用於指定是否使用cglib中那些已優化的類來提高延遲載入的效能。其有效值仍然是true或false,預設值為true。
當你執行增強,但是如果cglib不在類路徑上,效能增強仍然會被悄悄的禁用。
4.useStatementNamespaces
useStatementNamespaces配置項用於告訴iBATIS,在引用已對映語句時,是否需要使用限定名(qualified name)。其有效值也為true或false,預設值為false。
也就是說,當你定義了SQL對映之後,就用該SQL對映名來限定這條已對映語句。
例如,假設你有一個名為Account的SQL對映,他包含名為insert、update等對映語句。那麼當你想插入一個賬戶且在主配置檔案中啟用了useStatementNamespaces配置項時,就必須用Account.insert這個名字來呼叫這條已對映的語句。
通過使用名稱空間,你就可以根據需要,建立任意數目名為insert的已對映語句(在其他對映檔案中),而不會照成名字衝突。
雖然我們可以使用類似insertAccount這樣的名字來完成相同的事,但是在面對大型系統時,使用名稱空間將更有用,因為即使在已對映語句的組織毫無邏輯時,名稱空間也可以幫助找到他們。
5.maxRequests(已廢棄)
例如我們設定了maxRequests="32",這樣我們的資料庫一次最多隻允許有32個請求出於活動狀態。
6.maxSessions(已廢棄)
會話(session)是一種執行緒級機制,用於跟蹤關於一組相關事務和請求的資訊。
maxSessions="10" 表示任何時候都只允許10個會話。
7.maxTransactions(已廢棄)
事務(transaction)就是指資料庫事務。
(如果確實要修改這些配置項,請確保maxRequests總是大於maxSessions,而maxSessions總是大於maxTransactions)

<transactionManager>元素:
由於iBATIS就是為了使資料庫訪問變得更加簡單而設計的,因此他將為你處理所有的資料庫事務。
使用iBATIS時,有哪些事務管理器實現類是可用的。可以考慮使用iBATIS那些預定義的事務管理器。
<transactionManager>元素的type屬性就是用於指定應該使用哪個事務管理器的。
iBATIS提供了若干內建事務管理器實現:
JDBC 用於提供簡單的基於jdbc的事務管理。大多數情況下,使用這個事務管理器就足夠了。
JTA 用於在你的應用程式中提供基於容器的事務管理。
EXTERNAL 用於提供非事務管理,並假定管理事務的是應用程式,而不是iBATIS

對於事務管理器,另一個可用的設定就是commitRequired屬性,該屬性可被設定為true或者false,預設是為false。
他主要用於那些要求在釋放某個連線之前必須提交(commit)或者回退(rollback)的情況。
對於某些操作(例如選擇和儲存過程呼叫),通常並不需要事務,因此一般會將其忽略。
某些資料庫驅動程式(例如Sybase驅動程式)不會輕易釋放連線,直至該連線的每個已啟動事務都已提交或回退。在這些情況下,就可以使用commitRequired屬性來強制釋放連線,即使在那些通常需要在一個事務中釋放卻沒用釋放的情況。

1.<property>元素
每個事務管理器都可以有不同的配置項。因此,iBATIS框架允許使用<property>元素來指定任意數目的鍵值對以提供給相應的事務管理器實現類。
2.<dataSource>元素
在java中,使用連線池的標準方法就是通過javax.sql.DataSource物件。
事務管理的<dataSource>元素有一個屬性,用來告訴iBATIS他應該為其資料來源工廠例項化並使用哪個類。
<dataSource>元素名字很容易仍人產生誤解,因為實際上他並不是用來定義DataSource,而是用來定義DataSourceFactory實現類,iBATIS將用這個實現類來建立實際的DataSource。
iBATIS具有三種資料來源工廠實現類:
SIMPLE 即簡單的資料來源工廠。它用於配置那種內建有簡單連線池的資料來源,因此除了實際的JDBC驅動程式之外,該資料來源工廠所需的其他東西都包含在iBATIS框架中。
DBCP DBCP資料來源工廠用於使用Jakarta Commons資料庫連線池實現
JNDI JNDI資料來源工廠用於允許iBATIS共享通過JNDI定位的基於容器的資料來源

<sqlMap>元素:
SqlMapConfig.xml檔案的最後一部分就是我們配置<sqlMap>元素的地方。正是此處你開始真正瞭解到iBATIS可以為你做的大量繁重的工作。
他也是支援resource和url兩種屬性,分別用於將對映檔案通過相對於類路徑根路徑和任意的URL值。

<typeHandler>元素:
在上面的示例中
iBATIS使用型別處理器(type handler)將資料從資料庫特定的資料型別轉換為應用程式中的資料型別,這樣你就可以建立一個以一種儘可能透明的方式來使用資料庫的應用程式。
型別處理器本質上就是一個翻譯器(translator)--他將資料庫返回的結果集合中的列“翻譯”為相應的JavaBean中的欄位。
如果自定義型別處理器,需要建立兩個類:一個自定義型別處理器和一個型別處理器回撥類。

三:簡述SqlMap -- Account.xml
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2.   
  3.   
  4. <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"  
  5.     "http://ibatis.apache.org/dtd/sql-map-2.dtd">  
  6.   
  7.   
  8. <sqlMap namespace="Account">  
  9.   
  10.   
  11.     <typeAlias alias="account" type="com.partner4java.demo.entity.Account" />  
  12.       
  13.     <select id="getAllUsers" parameterClass="string" resultClass="account">  
  14.         select * from user_account where groupname=#groupname#  
  15.     </select>  
  16. </sqlMap>  

<typeAlias>元素:
除非別無選擇,否則沒人願意輸入像這樣一個長串的名字:com.partner4java.demo.entity.Account。
<typeAlias>元素允許定義別名,這樣就可以直接用account來代替剛才的那個冗長的全限定類名了。
<typeAlias alias="account" type="com.partner4java.demo.entity.Account" />
當在配置過程中定義account這個別名之後,就可以在任何時候使用他了,每當你必須提供一個資料型別時,都可以使用全限定類名或者別名來告訴iBATIS你想使用哪個資料。
iBATIS為若干型別定義了別名,以免除開發人員必須手工新增這些別名的麻煩。

==================================================================================================

闡述完基本概念和helloworld,我們開始進行詳細學習。前面的內容不是很理解也沒有問題,下面通過多寫幾個Demo,將會豁然開朗。

==================================================================================================
第4章 使用已對映語句

(導讀:下面進行學習常用簡單語句如何配置和執行 -- 或者說是剖析我們前面的hello)

一:從基礎開始

問題1:我們Demo中的Account.xml檔案,裡面的<select>標籤是如何選擇的呢?
1.已對映語句的型別
已對映語句有多種型別,每種型別都有各自不同的用途、屬性集以及子元素。
但一般來講,最好使用與你要完成的任務相匹配的語句型別(例如,要插入資料應該使用<insert>語句,而不是使用<update>),雖然這似乎是理所當然的,但仍請您務必遵守。

除了已對映語句的型別之外,還包括另外兩個通常用來建立已對映語句的元素:<sql>元素和<include>元素。
聯合使用這兩個元素能夠建立可插入到已對映語句中的元件。當你想要複用複雜的SQL片段,卻又不想複製它們時,就會發現上面這兩個元素非常有用。

<sql>元素用於建立一個文字片段,而這些文字片段又可以組合起來以建立完整的SQL語句。(如圖:程式碼清單4-1.jpg)

問題2:我們無論是否藉助框架,或者藉助其他ORM框架,對錶的查詢結果是不是要封裝到Java物件中啊?

2.建立JavaBean
bean的構成 -- 本質上而言,JavaBean規範就是一組規則,用於定義Java開發可使用的元件。
這些規則使得工具建立者可以知道如何與我們在應用程式中所使用的元件進行互動。
規範可以看做是框架開發人員和應用程式開發人員的一箇中間層公共語言。

問題3:有了存放結果的物件,那麼如何查詢結果呢?
3.iBATIS API的核心是SqlMapClient介面。

SqlMapClient大致相當於Hibernate的Session或者JPA的EntityManager,用於執行全部的資料訪問操作。


核心工具類SqlMapClient介面有很多方法,我們來介紹幾個核心方法:

queryForObject()方法 --
queryForObject()方法用於從資料庫中獲取一條記錄,並把它放入到一個Java物件中。
它有以下兩種簽名:
queryForObject(String id, Object parameter)
queryForObject(String id, Object parameter,Object result)
第一種簽名較為常用,只要所返回的物件有預設構造器(default constructor),它就能為你建立它(如果沒有,就會丟擲執行時異常)。
第二種簽名接受一個將被用作返回值的物件 -- 執行已對映語句之後,該方法並不會建立一個新的物件,而是在該語句中設定特性。如果你請求的物件不能被輕易建立,那麼這種方式非常有用。
使用queryForObject()方法時需要記住的是,如果查詢返回了不止一行,該方法就會丟擲異常,因此它總會檢查以確保僅返回一行。

queryForList()方法 --
queryForList()方法用於從資料庫中返回一行或多行,並把它放入到一個Java物件列表中,和queryForObject一樣,該方法也有兩個版本
queryForList(String id, Object parameter) throws java.sql.SQLException;
queryForList(String id, Object parameter, int skip, int max) throws java.sql.SQLException;
第一個版本的方法將已對映語句所返回的所有物件全部返回。而第二個版本的方法只返回其中的一部分--它將跳到skip引數所指定的位置上,並返回從查詢結果開始的max行。

queryForMap()方法 --
queryForMap()方法用於從資料庫中返回一行或多行組成的一個Java Map。
queryForMap(String id, Object parameter, String key) throws java.sql.SQLException;
queryForMap(String id, Object parameter, String key, String value) throws java.sql.SQLException;
第一個版本的方法將執行一個查詢,並返回由一組Java物件組成的Map,其中這些物件的鍵值由key引數所指定的特性來標識,而值物件則為已對映語句返回的物件。
第二個版本的方法將返回一個型別的Map,但是這些物件將會是value引數所標識物件的特性。


二:使用select已對映語句
從資料庫中獲取資料是一個應用程式中最基本的操作之一。iBATIS框架使得編寫大部分的SELEC語句都毫不費力,並且提供了很多特性,使得你幾乎可以從資料庫中得到任何想要的資料。

問題:我們前面SQL中包含了groupname=#groupname#,這麼個東東,可以從結構上猜想這應該是傳入引數的固定格式。那麼具體如何定義和使用呢?
(兩種方式#和$)
1.使用內聯引數(用#做佔位符)
內聯引數(inline parameter)就是一種在已對映語句中新增查詢條件的簡單方式,你可以使用兩個不同的方法來設定內聯引數。
第一個方法是使用雜湊(#)符號。
[html] view plaincopy
  1. <sqlMap namespace="Account">  
  2.   
  3.   
  4.     <typeAlias alias="account" type="com.partner4java.demo.entity.Account" />  
  5.       
  6.     <select id="getAllUsers" parameterClass="string" resultClass="account">  
  7.         select * from user_account where groupname=#groupname#  
  8.     </select>  
  9. </sqlMap>  

[java] view plaincopy
  1. public static void main(String[] args) throws Exception {  
  2.     Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
  3.     SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);  
  4.     List list = sqlMap.queryForList("getAllUsers""EMPLOYEE");  
  5.     for (int i = 0; i < list.size(); i++) {  
  6.         System.out.println(list.get(i));  
  7.     }  
  8.   
  9.   
  10. }  

問題:iBATIS框架是如何處理該語句的?
首先,iBATIS將查詢名為Account.getAllUsers的已對映語句,並把#groupname#佔位符變換為一個預備語句引數,就是轉換成我們JDBC中所用的"?"格式。


2.使用內聯引數(用$做佔位符)
使用內聯引數的另一種方式就是使用替代($)語法,它可以用來把值直接插入到SQL語句之中(在該SQL語句被轉變為引數化語句之前)。
但是使用這種方式時要非常小心,因為它可能使你暴漏給SQL隱碼攻擊,另外過度使用還可能造成效能問題。
Demo:
[html] view plaincopy
  1. <select id="getUsersByGroupName" parameterClass="string" resultClass="account">  
  2.     select * from user_account where groupname like '%$groupname$%'  
  3. </select>  
[java] view plaincopy
  1. Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
  2. SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);  
  3. List list = sqlMap.queryForList("getUsersByGroupName""EMPLOYEE");  
  4. for (int i = 0; i < list.size(); i++) {  
  5.     System.out.println(list.get(i));  
  6. }  

3.SQL隱碼攻擊簡介
SQL隱碼攻擊是指惡意的使用者將特殊格式的資料傳遞給某個應用程式,以使該應用程式做一些不該做的事。
例如:上面的示例傳入
test' or 1 like '1
如果我們這個引數是密碼之類的資訊,是不是別人就能很容易“猜到”了?

問題:我們hello中返回結果的各個欄位並沒有和JavaBean中的欄位做對應啊?它是如何自動對應的呢?

4.自動結果對映
你可能已經注意到了,在所有我們給出的示例中都沒有定義任何結果對映(result map),但是確實定義了結果類(result class)。
這種方式可行是因為iBATIS的自動結果對映機制,該機制會在已對映語句第一次被執行時,迅速的自動建立一個結果對映,然後將它應用於這條已對映語句。
可以有3種方式來使用這個特性:單列選擇(single-column selection),固定多列選擇(fixed-column list selection)和動態多列選擇(dynamic-column list selection)。

單列選擇 --
如果只想從某個查詢中獲取單例,就可以使用別名value作為一種快捷方式來完成此目的,這樣就可以不需要定義複雜的結果對映了,當然你不定義別名也可以,只需要指定resultClass:
[html] view plaincopy
  1. <select id="getAllAcoountIds" resultClass="int">  
  2.     select userid as value from user_account  
  3. </select>  
  4.   
  5.   
  6. <select id="getAllAcoountIds" resultClass="int">  
  7.     select userid from user_account  
  8. </select>  

固定多列選擇 -- 
如果需要查詢多列,就可以使用自動結果對映來告訴iBATIS將列名作為bean的特性名,或者作為Map鍵。
當以這種方式對映到bean時,需要牢記一點:如果所選擇的列在資料庫中存在,但是不存在於你要對映的bean中,你將不會得到任何錯誤或警告,但是也得不到任何資料--這些資料只會靜靜的被忽略。
對映到Map物件時,也有相似的問題:儘管你仍然可以得到資料,但是這些資料不會在你所期望的地方。

動態多列選擇 -- 
如果語句中被選擇的那些欄位列表可以在執行時改變,那麼也可以使用動態結果對映。
(後面章節我們再具體說動態對映)

5.聯結相關資料
有時候出於製作報表或者其他的什麼目的,你可能想要把多個資料表聯結起來,形成一個平板型結構。
iBATIS框架可以讓你毫不費力地完成這項工作,因為它是將SQL語句對映為物件,而不是將資料庫中的表對映為物件,所以對映表查詢和對映多表查詢幾乎沒有什麼不同。


三:對映引數
對映引數 -- java程式碼傳入SQL所需引數 和 xml中配置的SQL指令碼里的傳入引數 之間的對應關係。
有兩種方式可以將引數對映到已對映語句中:內聯對映(inline mapping)和外部對映(external mapping)。
使用內聯引數對映意味著:告訴iBATIS關於你需要什麼的暗示,然後就讓它完成細節的處理。
但是外部引數對映則明確的多 -- 可以明確地告訴iBATIS你要它做些什麼。

1.外部引數對映
使用外部引數對映時,可以指定多達6個屬性。如果沒有指定其中的某個屬性,iBATIS就會用反射來儘可能的為其確定而合理的值,但是這麼做比較費時並且可能不準確。
property -- 設定一個引數時,property屬性用於指定JavaBean中某個特性的名字,或者指定作為引數傳遞給已對映語句的Map例項的某個鍵值對(Map entry)的鍵值。
此名字可以不只使用一次,具體根據它在語句中所需要的次數而定。

javaType -- 設定一個引數時,javaType屬性用來顯示指定要設定的引數的Java特性型別
通常這個型別可以通過反射從JavaBEan特性中推斷出來,但是某些對映方式並不能為框架提供這樣的型別。
在這種情況下,如果沒有設定javaType,而框架又不能通過另外的方式確定型別,就會假定型別為Object

jdbcType -- 設定一個引數時,jdbcType屬性用來顯示指定引數的資料型別。一些jdbc驅動程式在進行某些特定操作時,如果沒有顯示提供列的型別資訊,他們就不能識別出這些類的型別。
通常,jdbcType屬性只有當某列允許被設定為空時才是必需的。

nullValue -- nullValue屬性用來指定外發的控制替換。
也就是說,資料寫入時,如果在待寫入的JavaBean特性或Map鍵值對中檢測到該替換值,就將空值寫入到資料庫(反過程亦然,即從資料庫中讀取一個控制時,則將相應的JavaBean欄位或Map鍵值對的值設為該替換值)

mode -- 該屬性專門用於支援儲存過程

typeHandler -- 如果想要指定型別處理器(而不是讓iBATIS根絕javaType和jdbcType屬性來選擇型別處理器),你可以指定typeHandler屬性
通常,該屬性用來提供自定義的型別處理器

按照我們前面的用法:
[html] view plaincopy
  1. <insert id="insertAccount" parameterClass="account">  
  2.     insert into user_account(username,password,groupname ) values(#username#,#password#,#groupname#)  
  3. </insert>  

[java] view plaincopy
  1. Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
  2. SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);  
  3. sqlMap.insert("insertAccount"new Account("hello""123""EMPLOYEE"));  

這是藉助了預設的反射機制,我們還可以自己指定各種引數型別:
[html] view plaincopy
  1. <parameterMap class="account" id="accountMap">  
  2.     <parameter property="username" nullValue="hello" jdbcType="VARCHAR"/>  
  3.     <parameter property="password" nullValue="123" jdbcType="VARCHAR"/>  
  4.     <parameter property="groupname" nullValue="123" jdbcType="EMPLOYEE"/>  
  5. </parameterMap>  
  6.   
  7.   
  8. <insert id="insertAccountMap" parameterMap="accountMap">  
  9.     insert into user_account(username,password,groupname ) values(?,?,?)  
  10. </insert>  

[java] view plaincopy
  1. Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
  2. SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);  
  3. sqlMap.insert("insertAccountMap"new Account(null"789"null));  


2.再論內聯引數對映
除了使用最簡單形式的內聯引數對映,來告訴iBATIS我們想要在執行時帶入到查詢中的特性名稱,也可以在內聯引數對映中提供一些外部引數對映所允許的特性。
例如:jdbcType(資料庫型別)以及nullValue(引數的空值佔位符),只要用冒號將引數名、資料庫型別和空值佔位符分隔開即可。
當你的資料庫中包含允許為空的列時,資料庫型別通常是必須設定。如果你沒有明確告訴iBATIS究竟使用何種資料庫型別,那麼iBATIS將預設地使用java.sql.Types.OTHER作為sqlType,但一些資料庫卻東程式是不允許這麼做的。
Demo:
[html] view plaincopy
  1. <select id="getUsersByGroupName3" resultClass="account">  
  2.     select * from user_account where groupname like #groupname,jdbcType=VARCHAR#  
  3. </select>  

[java] view plaincopy
  1. Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
  2. SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);  
  3. List list = sqlMap.queryForList("getUsersByGroupName3""%EMPLOYEE%");  
  4. for (int i = 0; i < list.size(); i++) {  
  5.     System.out.println(list.get(i));  
  6. }  

3.基本型別引數
Java5中,你可以在任何地方傳遞基本型別引數。

4. JavaBean引數和Map引數
雖然bean引數和Map引數有些不同,但是使用他們時所用的語法是一致的。
這兩者的不同在於:載入引數對映時,它們的行為是不相同的。這因為這點,bean可以提前告知我們錯誤--當訪問一個不存在的特性。

四:使用內聯結果對映和顯示結果對映


問題:我們講過了引數對映,hello裡也使用了自動結果對映,但是當資料庫欄位和JavaBean屬性名字不對應怎麼辦?如何收集結果呢?(生產程式碼通常情況下,我們資料庫欄位使用下劃線分割單詞,但是javabean屬性是大小寫字母替代下換線來分割單詞。)
顯示結果對映:
內聯結果對映的確非常好,因為他們非常容易使用,並且在大多數情況下都能很順利的完成工作。
iBATIS中的顯示結果對映也同樣有價值,因為它們可以提供更好的效能、更嚴格的配置驗證,以及更加精確的行為。
顯示結果對映中可用的屬性:
property -- 設定一條結果對映時,property屬性用於指定JavaBean中某個特性的名字,或者指定作為引數傳遞給已對映語句的Map例項的某個鍵值對的鍵值。
column -- 設定一條結果對映時,column屬性用於提供ResultSet的列的名稱
columnIndex -- 是一個可選欄位,用於提供列的索引以代替ResultSet中的列名
jdbcType -- 設定一條結果對映時,jdbcType屬性用於顯示指定ResultSet列的資料庫的列型別
javaType -- 設定一條結果對映時,javaType屬性用來顯示指定要設定特性的Java特性型別。
nullValue -- 設定一條結果對映時,nullValue屬性用來替換資料庫中的空值
select -- 設定一條結果對映時,select屬性用於描述物件之間的關係,這樣iBATIS就能自動地載入複雜的特定型別;該語句特性的值必須是另外一個已對映語句的名字。

1.基本型別結果
iBATIS能把結果對映為任意型別,但是由於它只返回Object例項,基本型別的值就必須以:簡單的值包裝、bean或者Map。

2.JavaBean結果和Map結果
對於領域資料我們推薦使用bean,而對已那些不那麼關鍵並且更加動態的資料,我們推薦使用Map。

Demo:
[html] view plaincopy
  1. <resultMap class="account" id="accountRes">  
  2.     <result property="username2" column="username" nullValue="hello" jdbcType="VARCHAR"/>  
  3.     <result property="password" column="password" nullValue="123" jdbcType="VARCHAR"/>  
  4.     <result property="groupname" column="groupname" nullValue="123" jdbcType="EMPLOYEE"/>  
  5. </resultMap>  
  6. <select id="getUsersByGroupName4" parameterClass="string" resultMap="accountRes">  
  7.     select * from user_account where groupname like #groupname#  
  8. </select>  
[java] view plaincopy
  1. Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
  2. SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);  
  3. List list = sqlMap.queryForList("getUsersByGroupName4""%EMPLOYEE%");  
  4. for (int i = 0; i < list.size(); i++) {  
  5.     System.out.println(list.get(i));  
  6. }  第5章 使用高階查詢技術

    一:用已對映語句關聯物件


    問題:如果你用過Hibernate或JPA,會想到entity(實體物件 -- 資料庫對應JavaBean)之間可能存在關聯關係。如一對一、多對多等。伴隨就出現了關聯獲取技術,我們iBATIS如何做到關聯獲取呢?

    使用iBATIS可以根據相關聯的物件來定義資料模型,並且讓iBATIS立即載入到它們。
    例如:
    假設你有一個資料庫,其中Account記錄和Order記錄存在關聯,建立起這些關聯關係之後,請求Account記錄時,就會同時獲得所有關聯的Order物件。

    使用起來非常簡單,大體通過以下步驟:
    第一步:在我們的JavaBean中建立對應關係
    [java] view plaincopy
    1. public class Order implements Serializable {  
    2.     private static final long serialVersionUID = -7307764485708148107L;  
    3.   
    4.   
    5.     private Integer orderId;  
    6.     private String orderAddress;  
    7. ...  
    8.   
    9.   
    10. public class Account implements Serializable {  
    11.     private static final long serialVersionUID = 3337778507086494766L;  
    12.   
    13.   
    14.     private Integer accountId;  
    15.     private String username;  
    16.     //建立了一個包含關聯關係  
    17.     private List<Order> orders = new LinkedList<Order>();  
    18. ...  



    第二步:建立表(我用的MySQL)
    1. CREATE TABLE `orders` (  
    2.   `order_id` int(11) NOT NULL AUTO_INCREMENT,  
    3.   `order_address` varchar(20) DEFAULT NULL,  
    4.   `account_id` int(11) DEFAULT NULL,  
    5.   PRIMARY KEY (`order_id`)  
    6. ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;  
    7.   
    8.   
    9. CREATE TABLE `account` (  
    10.   `account_id` int(11) NOT NULL AUTO_INCREMENT,  
    11.   `username` varchar(20) DEFAULT NULL,  
    12.   PRIMARY KEY (`account_id`)  
    13. ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;  
    14.   
    15.   
    16. INSERT INTO `account` (`account_id`, `username`) VALUES   
    17.   (1, 'hello'),  
    18.   (2, 'world');  
    19.   
    20.   
    21. INSERT INTO `orders` (`order_id`, `order_address`, `account_id`) VALUES   
    22.   (1, 'hello1', 1),  
    23.   (2, 'hello2', 1);  


    第三步:在SqlMap中(Account.xml中)新增關聯獲取關係
    主要在resultMap中做了手腳,實現方式很簡單:
    1.書寫,我們關聯獲取時被呼叫的用於獲取關聯資料的<select>;被調取的用於獲取關聯資料的select中(也就是getOrderList中)引數使用#value#(固定格式)。
    2.包含關聯關係的resultMap標籤中,除了新增自己本身表中對應的result,額外新增對應關聯屬性的result標籤。
    3.對應關聯屬性的result標籤中,select屬性值為被調取的用於獲取關聯資料的select標籤的id。
    4.對應關聯屬性的result標籤中,column屬性值為程式碼調取獲取資料的<select>中“主表”的關聯欄位名稱。
    (<result property="orders" column="account_id" select="getOrderList" />)

    [html] view plaincopy
    1. <sqlMap namespace="Account">  
    2.     <typeAlias alias="account" type="com.partner4java.demo1.bean.Account" />  
    3.     <typeAlias alias="order" type="com.partner4java.demo1.bean.Order" />  
    4.   
    5.   
    6.     <!-- 簡單關聯方案 -->  
    7.     <resultMap class="account" id="accountMap">  
    8.         <result property="accountId" column="account_id" />  
    9.         <result property="username" column="username" />  
    10.         <!-- 通過select來獲取的值放入orders;select需要一個關聯引數,為本處column -->  
    11.         <result property="orders" column="account_id" select="getOrderList" />  
    12.     </resultMap>  
    13.   
    14.   
    15.     <resultMap class="order" id="orderMap">  
    16.         <result property="orderId" column="order_id" />  
    17.         <result property="orderAddress" column="order_address" />  
    18.     </resultMap>  
    19.   
    20.   
    21.     <select id="getAccount" resultMap="accountMap">  
    22.         select * from account  
    23.     </select>  
    24.   
    25.   
    26.     <select id="getOrderList" resultMap="orderMap">  
    27.         select * from orders where  
    28.         account_id = #value#  
    29.     </select>  
    30. </sqlMap>  


    第四步:執行程式碼
    [java] view plaincopy
    1. public static void main(String[] args) throws Exception {  
    2.     Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
    3.     SqlMapClient sqlMapClient = SqlMapClientBuilder  
    4.             .buildSqlMapClient(reader);  
    5.     List<Account> accounts = sqlMapClient.queryForList("getAccount"null);  
    6.     for(Account account:accounts){  
    7.         System.out.println(account);  
    8.     }  
    9. }  
    10. 列印:  
    11. Account [accountId=1, username=hello, orders=[Order [orderId=1, orderAddress=hello1], Order [orderId=2, orderAddress=hello2]]]  
    12. Account [accountId=2, username=world, orders=[]]  

    延遲載入:
    要使用延遲載入,需要編輯SqlMapConfig.xml檔案,通過在<setting>元素中將lazyLoadingEnabled屬性改為true來啟動它。
    如果想要使用延遲載入的cglib增強版,則必須下載並將其新增到應用程式的類路徑中,同時也必須將<setting>元素中的enhancementEnabled屬性改為true。
    必須注意的是,這是一個全域性設定,因此如果啟用了這些特性,SQL對映中所有的已對映語句都將會使用延遲載入。

    問題:你是否會揣摩到上面的查詢方式會出現“N+1”問題呢,那麼如何解決“N+1”問題?
    避免N+1問題:
    [html] view plaincopy
    1. <!-- N + 1查詢方案,注意每個resultMap都新增了一個groupBy -->  
    2. <resultMap class="order" id="orderJMap" groupBy="orders.order_id">  
    3.     <result property="orderId" column="order_id" />  
    4.     <result property="orderAddress" column="order_address" />  
    5. </resultMap>  
    6.   
    7.   
    8. <resultMap class="account" id="accountJMap" groupBy="account.account_id">  
    9.     <result property="accountId" column="account_id" />  
    10.     <result property="username" column="username" />  
    11.     <!-- 這裡的resultMap為我們上面對order的定義 -->  
    12.     <result property="orders" resultMap="Account.orderJMap" />  
    13. </resultMap>  
    14.   
    15.   
    16. <select id="getAccountJ" resultMap="accountJMap">  
    17.     select  
    18.     account.account_id,account.username,orders.order_id,orders.order_address  
    19.     from account join orders on account.account_id = orders.account_id  
    20.     order  
    21.     by account.account_id,orders.order_id  
    22. </select>  

    [java] view plaincopy
    1. public static void main(String[] args) throws Exception {  
    2.     Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
    3.     SqlMapClient sqlMapClient = SqlMapClientBuilder  
    4.             .buildSqlMapClient(reader);  
    5.     List<Account> accounts = sqlMapClient.queryForList("getAccountJ"null);  
    6.     for(Account account:accounts){  
    7.         System.out.println(account);  
    8.     }  
    9. }  

    但是這種方式會丟失不存在order的Account,可以使用left join。


    問題:當我執行的不是一個簡單insert、update時如何選擇標籤?
    iBATIS框架的設計意圖就是要靈活。當無法使用其他型別的已對映語句時,也許就可以使用<statement>已對映語句。
    1.使用語句型別和DDL
    <statement>型別的已對映語句有點奇怪,它和其他型別(例如<insert>)的已對映語句不同的地方就是,它沒有對應的方法可以呼叫。
    這就暗示了,我們不鼓勵使用<statement>型別的已對映語句,只有在別無選擇的情況下才可以考慮使用它。

    <statement id="dropTableTest">
    drop table test;
    </statement>

    sqlMapClient.update("dropTableTest", null);


    ==================================================================================================
    第6章 事務

    一:事務是什麼


    用最簡單的話來說,事務就是一項通常包含若干步驟的工作單元,這些步驟必須作為整體來執行,無論成功還是失敗。
    也就是說,如果一個事務中的任何一個步驟失敗了,那麼所有其他步驟必須回滾,以保證資料仍處於一致的狀態。

    示例:就是常用的兩個賬戶的轉賬的問題。

    支援事務的資料庫需要具備的特性通常稱為ACID:原子性(atomicity)、一致性(consistency)、隔離性(isolation)和永續性(durability)。


    二:自動事務


    與JDBC還有所謂的“自動提交”模式不同,iBATIS處理的只有事務,它根本就沒有在一個事務之工作的概念(除非你資料庫不支援事務),因此它對“自動提交”的支援其實完全是間接的。
    作為“自動提交”的替代,iBATIS支援所有的自動事務。
    自動事務允許使用單個的方法呼叫來執行單個更新語句活查詢語句,而完全不用關心如何劃分事務。


    三:區域性事務
    所謂區域性事務,它僅僅涉及一個應用程式、一種資源(例如關聯式資料庫),並且一次只能處理一個事務。

    首先配置:
    [html] view plaincopy
    1. <transactionManager type="JDBC">  
    2.     <!-- SIMPLE是一個iBATIS內建事務管理器的名字 -->  
    3.     <dataSource type="SIMPLE">  
    4. ...  

    [java] view plaincopy
    1. public static void main(String[] args) throws Exception {  
    2.     Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
    3.     SqlMapClient sqlMapClient = SqlMapClientBuilder  
    4.             .buildSqlMapClient(reader);  
    5.       
    6.     //學過jdbc的,是不是很容易理解啊?  
    7.     sqlMapClient.startTransaction();  
    8.       
    9.     long being = System.currentTimeMillis();  
    10.     try {  
    11.         sqlMapClient.startBatch();  
    12.         for(int i=0;i<1000;i++){  
    13.             Account account = new Account("en5" + i, "o5""EMPLOYEE");  
    14.             sqlMapClient.insert("insertAccount", account);  
    15.         }  
    16.         sqlMapClient.executeBatch();  
    17.         sqlMapClient.commitTransaction();  
    18.     } catch (Exception e) {  
    19.         e.printStackTrace();  
    20.     }finally{  
    21.         sqlMapClient.endTransaction();  
    22.     }  
    23.     System.out.println("time:" + (System.currentTimeMillis() - being));  
    24.       
    25. }  

    定製事務:
    我們也可以獲取一個conn,然後使用conn手工管理事務,不過我們建議在基於conn管理事務的基礎上使用sqlMapClient來執行SQL操作。
    [java] view plaincopy
    1. public static void main(String[] args) throws Exception {  
    2.     Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
    3.     SqlMapClient sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);  
    4.     Connection conn = null;  
    5.     SqlMapSession session = null;  
    6.     try {  
    7.         conn = sqlMapClient.getDataSource().getConnection();  
    8.         conn.setAutoCommit(false);  
    9.         session = sqlMapClient.openSession(conn);  
    10.         session.insert("insertAccount"new Account("hello""123""EMPLOYEE"));  
    11.           
    12.         conn.commit();  
    13.     } catch (Exception e) {  
    14.         if(conn != null) conn.rollback();  
    15.         e.printStackTrace();  
    16.     }finally{  
    17.         if(session != null){  
    18.             session.close();  
    19.         }  
    20.         if(conn != null){  
    21.             conn.close();  
    22.         }  
    23.     }  
    24. }  

    (區域性事務就不多說了,你自己打一遍就明白了)

    ==================================================================================================
    第7章 使用動態SQL

    一:先來個hello

    [html] view plaincopy
    1. <select id="queryAccount" resultClass="account" parameterClass="string">  
    2.     select * from user_account   
    3.     <!-- 就多了一個這種標籤,看這標籤的起名是不是很像我們Junit裡的斷言啊? -->  
    4.     <isNotNull>  
    5.         where groupname = #value#  
    6.     </isNotNull>  
    7. </select>   

    [java] view plaincopy
    1. public static void main(String[] args) throws Exception {  
    2.     Reader reader = Resources.getResourceAsReader("sql-map-config.xml");  
    3.     SqlMapClient sqlMapClient = SqlMapClientBuilder  
    4.             .buildSqlMapClient(reader);  
    5.     List<Account> accounts = sqlMapClient  
    6.             .queryForList("queryAccount""EMPLOYEE");  
    7.     for (Account account : accounts) {  
    8.         System.out.println(account);  
    9.     }  
    10. }  


    也可以:使用<dynamic>標籤作為父標籤
    <dynamic>標籤不像其他動態SQL標籤,它不會去評測(evaluate)任何值或狀態。它通常只是使用prepend屬性,該屬性值將作為字首被加到<dynamic>標籤的內容體之前。
    如果<dynamic>標籤的內容體經iBATIS處理後沒有產生任何文字,那麼prepend值將被忽略。
    [html] view plaincopy
    1. <select id="queryAccount" resultClass="account" parameterClass="string">  
    2.     select * from user_account   
    3.     <dynamic>  
    4.         <isNotNull>  
    5.             where groupname = #value#  
    6.         </isNotNull>  
    7.     </dynamic>  
    8. </select>  

    二:熟悉動態標籤

    所有標籤分為5類:
    <dynamic>標籤、二元標籤、一元標籤、引數標籤以及<iterate>標籤。

    先看一下所有的動態SQL標籤都有的屬性和行為。
    open、close:無條件地將其屬性值放在標籤的結果內容的開始處和結束處。
    prepend:除了dynamic之外,prepend屬性在所有其他標籤中的功能也都是一樣的。<dynamic>標籤在內容體的結果SQL非空時,總是將其prepend屬性值新增為該結果SQL的字首。

    只有當你想要向據誒過內容體使用open、close或prepend值時,才必須使用<dynamic>標籤。


    <dynamic>標籤:
    <dynamic>標籤是最頂層標籤;這意味著它不能被巢狀。該標籤用來換分一個動態的SQL片段。
    <dynamic>標籤的屬性:
    prepend(可選的):該值用於作為字首新增到標籤的結果內容體前。但是當標籤的結果內容體為空時,prepend值將不起作用。
    open(可選的):該值用於作為字首新增到標籤的結果內容體前。但是如果結果內容體為空,open將不會附加到前面。open值將在prepend屬性值被新增字首先被新增字首。
    close(可選的):該值用於作為字尾附加到標籤的結果內容體後。如果標籤的結果內容體為空,close值將不起作用。

    iBATIS二元標籤:
    二元標籤(binary tag)用於將引數特性的某個值同另外一個值或者引數特性做比較。
    如果比較結果為true,那麼結果SQL中就包含其內容體。
    所有的二元標籤都共享compareProperty特性、compareValue屬性。
    property屬性用於設定被比較的基本型別值,而compareProperty屬性和compareValue屬性則用於設定比較的參考值。
    compareProperty屬性會指定引數物件中的一個特性,該欄位的取值會作為比較時的引數。
    compareValue屬性則會指定一個靜態值,用於比較的基本型別值。
    二元標籤的屬性:
    property(必需的):引數物件中用於同compareProperty或者compareValue想比較的特性。
    prepend(可選的):該值用於作為字首附加到標籤的結果內容體前。只有以下情況prepend值不會被加為字首:當標籤的結果內容體為空時;如果該標籤是第一個產生內容體,且它被巢狀在一個removeFirstPrepend屬性被設定為true的父標籤中時。
    open(可選的):該值用於作為字首新增到標籤的結果內容體前。如果標籤的結果內容體為空,open值將不會被附加到其前面。open值將在prepend屬性值被新增為字首之前先被新增字首。
    close(可選的):該值用於作為字尾附加到標籤結果內容體後。如果標籤的結果內容體為空,則close值將不起作用。
    removeFirstPrepend(可選的):該值用於確定第一個巢狀的內容生產標籤是否移除其prepend值(prepend是可選屬性)。
    compareProperty(如果沒有指定compareValue,則它是必需的):該值指定引數物件中的一個特性用來同property屬性所指定的特性相比較。
    compareValue(如果沒有指定compareProperty,則它是必需的):該值指定一個靜態比較值用於同property屬性所指定的特性相比較。

    二元動態標籤:
    <isEqual>:將property屬性同compareProperty屬性或compareValue屬性相比較,確定他們是否相同。
    <isNotEqual>:將property屬性同compareProperty屬性或compareValue屬性相比較,確定他們是否不同。
    <isGreaterThan>:確定property屬性是否大於compareProperty屬性或compareValue屬性。
    <isGreaterEqual>:確定property屬性是否大於等於compareProperty屬性或compareValue屬性。
    <isLessThan>:確定property屬性是否小於compareProperty屬性或compareValue屬性。
    <isLessEqual>:確定property屬性是否小於等於compareProperty屬性或compareValue屬性。

    iBATIS一元標籤:
    一元標籤(unary tag)用於直接考察引數物件中某個bean特性的狀態,而不是與其他進行比較。
    如果引數物件的狀態結果為真,那麼結果SQL中就會包含其內容體。
    所有的一元標籤都共享property屬性。一元標籤的property屬性用於指定引數物件上用來考察狀態的特性。

    一元標籤屬性:
    property(必需的):引數物件中用於狀態比較的特性。
    prepend(可選的):該值用於作為字首附加到標籤的結果內容體前。只有以下情況prepend值不會被加為字首:當標籤的結果內容體為空時;如果該標籤是第一個產生內容體,且它被巢狀在一個removeFirstPrepend屬性被設定為true的父標籤中時。
    open(可選的):該值用於作為字首,新增到結果內容體前。如果結果內容體為空,open值將不會被附加到其前面。open值將在prepend屬性值被新增字首之前先被新增字首。
    close(可選的):該值用於作為字尾附加到標籤的結果內容體後。如果結果內容體為空,則close值將不起作用。
    removeFirstPrepend(可選的):該屬性值用於決定第一個產生內容的巢狀子標籤是否移除其prepend值。

    一元標籤:
    <isPropertyAvailable>:確定引數物件中是是否存在所指定的欄位。對於bean,它尋找一個特性;而對於map,它尋找一個鍵。
    <isNotPropertyAvailable>:確定引數中是否不存在所指定的欄位。對於bean,它尋找一個特性;而對於map,它尋找一個鍵。
    <isNull>:確定所指定的欄位是否為空。對於bean,它尋找獲取方法特性的返回值;而對於map,它尋找一個鍵,若這個鍵不存在,則返回true。
    <isNotNull>:確定所指定的欄位是否為非空。對於bean,它尋找獲取方法特性的返回值;而對於map,它尋找一個鍵,若這個鍵不存在,則返回false。
    <isEmpty>:確定所指定的欄位是否為空、空字串、空集合或者空的String.valueOf()。
    <isNotEmpty>:確定所指定的欄位是否為非空、非空字串、非空集合或者非空的String.valueOf()。

    iBATIS引數標籤:
    考慮到iBATIS允許定義沒有引數的已對映語句。
    引數標籤(parameter tag)就是用來檢查某個特定引數是否被傳遞給了已對映語句。
    引數標籤的屬性和前面大同小異。
    <isParameterPresent>:確定引數物件是否出現。
    <isNotParameterPresent>:確定引數物件是否不存在。

    iBATIS<iterate>標籤:
    <iterate>標籤以一個集合或陣列型別的特性作為其property屬性值,iBATIS通過遍歷這個集合(陣列)來從一組值中重複產生某種SQL小片段。
    這些小片段以conjunction屬性值作為分隔符連線起來,從而形成一個有意義的SQL語句片段,open屬性值將作為所呈現的值列表的字首,close屬性值將作為所呈現的值列表的字尾,最終形體形成一個完成合法的SQL。
    <iterate>標籤屬性:
    conjunction(可選的):該值用於連線iBATIS遍歷集合(陣列)時重複產生那些SQL小片段。
    其它屬性基本一致。

    ==================================================================================================
    第8章 使用快取記憶體提高效能

    (導讀:建議你看的同事自己動手練,有什麼疑問自己用各種方式試出來)
    (測試的建議:想看下是不是快取,可在兩次查詢中做一個執行緒等待,然後去修改資料庫,等待結束後,看資料是否改變,當然前提是你的快取時間長於你的兩次列印時間間隔,且修改工作在等待過程中完成)

    一:一個簡單的iBATIS快取記憶體示例

    iBATIS的強健且簡單的快取記憶體機制是完全基於配置的,因此避免了直接使用快取記憶體的負擔。
    [html] view plaincopy
    1. <!-- type="MEMORY",指定其快取記憶體型別為MEMORY,把查詢結果直接儲存在記憶體中。 -->  
    2. <cacheModel type="MEMORY" id="categoryCache">  
    3.     <!-- flushOnExecute標籤用於指定當某個特定快取記憶體被訪問時,其儲存結果將被清除。 -->  
    4.     <flushOnExecute statement="insert" />  
    5.     <flushOnExecute statement="update" />  
    6.     <flushOnExecute statement="delete" />  
    7.     <!-- 每一種型別的快取記憶體模型都有一些專用於它自己配置的特性,property標籤就是用於完成這些專用特性的設定的。那麼屬性指定該快取記憶體模型將要設定的特性的名稱,value指定該預定義特性的值。 -->  
    8.     <property name="reference-type" value="WEAK" />  
    9. </cacheModel>  
    10.   
    11.   
    12. <select id="queryAccount" resultClass="account" parameterClass="string"  
    13.     cacheModel="categoryCache">  
    14.     select * from user_account  
    15.     <dynamic>  
    16.         <isNotEmpty>  
    17.             where groupname = #value#  
    18.         </isNotEmpty>  
    19.     </dynamic>  
    20. </select>  

    二:理解快取記憶體模型

    有關快取記憶體模型,最簡單的描述就是,它是一種快取記憶體配置。或者更確切的說,它是定義所有的iBATIS快取記憶體實現的基礎。
    快取記憶體模型的配置是在SQL Map配置檔案中,通過<cacheModel>標籤來定義。
    <cacheModel>標籤屬性:
    id(必需的):該值用於指定一個唯一的ID,便於為需要使用此快取記憶體模型所配置的快取記憶體的查詢已對映語句所引用。
    Type(必需的):此屬性用於指定高數快取模型所配置的快取記憶體的型別。其有效值包括MEMORY、LRU、FIFO和OSCACHE。該屬性也可取值為某個自定義CacheController實現的全限定類名。
    readOnly(可選的):將該值設定為true,就表示快取記憶體將僅僅被用作只讀快取記憶體。
    serialize(可選的):該屬性值指定在讀取快取記憶體內容時是否要進行“深複製”

    1.type屬性 -- 內建的各種快取記憶體模型型別:
    MEMORY:這個模型簡單的將快取記憶體儲存在記憶體中,直至垃圾收集器將它移除。
    FIFO:這個模型中,快取記憶體的資料量是固定的,使用“先進先出(first in first out)”演算法來移除快取記憶體中的資料。
    LRU:這個模型中,快取記憶體的資料量也是固定的,使用“最近最少使用(least recently used)”演算法來移除快取記憶體中的資料。
    OSCACHE:這個模型使用OpenSymphony快取記憶體

    要使用何種快取記憶體實現,是通過將它們相應的預設關鍵字(MEMORY、LRU、FIFO以及OSCACHE)新增到<cacheModel>標籤的type屬性來指定的。
    也可以自己實現CacheController介面,然後在type屬性中指定其全限定類名,以提供一套自己的快取記憶體方案。

    2.readOnly屬性:
    <cacheModel>標籤提供了一個readOnly屬性。
    該屬性僅僅是一個為快取記憶體模型提供指令的指示器,用於告訴快取記憶體模型應該如何檢索和儲存已告訴快取物件。
    該屬性設定為true,並不能保證從快取記憶體中檢索出的物件其內容不被改變。
    指定一個快取記憶體為只讀時,也就是告訴快取記憶體模型允許其返回對存在於快取記憶體中的某個物件的引用,因為該物件將不會被正在請求它的應用程式所改變。
    如果readOnly屬性設定為false,就可以確保不會出現多個使用者同時訪問某個已快取記憶體的物件的同一引用例項的情況。

    3.serialize屬性:
    serialize屬性用於指示已快取記憶體物件應該如何返回。
    當該屬性被設定為true時,快取記憶體中所請求的每個物件都將作為一個深拷貝被返回。
    這就意味著從快取記憶體中檢索出來的物件只具有相同的值,但並不是同一個例項。
    這就可以確儲存儲在快取記憶體中的實際資料永遠不會被改變。
    需要注意的是,此處所謂的序列化並不是我們所學的“序列化”,它並不是把資料序列化到硬碟上。
    這裡的序列化是基於記憶體的序列化,用於建立記憶體中已快取記憶體物件的深拷貝。

    4.聯合使用readOnly屬性和serialize屬性:
    它們看上去在功能上視乎存在一些重疊,事實上,它們需要緊密協同才能正常工作。
    readOnly serialize結果原因
    true false可以最快速的檢索出已快取記憶體的物件。返回已快取記憶體物件的一個共享例項,若使用不當可能會導致問題。
    false true  好 能快速檢索出已快取記憶體物件。返回已快取記憶體物件的一個深拷貝。
    false false  警告 對於此種組合,快取記憶體僅僅對呼叫執行緒的會話的宣告週期有關,且不能被其他執行緒所使用。
    true true  壞 這種組合同readOnly=false而serialize=true的組合作用一致,否則它在語義上沒有任何意義。

    以上兩個屬性的預設組合是readOnly=true和serizalize=false。  


    三:如何使用快取記憶體模型中的標籤
    1.快取記憶體的清除
    <flushOnExecute> -- 定義查詢已對映語句,其執行將引起相關快取記憶體的清除
    <flushInterval> -- 定義一個時間間隔,快取記憶體將以此間隔定期清除

    <flushOnExecute>標籤:
    只有一個屬性statement,當某條已對映語句執行時,將觸發相應快取記憶體的清除。
    當你在需要必須更新快取記憶體以保證與資料庫一致是尤其有用。

    <flushInterval>標籤:
    除了時間之外它沒有任何配置上的依賴性。
    每個一定的時間間隔就會清除一次快取記憶體。

    2.設定快取記憶體模型實現的特性:
    <property>標籤的屬性name和value,都是必需的。
    它們用來構建一個可傳遞給快取記憶體模型元件以供其初始化的Properties物件。

    四:快取記憶體模型的型別
    MEMORY:
    MEMORY快取記憶體是一種基於引用的快取記憶體。
    快取記憶體中的每個物件都賦予一個引用型別。此引用型別為垃圾收集器提供了線索,指導它如何處理相應的物件。
    引用型別:
    WEAK -- 將很快的廢棄快取記憶體的物件。這種引用不會阻止物件被垃圾收集器收集。它僅僅提供一種方式來訪問快取記憶體中的物件,該物件在垃圾收集器的第一遍收集中就會被移除。(預設)如果因為某種原因GC比較頻繁,那麼會帶來頻繁訪問資料庫的問題。
    SOFT -- 除非確定需要更多的記憶體,否則垃圾收集器始終不會收集物件。SOFT引用也確保不會超過記憶體限制,和WEAK相比,其資料庫訪問頻率會低些。
    STRONG -- 其中的已快取記憶體物件永遠不會被廢棄,除非到達了指定清除時間間隔。
    通過<property name="reference-type" value="WEAK" />來指定期望使用的引用型別。

    LRU:
    使用最近最少使用策略來管理快取記憶體。
    只有當快取記憶體超過指定大小限制約束條件時,才會廢棄最近最少被訪問的物件。
    大小限制定義了快取記憶體中可以包含的物件數目。
    <property name="size" value="200" />

    FIFO:
    才用先進先出的管理策略。
    用法和收集點同上。
    (<property name="size" value="200" />)

    OSCACHE:
    需要依賴OSCache的jar。並放入OSCache需要的配置檔案。

    ==================================================================================================
    第9章 結合Spring

    先從配置檔案下手,然後書寫我們的service,最後呼叫測試

    建立工程,我們Maven配置如下:
    pom.xml
    [html] view plaincopy
    1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
    3.     <modelVersion>4.0.0</modelVersion>  
    4.     <groupId>com.partner4java.spring</groupId>  
    5.     <artifactId>5spring</artifactId>  
    6.     <version>0.0.1-SNAPSHOT</version>  
    7.   
    8.   
    9.     <properties>  
    10.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
    11.         <spring.version>3.1.1.RELEASE</spring.version>  
    12.         <ibatis.version>2.1.7.597</ibatis.version>  
    13.     </properties>  
    14.   
    15.   
    16.     <dependencies>  
    17.         <dependency>  
    18.             <groupId>org.springframework</groupId>  
    19.             <artifactId>spring-context</artifactId>  
    20.             <version>${spring.version}</version>  
    21.         </dependency>  
    22.         <dependency>  
    23.             <groupId>org.springframework</groupId>  
    24.             <artifactId>spring-web</artifactId>  
    25.             <version>${spring.version}</version>  
    26.         </dependency>  
    27.         <dependency>  
    28.             <groupId>org.springframework</groupId>  
    29.             <artifactId>spring-aop</artifactId>  
    30.             <version>${spring.version}</version>  
    31.         </dependency>  
    32.         <dependency>  
    33.             <groupId>org.springframework</groupId>  
    34.             <artifactId>spring-orm</artifactId>  
    35.             <version>${spring.version}</version>  
    36.         </dependency>  
    37.   
    38.   
    39.         <dependency>  
    40.             <groupId>org.apache.ibatis</groupId>  
    41.             <artifactId>ibatis-sqlmap</artifactId>  
    42.             <version>2.3.4.726</version>  
    43.         </dependency>  
    44.         <dependency>  
    45.             <groupId>org.apache.ibatis</groupId>  
    46.             <artifactId>ibatis-core</artifactId>  
    47.             <version>3.0</version>  
    48.         </dependency>  
    49.   
    50.   
    51.         <dependency>  
    52.             <groupId>mysql</groupId>  
    53.             <artifactId>mysql-connector-java</artifactId>  
    54.             <version>5.1.21</version>  
    55.         </dependency>  
    56.         <dependency>  
    57.             <groupId>c3p0</groupId>  
    58.             <artifactId>c3p0</artifactId>  
    59.             <version>0.9.1.2</version>  
    60.         </dependency>  
    61.   
    62.   
    63.         <dependency>  
    64.             <groupId>cglib</groupId>  
    65.             <artifactId>cglib</artifactId>  
    66.             <version>2.2.2</version>  
    67.         </dependency>  
    68.   
    69.   
    70.         <dependency>  
    71.             <groupId>org.aspectj</groupId>  
    72.             <artifactId>aspectjweaver</artifactId>  
    73.             <version>1.6.9</version>  
    74.         </dependency>  
    75.   
    76.   
    77.         <dependency>  
    78.             <groupId>junit</groupId>  
    79.             <artifactId>junit</artifactId>  
    80.             <version>4.10</version>  
    81.             <scope>test</scope>  
    82.         </dependency>  
    83.     </dependencies>  
    84.   
    85.   
    86. </project>  

    第一步:建立資料庫相關配置檔案

    在類路徑下建立:jdbc-c3p0.properties
    [html] view plaincopy
    1. driverClass=com.mysql.jdbc.Driver  
    2. jdbcUrl=jdbc:mysql://localhost:3306/ibatis_test?useUnicode=true&characterEncoding=UTF-8  
    3. user=root  
    4. password=123456  
    5.   
    6.   
    7. #初始化時獲取的連線數,取值應在minPoolSize與maxPoolSize之間。Default: 3  
    8. initialPoolSize=1  
    9.   
    10.   
    11. #連線池中保留的最小連線數  
    12. minPoolSize=1  
    13.   
    14.   
    15. #連線池中保留的最大連線數。Default: 15  
    16. maxPoolSize=300  
    17.   
    18.   
    19. #最大空閒時間,60秒內未使用則連線被丟棄。若為0則永不丟棄。Default: 0  
    20. maxIdleTime=60  
    21.   
    22.   
    23. #當連線池中的連線耗盡的時候c3p0一次同時獲取的連線數。Default: 3  
    24. acquireIncrement=5  
    25.   
    26.   
    27. #每60秒檢查所有連線池中的空閒連線。Default: 0  
    28. idleConnectionTestPeriod=120  
    29.   
    30.   
    31. #定義在從資料庫獲取新連線失敗後重復嘗試的次數。Default: 30  
    32. #acquireRetryAttempts=30  
    33.   
    34.   
    35. #兩次連線中間隔時間,單位毫秒。Default: 1000  
    36. #acquireRetryDelay=1000  
    37.   
    38.   
    39. #連線關閉時預設將所有未提交的操作回滾。Default: false  
    40. #autoCommitOnClose=false  
    41.   
    42.   
    43. #JDBC的標準引數,用以控制資料來源內載入的PreparedStatements數量。但由於預快取的statements   
    44. #  屬於單個connection而不是整個連線池。所以設定這個引數需要考慮到多方面的因素。   
    45. #  如果maxStatements與maxStatementsPerConnection均為0,則快取被關閉。Default: 0  
    46. #maxStatements=100  
    47.   
    48.   
    49. #maxStatementsPerConnection定義了連線池內單個連線所擁有的最大快取statements數。Default: 0   
    50. #maxStatementsPerConnection=  
    51.   
    52.   
    53. #c3p0是非同步操作的,緩慢的JDBC操作通過幫助程式完成。擴充套件這些操作可以有效的提升效能   
    54. # 通過多執行緒實現多個操作同時被執行。Default: 3  
    55. #numHelperThreads=3  
    56.   
    57.   
    58. #當使用者呼叫getConnection()時使root使用者成為去獲取連線的使用者。主要用於連線池連線非c3p0   
    59. #  的資料來源時。Default: null  
    60. #overrideDefaultUser=root  
    61.   
    62.   
    63. #與overrideDefaultUser引數對應使用的一個引數。Default: null  
    64. #overrideDefaultPassword=  
    65.   
    66.   
    67. #定義所有連線測試都執行的測試語句。在使用連線測試的情況下這個一顯著提高測試速度。注意:   
    68. #  測試的表必須在初始資料來源的時候就存在。Default: null  
    69. #preferredTestQuery=select id from test where id=1  
    70.   
    71.   
    72. #使用者修改系統配置引數執行前最多等待300秒。Default: 300  
    73. #propertyCycle=300  
    74.   
    75.   
    76. #因效能消耗大請只在需要的時候使用它。如果設為true那麼在每個connection提交的   
    77. #  時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable   
    78. #  等方法來提升連線測試的效能。Default: false  
    79. #testConnectionOnCheckout=false  
    80.   
    81.   
    82. #如果設為true那麼在取得連線的同時將校驗連線的有效性。Default: false  
    83. #testConnectionOnCheckin=true  
    84.   
    85.   
    86. #c3p0將建一張名為Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個引數那麼   
    87. #屬性preferredTestQuery將被忽略。你不能在這張Test表上進行任何操作,它將只供c3p0測試   
    88. #使用。Default: null  
    89. #automaticTestTable=Test  
    90.   
    91.   
    92. #獲取連線失敗將會引起所有等待連線池來獲取連線的執行緒丟擲異常。但是資料來源仍有效   
    93. #  保留,並在下次呼叫getConnection()的時候繼續嘗試獲取連線。如果設為true,那麼在嘗試   
    94. #  獲取連線失敗後該資料來源將申明已斷開並永久關閉。Default: false  
    95. #breakAfterAcquireFailure=false  
    96.   
    97.   
    98. #當連線池用完時客戶端呼叫getConnection()後等待獲取新連線的時間,超時後將丟擲   
    99. #  SQLException,如設為0則無限期等待。單位毫秒。Default: 0  
    100. #checkoutTimeout=5000  
    101.   
    102.   
    103. #通過實現ConnectionTester或QueryConnectionTester的類來測試連線。類名需制定全路徑。   
    104. #  Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester  
    105. #connectionTesterClassName=  
    106.   
    107.   
    108. #指定c3p0 libraries的路徑,如果(通常都是這樣)在本地即可獲得那麼無需設定,預設null即可   
    109. #  Default: null  
    110. #factoryClassLocation=  
    111.   
    112.   
    113. #maxConnectionAge=400  

    第二步:建立我們Spring的DataSource(不多說,不懂去查閱Spring相關資料)

    建立檔案datasource-c3p0.xml,放入類路徑下的META-INF/spring中
    [html] view plaincopy
    1. <?xml version="1.0" encoding="GBK"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    4.     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"  
    5.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
    6.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
    7.            http://www.springframework.org/schema/context  
    8.            http://www.springframework.org/schema/context/spring-context-2.5.xsd  
    9.            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
    10.            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">  
    11.       
    12.     <context:property-placeholder location="classpath:jdbc-c3p0.properties" />  
    13.     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
    14.         <property name="driverClass" value="${driverClass}"/>  
    15.         <property name="jdbcUrl" value="${jdbcUrl}"/>  
    16.         <property name="user" value="${user}"/>  
    17.         <property name="password" value="${password}"/>  
    18.         <property name="initialPoolSize" value="${initialPoolSize}"/>  
    19.         <property name="minPoolSize" value="${minPoolSize}"/>   
    20.         <property name="maxPoolSize" value="${maxPoolSize}"/>  
    21.         <property name="maxIdleTime" value="${maxIdleTime}"/>   
    22.         <property name="acquireIncrement" value="${acquireIncrement}"/>     
    23.         <property name="idleConnectionTestPeriod" value="${idleConnectionTestPeriod}"/>  
    24.     </bean>  
    25.       
    26.       
    27. </beans>  

     第三步:建立我們的JavaBean
     
    [java] view plaincopy
    1. public class Account implements Serializable {  
    2.     private static final long serialVersionUID = -6876471972027832806L;  
    3.     private int userid;  
    4.     private String username;  
    5.     private String password;  
    6.     private String groupname;  
    7. ...  

    第四步:建立SqlMap
    建立檔案Account.xml放入類路徑下的ibatis資料夾中
    [html] view plaincopy
    1. <?xml version="1.0" encoding="UTF-8" ?>  
    2.   
    3.   
    4. <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"  
    5.     "http://ibatis.apache.org/dtd/sql-map-2.dtd">  
    6.   
    7.   
    8. <sqlMap namespace="Account">  
    9.     <typeAlias alias="account" type="com.partner4java.demo.entity.Account" />  
    10.   
    11.   
    12.     <select id="queryAccount" resultClass="account" parameterClass="string">  
    13.         select * from user_account  
    14.         <dynamic>  
    15.             <isNotEmpty>  
    16.                 where groupname = #value#  
    17.             </isNotEmpty>  
    18.         </dynamic>  
    19.         order by userid desc  
    20.     </select>  
    21.   
    22.   
    23.     <insert id="insertAccount" parameterClass="account">  
    24.         insert into  
    25.         user_account(username,password,groupname)  
    26.         values(#username#,#password#,#groupname#)  
    27.     </insert>  
    28. </sqlMap>  

    第五步:建立我們iBATIS的sql-map-config.xml
    建立檔案sql-map-config.xml,放入類路徑的ibatis檔案中
    [html] view plaincopy
    1. <?xml version="1.0" encoding="UTF-8" ?>  
    2.   
    3.   
    4. <!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"  
    5.     "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">  
    6.   
    7.   
    8. <sqlMapConfig>  
    9.   
    10.   
    11.     <settings   
    12.         useStatementNamespaces="true"   
    13.         />  
    14.       
    15.     <sqlMap resource="ibatis/Account.xml" />  
    16.       
    17. </sqlMapConfig>  

    第六步:繼續配置Spring配置檔案 -- 從Spring中建立SqlMapClient
    建立檔案beans.xml放入類路徑下的META-INF/spring中
    [html] view plaincopy
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    4.     xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"  
    5.     xmlns:util="http://www.springframework.org/schema/util"  
    6.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
    7.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    8.         http://www.springframework.org/schema/context  
    9.         http://www.springframework.org/schema/context/spring-context-3.0.xsd  
    10.         http://www.springframework.org/schema/aop  
    11.         http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
    12.         http://www.springframework.org/schema/util  
    13.         http://www.springframework.org/schema/util/spring-util-3.0.xsd">  
    14.     <aop:aspectj-autoproxy />  
    15.     <context:component-scan base-package="com.partner4java" />  
    16.       
    17.     <import resource="classpath:META-INF/spring/datasource-c3p0.xml" />  
    18.       
    19.     <!-- SqlMapClientFactoryBean是個Spring工廠Bean,用於生成SqlMapClient。  
    20.         iBATIS API的核心是SqlMapClient介面。  
    21.         SqlMapClient大致相當於Hibernate的Session或者JPA的EntityManager,用於執行全部的資料訪問操作。 -->  
    22.     <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">  
    23.         <property name="dataSource" ref="dataSource"/>  
    24.         <property name="configLocation" value="classpath:ibatis/sql-map-config.xml"/>  
    25.     </bean>  
    26.       
    27.     <!-- SqlMapClientTemplate包裹了一個SqlMapClient來透明的開啟和關閉會話,好捕獲丟擲的SQLException。 -->  
    28.     <bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">  
    29.         <property name="sqlMapClient" ref="sqlMapClient"/>  
    30.     </bean>  
    31.           
    32. </beans>          

    第七步:建立我們的Service
    [java] view plaincopy
    1. public interface AccountService {  
    2.     List<Account> query(String groupname);  
    3.     void add(Account account);  
    4. }  
    5.   
    6.   
    7. @Service  
    8. public class AccountServiceBean implements AccountService {  
    9.     @Autowired  
    10.     private SqlMapClientTemplate sqlMapClientTemplate;  
    11.     public void setSqlMapClientTemplate(  
    12.             SqlMapClientTemplate sqlMapClientTemplate) {  
    13.         this.sqlMapClientTemplate = sqlMapClientTemplate;  
    14.     }  
    15.   
    16.   
    17.     public List<Account> query(String groupname) {  
    18.         return sqlMapClientTemplate.queryForList("Account.queryAccount",  
    19.                 groupname);  
    20.     }  
    21.   
    22.   
    23.     public void add(Account account) {  
    24.         sqlMapClientTemplate.insert("Account.insertAccount", account);  
    25.     }  
    26.   
    27.   
    28. }  

    最後一步:測試
    [java] view plaincopy
    1. public class AccountServiceBeanTest {  
    2.     ApplicationContext applicationContext;  
    3.     private AccountService accountService;  
    4.     @Before  
    5.     public void setUp() throws Exception {  
    6.         applicationContext = new ClassPathXmlApplicationContext(  
    7.                 "META-INF/spring/beans.xml");  
    8.         accountService = applicationContext.getBean("accountServiceBean", AccountService.class);  
    9.     }  
    10.   
    11.   
    12.     @Test  
    13.     public void testQuery() {  
    14.         System.out.println(accountService.query(null));  
    15.     }  
    16.   
    17.   
    18. }  

相關文章