ibatis 使用文檔 (下篇)

====================================================================================
第5章 使用高級查詢技術

一:用已映射語句關聯對象


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

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

使用起來非常簡單,大體通過以下步驟:
第一步:在我們的JavaBean中建立對應關係
public class Order implements Serializable {
	private static final long serialVersionUID = -7307764485708148107L;


	private Integer orderId;
	private String orderAddress;
...


public class Account implements Serializable {
	private static final long serialVersionUID = 3337778507086494766L;


	private Integer accountId;
	private String username;
	//建立了一個包含關聯關係
	private List<Order> orders = new LinkedList<Order>();
...



第二步:創建表(我用的MySQL)
CREATE TABLE `orders` (
  `order_id` int(11) NOT NULL AUTO_INCREMENT,
  `order_address` varchar(20) DEFAULT NULL,
  `account_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


CREATE TABLE `account` (
  `account_id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`account_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


INSERT INTO `account` (`account_id`, `username`) VALUES 
  (1, 'hello'),
  (2, 'world');


INSERT INTO `orders` (`order_id`, `order_address`, `account_id`) VALUES 
  (1, 'hello1', 1),
  (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" />)

<sqlMap namespace="Account">
	<typeAlias alias="account" type="com.partner4java.demo1.bean.Account" />
	<typeAlias alias="order" type="com.partner4java.demo1.bean.Order" />


	<!-- 簡單關聯方案 -->
	<resultMap class="account" id="accountMap">
		<result property="accountId" column="account_id" />
		<result property="username" column="username" />
		<!-- 通過select來獲取的值放入orders;select需要一個關聯參數,爲本處column -->
		<result property="orders" column="account_id" select="getOrderList" />
	</resultMap>


	<resultMap class="order" id="orderMap">
		<result property="orderId" column="order_id" />
		<result property="orderAddress" column="order_address" />
	</resultMap>


	<select id="getAccount" resultMap="accountMap">
		select * from account
	</select>


	<select id="getOrderList" resultMap="orderMap">
		select * from orders where
		account_id = #value#
	</select>
</sqlMap>


第四步:執行代碼
public static void main(String[] args) throws Exception {
	Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
	SqlMapClient sqlMapClient = SqlMapClientBuilder
			.buildSqlMapClient(reader);
	List<Account> accounts = sqlMapClient.queryForList("getAccount", null);
	for(Account account:accounts){
		System.out.println(account);
	}
}
打印:
Account [accountId=1, username=hello, orders=[Order [orderId=1, orderAddress=hello1], Order [orderId=2, orderAddress=hello2]]]
Account [accountId=2, username=world, orders=[]]

延遲加載:
要使用延遲加載,需要編輯SqlMapConfig.xml文件,通過在<setting>元素中將lazyLoadingEnabled屬性改爲true來啓動它。
如果想要使用延遲加載的cglib增強版,則必須下載並將其添加到應用程序的類路徑中,同時也必須將<setting>元素中的enhancementEnabled屬性改爲true。
必須注意的是,這是一個全局設置,因此如果啓用了這些特性,SQL映射中所有的已映射語句都將會使用延遲加載。

問題:你是否會揣摩到上面的查詢方式會出現“N+1”問題呢,那麼如何解決“N+1”問題?
避免N+1問題:
<!-- N + 1查詢方案,注意每個resultMap都添加了一個groupBy -->
<resultMap class="order" id="orderJMap" groupBy="orders.order_id">
	<result property="orderId" column="order_id" />
	<result property="orderAddress" column="order_address" />
</resultMap>


<resultMap class="account" id="accountJMap" groupBy="account.account_id">
	<result property="accountId" column="account_id" />
	<result property="username" column="username" />
	<!-- 這裏的resultMap爲我們上面對order的定義 -->
	<result property="orders" resultMap="Account.orderJMap" />
</resultMap>


<select id="getAccountJ" resultMap="accountJMap">
	select
	account.account_id,account.username,orders.order_id,orders.order_address
	from account join orders on account.account_id = orders.account_id
	order
	by account.account_id,orders.order_id
</select>

public static void main(String[] args) throws Exception {
	Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
	SqlMapClient sqlMapClient = SqlMapClientBuilder
			.buildSqlMapClient(reader);
	List<Account> accounts = sqlMapClient.queryForList("getAccountJ", null);
	for(Account account:accounts){
		System.out.println(account);
	}
}

但是這種方式會丟失不存在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支持所有的自動事務。
自動事務允許使用單個的方法調用來運行單個更新語句活查詢語句,而完全不用關心如何劃分事務。


三:局部事務
所謂局部事務,它僅僅涉及一個應用程序、一種資源(例如關係數據庫),並且一次只能處理一個事務。

首先配置:
<transactionManager type="JDBC">
	<!-- SIMPLE是一個iBATIS內置事務管理器的名字 -->
	<dataSource type="SIMPLE">
...

public static void main(String[] args) throws Exception {
	Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
	SqlMapClient sqlMapClient = SqlMapClientBuilder
			.buildSqlMapClient(reader);
	
	//學過jdbc的,是不是很容易理解啊?
	sqlMapClient.startTransaction();
	
	long being = System.currentTimeMillis();
	try {
		sqlMapClient.startBatch();
		for(int i=0;i<1000;i++){
			Account account = new Account("en5" + i, "o5", "EMPLOYEE");
			sqlMapClient.insert("insertAccount", account);
		}
		sqlMapClient.executeBatch();
		sqlMapClient.commitTransaction();
	} catch (Exception e) {
		e.printStackTrace();
	}finally{
		sqlMapClient.endTransaction();
	}
	System.out.println("time:" + (System.currentTimeMillis() - being));
	
}

定製事務:
我們也可以獲取一個conn,然後使用conn手工管理事務,不過我們建議在基於conn管理事務的基礎上使用sqlMapClient來執行SQL操作。
public static void main(String[] args) throws Exception {
	Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
	SqlMapClient sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);
	Connection conn = null;
	SqlMapSession session = null;
	try {
		conn = sqlMapClient.getDataSource().getConnection();
		conn.setAutoCommit(false);
		session = sqlMapClient.openSession(conn);
		session.insert("insertAccount", new Account("hello", "123", "EMPLOYEE"));
		
		conn.commit();
	} catch (Exception e) {
		if(conn != null) conn.rollback();
		e.printStackTrace();
	}finally{
		if(session != null){
			session.close();
		}
		if(conn != null){
			conn.close();
		}
	}
}

(局部事務就不多說了,你自己打一遍就明白了)

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

一:先來個hello

<select id="queryAccount" resultClass="account" parameterClass="string">
	select * from user_account 
	<!-- 就多了一個這種標籤,看這標籤的起名是不是很像我們Junit裏的斷言啊? -->
	<isNotNull>
		where groupname = #value#
	</isNotNull>
</select>	

public static void main(String[] args) throws Exception {
	Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
	SqlMapClient sqlMapClient = SqlMapClientBuilder
			.buildSqlMapClient(reader);
	List<Account> accounts = sqlMapClient
			.queryForList("queryAccount", "EMPLOYEE");
	for (Account account : accounts) {
		System.out.println(account);
	}
}


也可以:使用<dynamic>標籤作爲父標籤
<dynamic>標籤不像其他動態SQL標籤,它不會去評測(evaluate)任何值或狀態。它通常只是使用prepend屬性,該屬性值將作爲前綴被加到<dynamic>標籤的內容體之前。
如果<dynamic>標籤的內容體經iBATIS處理後沒有產生任何文本,那麼prepend值將被忽略。
<select id="queryAccount" resultClass="account" parameterClass="string">
	select * from user_account 
	<dynamic>
		<isNotNull>
			where groupname = #value#
		</isNotNull>
	</dynamic>
</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的強健且簡單的高速緩存機制是完全基於配置的,因此避免了直接使用高速緩存的負擔。
<!-- type="MEMORY",指定其高速緩存類型爲MEMORY,把查詢結果直接存儲在內存中。 -->
<cacheModel type="MEMORY" id="categoryCache">
	<!-- flushOnExecute標籤用於指定當某個特定高速緩存被訪問時,其存儲結果將被清除。 -->
	<flushOnExecute statement="insert" />
	<flushOnExecute statement="update" />
	<flushOnExecute statement="delete" />
	<!-- 每一種類型的高速緩存模型都有一些專用於它自己配置的特性,property標籤就是用於完成這些專用特性的設置的。那麼屬性指定該高速緩存模型將要設置的特性的名稱,value指定該預定義特性的值。 -->
	<property name="reference-type" value="WEAK" />
</cacheModel>


<select id="queryAccount" resultClass="account" parameterClass="string"
	cacheModel="categoryCache">
	select * from user_account
	<dynamic>
		<isNotEmpty>
			where groupname = #value#
		</isNotEmpty>
	</dynamic>
</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
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.partner4java.spring</groupId>
	<artifactId>5spring</artifactId>
	<version>0.0.1-SNAPSHOT</version>


	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.version>3.1.1.RELEASE</spring.version>
		<ibatis.version>2.1.7.597</ibatis.version>
	</properties>


	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>


		<dependency>
			<groupId>org.apache.ibatis</groupId>
			<artifactId>ibatis-sqlmap</artifactId>
			<version>2.3.4.726</version>
		</dependency>
		<dependency>
			<groupId>org.apache.ibatis</groupId>
			<artifactId>ibatis-core</artifactId>
			<version>3.0</version>
		</dependency>


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


		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2.2</version>
		</dependency>


		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.6.9</version>
		</dependency>


		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
			<scope>test</scope>
		</dependency>
	</dependencies>


</project>

第一步:創建數據庫相關配置文件

在類路徑下創建:jdbc-c3p0.properties
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/ibatis_test?useUnicode=true&characterEncoding=UTF-8
user=root
password=123456


#初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。Default: 3
initialPoolSize=1


#連接池中保留的最小連接數
minPoolSize=1


#連接池中保留的最大連接數。Default: 15
maxPoolSize=300


#最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0
maxIdleTime=60


#當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3
acquireIncrement=5


#每60秒檢查所有連接池中的空閒連接。Default: 0
idleConnectionTestPeriod=120


#定義在從數據庫獲取新連接失敗後重復嘗試的次數。Default: 30
#acquireRetryAttempts=30


#兩次連接中間隔時間,單位毫秒。Default: 1000
#acquireRetryDelay=1000


#連接關閉時默認將所有未提交的操作回滾。Default: false
#autoCommitOnClose=false


#JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但由於預緩存的statements 
#  屬於單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。 
#  如果maxStatements與maxStatementsPerConnection均爲0,則緩存被關閉。Default: 0
#maxStatements=100


#maxStatementsPerConnection定義了連接池內單個連接所擁有的最大緩存statements數。Default: 0 
#maxStatementsPerConnection=


#c3p0是異步操作的,緩慢的JDBC操作通過幫助進程完成。擴展這些操作可以有效的提升性能 
# 通過多線程實現多個操作同時被執行。Default: 3
#numHelperThreads=3


#當用戶調用getConnection()時使root用戶成爲去獲取連接的用戶。主要用於連接池連接非c3p0 
#  的數據源時。Default: null
#overrideDefaultUser=root


#與overrideDefaultUser參數對應使用的一個參數。Default: null
#overrideDefaultPassword=


#定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個一顯著提高測試速度。注意: 
#  測試的表必須在初始數據源的時候就存在。Default: null
#preferredTestQuery=select id from test where id=1


#用戶修改系統配置參數執行前最多等待300秒。Default: 300
#propertyCycle=300


#因性能消耗大請只在需要的時候使用它。如果設爲true那麼在每個connection提交的 
#  時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable 
#  等方法來提升連接測試的性能。Default: false
#testConnectionOnCheckout=false


#如果設爲true那麼在取得連接的同時將校驗連接的有效性。Default: false
#testConnectionOnCheckin=true


#c3p0將建一張名爲Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個參數那麼 
#屬性preferredTestQuery將被忽略。你不能在這張Test表上進行任何操作,它將只供c3p0測試 
#使用。Default: null
#automaticTestTable=Test


#獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效 
#  保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設爲true,那麼在嘗試 
#  獲取連接失敗後該數據源將申明已斷開並永久關閉。Default: false
#breakAfterAcquireFailure=false


#當連接池用完時客戶端調用getConnection()後等待獲取新連接的時間,超時後將拋出 
#  SQLException,如設爲0則無限期等待。單位毫秒。Default: 0
#checkoutTimeout=5000


#通過實現ConnectionTester或QueryConnectionTester的類來測試連接。類名需制定全路徑。 
#  Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester
#connectionTesterClassName=


#指定c3p0 libraries的路徑,如果(通常都是這樣)在本地即可獲得那麼無需設置,默認null即可 
#  Default: null
#factoryClassLocation=


#maxConnectionAge=400

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

創建文件datasource-c3p0.xml,放入類路徑下的META-INF/spring中
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	
	<context:property-placeholder location="classpath:jdbc-c3p0.properties" />
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${driverClass}"/>
		<property name="jdbcUrl" value="${jdbcUrl}"/>
		<property name="user" value="${user}"/>
		<property name="password" value="${password}"/>
		<property name="initialPoolSize" value="${initialPoolSize}"/>
		<property name="minPoolSize" value="${minPoolSize}"/>	
		<property name="maxPoolSize" value="${maxPoolSize}"/>
		<property name="maxIdleTime" value="${maxIdleTime}"/>	
		<property name="acquireIncrement" value="${acquireIncrement}"/>	
		<property name="idleConnectionTestPeriod" value="${idleConnectionTestPeriod}"/>
	</bean>
	
	
</beans>

 第三步:創建我們的JavaBean
 
public class Account implements Serializable {
	private static final long serialVersionUID = -6876471972027832806L;
	private int userid;
	private String username;
	private String password;
	private String groupname;
...

第四步:創建SqlMap
創建文件Account.xml放入類路徑下的ibatis文件夾中
<?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="queryAccount" resultClass="account" parameterClass="string">
		select * from user_account
		<dynamic>
			<isNotEmpty>
				where groupname = #value#
			</isNotEmpty>
		</dynamic>
		order by userid desc
	</select>


	<insert id="insertAccount" parameterClass="account">
		insert into
		user_account(username,password,groupname)
		values(#username#,#password#,#groupname#)
	</insert>
</sqlMap>

第五步:創建我們iBATIS的sql-map-config.xml
創建文件sql-map-config.xml,放入類路徑的ibatis文件中
<?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>


    <settings 
	    useStatementNamespaces="true" 
	    />
	
	<sqlMap resource="ibatis/Account.xml" />
	
</sqlMapConfig>

第六步:繼續配置Spring配置文件 -- 從Spring中創建SqlMapClient
創建文件beans.xml放入類路徑下的META-INF/spring中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.0.xsd">
	<aop:aspectj-autoproxy />
	<context:component-scan base-package="com.partner4java" />
	
	<import resource="classpath:META-INF/spring/datasource-c3p0.xml" />
	
	<!-- SqlMapClientFactoryBean是個Spring工廠Bean,用於生成SqlMapClient。
		iBATIS API的核心是SqlMapClient接口。
		SqlMapClient大致相當於Hibernate的Session或者JPA的EntityManager,用於執行全部的數據訪問操作。 -->
	<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="configLocation" value="classpath:ibatis/sql-map-config.xml"/>
	</bean>
	
	<!-- SqlMapClientTemplate包裹了一個SqlMapClient來透明的打開和關閉會話,好捕獲拋出的SQLException。 -->
	<bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
		<property name="sqlMapClient" ref="sqlMapClient"/>
	</bean>
		
</beans>        

第七步:創建我們的Service
public interface AccountService {
	List<Account> query(String groupname);
	void add(Account account);
}


@Service
public class AccountServiceBean implements AccountService {
	@Autowired
	private SqlMapClientTemplate sqlMapClientTemplate;
	public void setSqlMapClientTemplate(
			SqlMapClientTemplate sqlMapClientTemplate) {
		this.sqlMapClientTemplate = sqlMapClientTemplate;
	}


	public List<Account> query(String groupname) {
		return sqlMapClientTemplate.queryForList("Account.queryAccount",
				groupname);
	}


	public void add(Account account) {
		sqlMapClientTemplate.insert("Account.insertAccount", account);
	}


}

最後一步:測試
public class AccountServiceBeanTest {
	ApplicationContext applicationContext;
	private AccountService accountService;
	@Before
	public void setUp() throws Exception {
		applicationContext = new ClassPathXmlApplicationContext(
				"META-INF/spring/beans.xml");
		accountService = applicationContext.getBean("accountServiceBean", AccountService.class);
	}


	@Test
	public void testQuery() {
		System.out.println(accountService.query(null));
	}


}


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