?
??
讓我們重回到車輛管理系統和張三的故事中。
?
???
???
在
iBATIS SQL Maps
的世界里也存在
one-to-many
、
many-to-one
的關系,想必你已經對這些概念駕輕就熟了。好!還是每個
People
對應多條
AutoInfo
信息。
?
???
??? 本系列文章第一部分提到過 iBATIS SQL Maps 的映射文件個數可以人為設定,但是,把一組有共性的操作放在一起是首選策略。下面我們看看為張三首次買車所生成的映射文件是怎樣的:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap
<sqlMap namespace="AutoMag">
? <insert id="insertPeople" parameterClass="bo.People">
?
??? PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
??? "
http://www.ibatis.com/dtd/sql-map-2.dtd
">
??? <![CDATA[
????? insert into people (name, address) values (#name#, #address#)
??? ]]>
????? <selectKey resultClass="java.lang.Integer" keyProperty="id">
??? <![CDATA[
????????? select last_insert_id();
??? ]]>
????? </selectKey>
? </insert>
?
? <insert id="insertAutoInfo" parameterClass="bo.AutoInfo">
??? <![CDATA[
????? insert into auto_info (license_plate, owner_no) VALUES (#licensePlate#, #ownerNo.id#)
??? ]]>
? </insert>
</sqlMap>
sqlMap
sqlMap 元素擁有屬性 namespace="…" ,定義了該 XML 文件命名空間。如果你在配置文件 SqlMapConfig.xml 中指定了 settings 元素的屬性 useStatementNamespaces="true" ,那么就可以按照命名空間的方式訪問 Mapped statement ,比如 namespace=" AutoMag " ,相應 Java 代碼: sqlMap.insert("AutoMag.insertPeople",people) 。這樣做是為了防止不同映射文件中出現同名 Mapped statement 而產生沖突。什么是 Mapped statement ?
Mapped statement
iBATIS SQL Maps
的核心概念就是
Mapped statement
!
Mapped Statement
可以使用任意的
SQL
語句,利用
POJO
、
原始變量
及其
Wrapper Class
作為輸入(
parameter class
)和輸出(
result class
)。
Mapped Statement 包含以下幾種類型:
insert
對應數據庫的
insert
操作,該操作返回本次操作插入記錄的主鍵值。
select
對應數據庫的
select
操作,該操作返回特定的
POJO
或
對象。
update
對應數據庫的
update
操作,該操作返回被更新的記錄個數。
delete
對應數據庫的
delete
操作,該操作返回被刪除的記錄個數。
procedure
對應數據庫存儲過程。
statement 類型最為通用,可以代替以上所有的類型。但由于缺乏操作直觀性故不推薦。
insert id="insertPeople" parameterClass="bo.People"
定義了 insert 類型的 Mapped Statement 。屬性 id="insertPeople" 定義操作名稱, parameterClass="bo.People" 定義傳入參數為 People 對象實例,框架可確保其屬性持久化到數據庫相應字段中。由于 SQL 語句會包含 “<>” 這樣的符號,容易和 XML 產生 沖突,放進 <![CDATA[ …… ]]> 區域就可避免。 insert into people (name, address) values (#name#, #address#) , 是一條普通的 SQL 語句, “#name# 、 #address#” 利用 Java 反射機制訪問 People 對象實例的相應屬性。
selectKey resultClass="java.lang.Integer" keyProperty="id"
iBATIS SQL Maps 通過 <insert> 元素的子元素 <selectKey> 來支持主鍵自動生成。 resultClass="java.lang.Integer" 定義返回對象為 int 的 Wrapper Class 。 keyProperty="id" 定義了主鍵名稱。本例是 MySQL 主鍵生成方式,參考官方文檔, MySQL 的主鍵生成無需人為來控制,也就是說可不使用 <selectKey> 而由數據庫自動處理。但我測試發現,在執行 insert 操作以后,程序沒有返回 本次操作插入記錄的主鍵值。在官方論壇上也有很多用戶提出這樣的疑惑,作者的答復是:這和 JDBC Driver 有關系。不可能把驅動一一測試吧?一勞永逸的辦法是使用 <selectKey> 元素。以下是 Oracle 和 SQL Server 主鍵生成方法:
< !- Oracle ->
<!- Microsoft SQL Server ->
?
<insert id="insertProduct-ORACLE" parameterClass="com.domain.Product">
? <selectKey resultClass="int" keyProperty="id" >
??? SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL
? </selectKey>
? insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#)
</insert>
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
? insert into PRODUCT (PRD_DESCRIPTION) values (#description#)
? <selectKey resultClass="int" keyProperty="id" >
? SELECT @@IDENTITY AS ID
? </selectKey>
</insert>
insert into auto_info (license_plate, owner_no) VALUES (#licensePlate#, #ownerNo.id#)
在插入了
people
記錄后,要為
auto_info
插入記錄。基本原則和之前遇到過的一樣,只是
”
owner_no”
這個字段值由
AutoInfo
對象屬性
”
ownerNo”
獲得,該屬性類型為
People
。這是由于我沿用了
Hibernate
產生的
POJO
,如果你愿意,完全可以把
”
ownerNo”
替換為
Integer
類型。
??? com.ibatis.common.resources.Resources
對象負責從
XML
得到
java.io.Reader
抽象類的實例,供工廠方法調用。
com.ibatis.sqlmap.client.SqlMapClientBuilder
構造
SqlMapClient
實例。
??? com.ibatis.sqlmap.client.SqlMapClient
是
iBATIS SQL Maps
核心組件,可以說我們的編程工作都是圍繞著它展開。
package test;
import java.io.Reader;
import com.ibatis.sqlmap.client.*;
import bo.*;
public class AutoMag {
?private Reader reader;
??????????? AutoInfo autoInfo=new AutoInfo();
?
import com.ibatis.common.resources.*;
?private SqlMapClient sqlMap;
?private String resource = "SqlMapConfig.xml";
?
?public void insertPeople() throws Exception{
??try{
???reader = Resources.getResourceAsReader(resource);
????? sqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
????? sqlMap.startTransaction();
?????
????? People people=new People();
????? people.setName("張三");
????? people.setAddress("中國");
??????????? sqlMap.insert("insertPeople",people);
??????????? autoInfo.setLicensePlate("A00001");
??????????? autoInfo.setOwnerNo(people);???????????
????? sqlMap.insert("insertAutoInfo",autoInfo);
?????
????? sqlMap.commitTransaction();
??}finally{
???sqlMap.endTransaction();
??}
?}
}
程序和
Hibernate
寫法差不多,我感覺甚至比
Hibernate
更簡單。我可以顯示的進行
insert
操作,符合傳統
JDBC
編程習慣。
iBATIS SQL Maps
支持自動事務處理,可以不用寫明
startTransaction
、
commitTransaction
。但如果
People insert
操作成功,而
AutoInfo insert
操作失敗,就破壞了兩次
insert
操作的原子性。最后
endTransaction
包含異常情況下的回滾事務和關閉連接池連接兩種功能。
請注意!引用、轉貼本文應注明原作者:Rosen Jiang 以及出處:http://www.aygfsteel.com/rosen