ibatis 使用文檔 (上篇)

(代碼下載地址: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的能力。

select 
	name,
	age
from user
where address=#address#


三:封裝化的SQL
計算機編程領域一個最古老的概念就是模塊化。
iBATIS使用XML來封裝SQL。
<select id="categoryById" 
	parameterClass="string" resultClass="category">
	select categoryid,name,description
	from category
	where categoryid=#categoryId#
</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語句的輸入和輸出。

示例:
<select id="getAddress" parameterClass="int" resultClass="Address">
	select 
		ADR_ID				as 	id,
		ADR_DESCRIPTION 	as	description
	from address
	where ADR_ID = #id#
</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項目:
<dependencies>
	<dependency>
		<groupId>com.ibatis</groupId>
		<artifactId>ibatis2-common</artifactId>
		<version>2.1.7.597</version>
	</dependency>


	<dependency>
		<groupId>com.ibatis</groupId>
		<artifactId>ibatis2-dao</artifactId>
		<version>2.1.7.597</version>
	</dependency>


	<dependency>
		<groupId>com.ibatis</groupId>
		<artifactId>ibatis2-sqlmap</artifactId>
		<version>2.1.7.597</version>
	</dependency>


	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.21</version>
	</dependency>
</dependencies>

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

(你先考進去或對比敲進去,然後改下數據庫配置 -- 後面會詳細介紹各配置的作用)
<?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">
		<!-- SIMPLE是一個iBATIS內置事務管理器的名字 -->
		<dataSource type="SIMPLE">
			<property value="com.mysql.jdbc.Driver" name="JDBC.Driver" />
			<property
				value="jdbc:mysql://localhost:3306/ibatis_test?autoReconnect=true"
				name="JDBC.ConnectionURL" />
			<property value="root" name="JDBC.Username" />
			<property value="123456" name="JDBC.Password" />
		</dataSource>
	</transactionManager>
</sqlMapConfig>

第三步:創建表
我這裏用的mysql
CREATE TABLE `user_account` (
  `userid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `password` varchar(20) DEFAULT NULL,
  `groupname` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `user_account` (`userid`, `username`, `password`, `groupname`) VALUES 
  (1, 'LMEADORS', 'PICKLE', 'EMPLOYEE'),
  (2, 'JDOE', 'TEST', 'EMPLOYEE');

第四步:編寫代碼 -- 數據庫對應的POJO
像Hibernate一樣寫個數據庫對應的POJO
public class Account implements Serializable {
	private int userid;
	private String username;
	private String password;
	private String groupname;
...

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

本實體具體創建Account.xml(我們現在也是把此文件放在了類路徑)
繼續考入或參考敲入到你的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 namespace="Account">
	<!-- 給我們的返回類型定義了一個短名稱 -- account  -->
	<typeAlias alias="account" type="com.partner4java.demo.entity.Account" />
	
	<!-- 我們寫了一個select,查詢語句就是被<select>名稱對應的標籤所包圍;parameterClass爲我們的傳入參數值類型(string爲系統默認定義的String短名稱);resultClass返回類型;然後就是id,沒什麼可說的,具體SQL總要有標識id。 -->
	<select id="getAllUsers" parameterClass="string" resultClass="account">
		select * from user_account where groupname=#groupname#
	</select>
</sqlMap>

然後在sql-map-config.xml中加入
<sqlMapConfig>
	...
	<sqlMap resource="Account.xml"/>
</sqlMapConfig>


第六步:編寫代碼 -- iBATIS的具體調用
public static void main(String[] args) throws Exception {
	//先創建一個Reader,爲讀取的配置的封裝
	Reader reader = Resources.getResourceAsReader("sql-map-config.xml");


	//iBATIS API的核心是SqlMapClient接口。
	//SqlMapClient大致相當於Hibernate的Session或者JPA的EntityManager,用於執行全部的數據訪問操作。
	SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);


	//queryForList第一個參數爲我們定義的<select>的id,第二個爲傳入的查詢參數#groupname#
	List list = sqlMap.queryForList("getAllUsers", "EMPLOYEE");


	//對獲取的結果遍歷(我對Account的toString進行了重寫)
	for (int i = 0; i < list.size(); i++) {
		System.out.println(list.get(i));
	}
}

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

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

SQL Map配置文件:
SQL Map配置文件(就是前前面的SqlMapConfig.xml)是iBATIS配置的核心(因此也成爲主配置文件)。從數據庫連接到實際所用的SqlMap文件都是通過此文件中的配置提供給框架的。
我們來看一個SqlMapConfig.xml文件(比我們前面hello裏面更加完整)
<?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>


  <properties resource="properties/database.properties"/>
  
  <settings 
    useStatementNamespaces="false" 
    cacheModelsEnabled="true" 
    enhancementEnabled="true" 
    lazyLoadingEnabled="true" 
    maxRequests="32" 
    maxSessions="10" 
    maxTransactions="5"
    />
    
  <transactionManager type="JDBC">
    <dataSource type="SIMPLE">
      <property value="${driver}" name="JDBC.Driver"/>
      <property value="${url}" name="JDBC.ConnectionURL"/>
      <property value="${username}" name="JDBC.Username"/>
      <property value="${password}" name="JDBC.Password"/>
    </dataSource>
  </transactionManager>


  <sqlMap resource="org/apache/ibatis/jgamestore/persistence/sqlmapdao/sql/Account.xml"/>


</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
<?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 namespace="Account">


	<typeAlias alias="account" type="com.partner4java.demo.entity.Account" />
	
	<select id="getAllUsers" parameterClass="string" resultClass="account">
		select * from user_account where groupname=#groupname#
	</select>
</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)就是一種在已映射語句中添加查詢條件的簡單方式,你可以使用兩個不同的方法來設置內聯參數。
第一個方法是使用散列(#)符號。
<sqlMap namespace="Account">


	<typeAlias alias="account" type="com.partner4java.demo.entity.Account" />
	
	<select id="getAllUsers" parameterClass="string" resultClass="account">
		select * from user_account where groupname=#groupname#
	</select>
</sqlMap>

public static void main(String[] args) throws Exception {
	Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
	SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
	List list = sqlMap.queryForList("getAllUsers", "EMPLOYEE");
	for (int i = 0; i < list.size(); i++) {
		System.out.println(list.get(i));
	}


}

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


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

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:
<select id="getAllAcoountIds" resultClass="int">
	select userid as value from user_account
</select>


<select id="getAllAcoountIds" resultClass="int">
	select userid from user_account
</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屬性
通常,該屬性用來提供自定義的類型處理器

按照我們前面的用法:
<insert id="insertAccount" parameterClass="account">
	insert into user_account(username,password,groupname ) values(#username#,#password#,#groupname#)
</insert>

Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
sqlMap.insert("insertAccount", new Account("hello", "123", "EMPLOYEE"));

這是藉助了默認的反射機制,我們還可以自己指定各種參數類型:
<parameterMap class="account" id="accountMap">
	<parameter property="username" nullValue="hello" jdbcType="VARCHAR"/>
	<parameter property="password" nullValue="123" jdbcType="VARCHAR"/>
	<parameter property="groupname" nullValue="123" jdbcType="EMPLOYEE"/>
</parameterMap>


<insert id="insertAccountMap" parameterMap="accountMap">
	insert into user_account(username,password,groupname ) values(?,?,?)
</insert>

Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
sqlMap.insert("insertAccountMap", new Account(null, "789", null));


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

Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
List list = sqlMap.queryForList("getUsersByGroupName3", "%EMPLOYEE%");
for (int i = 0; i < list.size(); i++) {
	System.out.println(list.get(i));
}

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:
<resultMap class="account" id="accountRes">
	<result property="username2" column="username" nullValue="hello" jdbcType="VARCHAR"/>
	<result property="password" column="password" nullValue="123" jdbcType="VARCHAR"/>
	<result property="groupname" column="groupname" nullValue="123" jdbcType="EMPLOYEE"/>
</resultMap>
<select id="getUsersByGroupName4" parameterClass="string" resultMap="accountRes">
	select * from user_account where groupname like #groupname#
</select>
Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
List list = sqlMap.queryForList("getUsersByGroupName4", "%EMPLOYEE%");
for (int i = 0; i < list.size(); i++) {
	System.out.println(list.get(i));
}


發佈了183 篇原創文章 · 獲贊 20 · 訪問量 80萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章