iBATIS In Action:执行非查询语句(二)
本章內容包括
- iBATIS API的更多內容
- 插入數據
- 更新和刪除數據
- 使用存儲過程
5.3 更新和刪除數據
至此,我們已經學習了如何向數據庫插入數據以及獲取相應記錄的鍵值,再來看看如何更新和刪除數據。
Insert方法返回的是object類型的值,而Update和Delete方法則返回int類型的值,該值指示了更新或刪除語句所影響的記錄數。
iBATIS框架允許使用單條語句操作一條或多條記錄。這是它與大多數ORM工具不同的地方之一,后者一般只能修改單條記錄。
5.3.1 并發更新處理
iBATIS目前尚未實現的一個功能是鎖定記錄以管理對相同數據的并發修改。有幾種技術可用來處理并發更新,如在數據行上使用時間戳或者版本號。比如,有如下一個account表,定義為:CREATE?TABLE?account?(
accountid?serial?NOT?NULL,
username?varchar(10),
passwd?varchar(10),
firstname?varchar(30),
lastname?varchar(30),
address1?varchar(30),
address2?varchar(30),
city?varchar(30),
state?varchar(5),
postalcode?varchar(10),
country?varchar(5),
version?int8,
CONSTRAINT?account_pkey?PRIMARY?KEY?(accountid)
)
我們可以在更新數據的時候增加version列,在update語句的where子句使用accountId和version兩個字段。在執行更新操作時,如果要更新的記錄未被其它用戶修改,那么更新會得以成功地執行,因為版本號未被改變,映射語句也可返回期望的記錄數。如果返回的是0,而且未拋出任何異常,那么您就該知道,其他人已經修改了記錄。下一步如何處理就由您來決定了。
5.3.2 更新和刪除子項記錄
在一個對象模型系統中,不難找到一些包含子對象的組件(component)。例如,一個Order對象可能會包含一個OrderItem類型的列表(list)或數組(array),這些子對象表示訂單的細項。
因為iBATIS主要是一個SQL映射工具,它不能在更新數據庫時去管理這種關系。這樣,這種功能就必須由您程序的數據訪問層而不是iBATIS來處理了。而其實現代碼相當簡單:
????public?void?SaveOrder(Order?order)????{
????????if?(null?==?order.OrderId)?
????????{
????????????sqlMapper.Insert("Order.insert",?order);
????????}?
????????else?
????????{
????????????sqlMapper.Update("Order.update",?order);
????????}
????????sqlMapper.Delete("Order.deleteDetails",?order);
????????for(int?i?=?0;?i?<?order.OrderItems.Count;?i++)?
????????{
????????????OrderItem?oi?=?order.OrderItems[i];
????????????sqlMapper.Insert("OrderItem.insert",?oi);
????????}
????}
<!--[if mso & !supportInlineShapes & supportFields]--> 這段代碼能夠實現功能,但它沒有提供任何事務機制,因此如果最后一個OrderItem失敗,那么所有的其它數據則處于不一致的狀態,因為事務只能處理每一次插入和更新操作。此外,性能也是個問題,因為每條的執行都會立即提交。在下節中,您將了解如何使用批量更新技術來解決上述的兩個問題(即事務和性能)。
5.4 執行批量更新
譯注:在iBATIS.NET中并未提供與此等價的功能,不過園子里的Zealic提供了一種方法,可以借鑒一下。
5.5 使用存儲過程
存儲過程是在數據庫服務器進程中執行的代碼塊。大部分存儲過程都是以特定于數據庫的語言(而這些語言一般基于SQL),一些數據庫生產商還允許使用其它語言開發(例如,Oracle中可用Java,SQL Server 2005中可用C#,而PostgreSQL允許幾乎任何語言)。
5.5.1 三思而行
存儲過程往往被Java開發人員視為“敵人”,因為它們是平臺相關的(數據庫平臺相關的, 不一定是操作系統相關的),而這是一些Java開發人員所不接受的。
相比于特定的解決方案,我們更注重于解決問題,我們發現存儲過程在優化過程中值得考慮,它還可以解決那些復雜的以數據為中心的問題。
不要作極端主義者
在對存儲過程的討論中存在兩個極端的觀點。一方面,一些純Java主義者認為永遠不要使用存儲過程(更有甚者,有人認為SQL本身也不能使用)。另一方面,一些純數據庫主義者認為與數據庫間的任何交互都要通過存儲過程來進行。
實際上,那則古老的諺語:“純粹主義者都是錯誤的。”在這里也是適用的,兩個極端的觀點都是錯誤的。存儲過程是一個工具,僅僅如此。考慮一個類似的情景:一位木匠,他有一把錘子,一個卷尺和一張鋸。雖然他能夠使用錘子來測量木板,但使用卷尺顯然更好(使用鋸來釘釘子的情況也是如此)。每一項工作都有最合適的工具,每個工具也都是為某種工作設計的。如果選擇了錯誤的工具,它就不會發揮太好的作用。
選擇正確的工具
我們可以選擇的工具有SQL,存儲過程和應用程序代碼。一些操作用SQL實現起來不錯,另一些可以選擇存儲過程,其它的則可以使用應用程序代碼。
例如,考慮這種情況,我們要從幾張表中查詢以生成報表。使用應用程序代碼,先從幾張表中抓取所有數據,然后再進行過濾、連接,沒什么意義。為一個簡單的查詢創建存儲過程也會增加復雜性,卻不會帶來大的好處。將SQL放在iBATIS的映射語句中則是一種快速、簡單、高效的解決方案。
現在,有一個更復雜的報表,需要進行多個子查詢和左連接從百萬級的記錄中收集數據,存儲過程會更好。使用存儲過程,可以有更多選項進行優化。
在應用程序中,如果需要使用動態SQL,映射語句也非常有用。在第8章,我們會看到分別使用C#代碼,存儲過程和映射語句來實現動態SQL。我們不想吊您的胃口,如果您等不及了,直接跳到8.5節看一下吧。
在使用存儲過程時需要考慮的一點是,它們可用來更新并返回數據,如果此時調用存儲過程的方法不需要提交(比如僅用作讀取數據),因而事務并未提交,那么就可能出現問題。在這種情況下,事務管理器需要完成提交,即使是讀操作,或者您手工管理這些事務,就像下面的例子:????try
????{
????????sqlMapper.BeginTransaction();
????????sqlMapper.QueryForObject("Account.insertAndReturn",?a);
????????sqlMapper.CommitTransaction();
????}
????catch(Exception)
????{
????????sqlMapper.RollBackTransaction();
????}
關于事務的更多內容在第7章,因此要獲得更多信息,去那里看看吧。
5.5.2 IN,OUT,INOUT參數
目前為止,我們看到的參數都是輸入參數——將它們傳給iBATIS,傳入的數據保持不變(除了<selectKey>元素)。使用存儲過程時,可以使用三種參數:IN,OUT和INOUT。
在iBATIS中使用存儲過程時,IN參數用法很簡單。將參數傳給存儲過程跟傳給映射語句是一樣的。這里是一個簡單的存儲過程,它接受兩個IN參數并返回一個值:CREATE?OR?REPLACE?FUNCTION?max_in_example
(a?float4,?b?float4)
RETURNS?float4?AS
$BODY$
BEGIN
if?(a?>?b)?then
return?a;
else
return?b;
end?if;
END;
$BODY$
LANGUAGE?'plpgsql'?VOLATILE;
下面是相應的參數映射,映射語句以及C#代碼:
<parameterMap?id="pm_in_example"?class="hashtable">
????<parameter?property="a"?column="a"?/>
????<parameter?property="b"?column="b"?/>
</parameterMap>
<procedure?id="in_example"?parameterMap="pm_in_example"?resultClass="int"?>
????max_in_example
</procedure>
Hashtable?condition?=?new?Hashtable();
condition.Add("a",?7);
condition.Add("b",?5);
return?sqlMapper.QueryForObject<int>("Account.in_example",?condition);
INOUT參數是指那些可以傳給存儲過程,可被存儲過程修改的參數,就像在下面的例子中,存儲過程接受兩個數字,交換它們的值。看存儲過程的代碼(使用MS SQL的T-SQL):
CREATE?PROCEDURE?Swap
????@a?INT?OUTPUT,
????@b?INT?OUTPUT
AS
????DECLARE?@temp?INT
????SET?@temp?=?@a
????SET?@a?=?@b
????SET?@b?=?@temp
下面是相應的參數映射、映射語句和C#代碼:
<parameterMap?id="para_swap_numbers"?class="hashtable">
????<parameter?property="a"?column="a"?direction="InputOutput"?/>
????<parameter?property="b"?column="b"?direction="InputOutput"?/>
</parameterMap>
<procedure?id="swap_numbers"?parameterMap="para_swap_numbers">
????Swap
</procedure>
Hashtable?condition?=?new?Hashtable();
condition.Add("a",?7);
condition.Add("b",?5);
sqlMapper.Insert("swap_numbers",?condition);
OUT參數較為少用。它類似于結果(就像resultMap中的那樣),卻像參數那樣傳入。傳入的值會被忽略,然后在存儲過程中被返回值替換掉。OUT參數可返回任意的單個值(見我們的下個例子)。 下面例子中的存儲過程很簡單,使用兩個IN參數和一個OUT參數(MS SQL的T-SQL):
CREATE?PROCEDURE?Max
????@a?INT,
????@b?INT,
????@c?INT?OUTPUT
AS
????IF?@a?>?@b
????????SET?@c?=?@a
????ELSE
????????SET?@c?=?@b
這個存儲過程接收三個參數并返回void。但是,第三個參數僅用作輸出,因此取決于另外兩個參數的大小關系,它將被替換為較大的一個。要在iBATIS中使用它,需要如下代碼:
<procedure?id="max_number"?parameterMap="para_max_number">
????Max
</procedure>
<parameterMap?id="para_max_number"?class="hashtable">
????<parameter?property="a"?column="a"?direction="Input"?/>
????<parameter?property="b"?column="b"?direction="Input"?/>
????<parameter?property="c"?column="c"?direction="Output"?dbType="int"?/>
</parameterMap>
Hashtable?condition?=?new?Hashtable();
condition.Add("a",?a);
condition.Add("b",?b);
ExecuteInsert("max_number",?condition);
//Convert.ToInt32(condition["c"])?is?the?maximum.
譯注:參數c對應的parameter節點中有一個dbType特性,此處不能省略。如果省略,則會出現“String[2]: the Size property has an invalid size of 0.”的異常,原因時參數c被解析為varchar類型,此時OUT參數需要設定長度,故而引發上述異常。將dbType加上就不會有問題了。這個問題也說明parameter Map要盡量寫得詳細。
存儲過程也可以返回多行記錄。在對大量數據進行復雜的查詢時,如果傳統的SQL優化技術沒有效果,使用存儲過程可能會顯著地改善性能。一些更為困難的情況是外連接(outer join)和需要計算的過濾。如果在這些情景中,您決定要使用存儲過程,就需要確保對系統的瓶頸有良好的理解,否則不僅不能改善性能,還可能會變得更糟。
5.6 小結
在本章中,我們查看了使用iBATIS修改數據庫數據的絕大多數選項。在讀完第4章和本章后,您已經了解了使用iBATIS維護數據的所有信息。
在第6章中,我們將繼續進行擴展。通過了解更為高級的查詢技術,您能夠更好地利用已掌握的數據庫技能和平臺。轉載于:https://www.cnblogs.com/EricGu/archive/2008/02/26/1082246.html
總結
以上是生活随笔為你收集整理的iBATIS In Action:执行非查询语句(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装程序的窗体
- 下一篇: COM本质论学习笔记(一)IDL