簡介
本文是初學者的快速入門教程,涵蓋了 SQL Map 的一個簡單而典型的應用。每個主題
更詳細的資訊可以參考《iBatis SQL Maps 2.0 開發指南》 。
本文是《iBatis SQL Maps Tutorial》的中文版,僅供讀者參考。最權威的以 Clinton Begin
的官方文檔為準,它可以從 http://www.ibatis.com 網站下載。如果中文翻譯有錯誤,請通知
譯者(email:toleu@21cn.com,Blog:http://starrynight.blogdriver.com/) 。
準備使用 SQL Map
SQL Map 架構能應用於設計不好的資料庫模型甚至是設計不好的物件模型。儘管如此,
您在設計資料庫模型和物件模型時,還是應該遵循最佳的設計原則。這樣,您會獲得更好的
效能和更簡潔清晰的設計方案。
設計最容易開始的地方是分析應用的商務邏輯。分析什麼是應用的業務對象,什麼是數
據模型以及兩者之間的關係。作為快速入門第一個例子,我們使用一個簡單的 Java Bean
Person 類。
Person.java
package examples.domain; //imports implied…. public class Person { private int id; private String firstName; private String lastName; private Date birthDate; private double weightInKilograms; private double heightInMeters; public int getId () { return id; } public void setId (int id) { this.id = id; } //…let’s assume we have the other getters and setters to save space… }
Person 類有了, 如何將 Person 類映射成資料表呢?SQL Map對 Java Bean和資料表之間
的關係沒有限制,如一個資料表映射成一個 Java Bean,或多個表映射成一個 Java Bean,或
多個 Java Bean 映射成一個資料表等。因為使用 SQL Map 您可以充分發揮 SQL 陳述式的全部
潛力而很少限制。下面這個例子,我們使用一個簡單的表,將一個表映射成一個 Java Bean,
Java Bean 和表是一對一的關係。
Person.sql
CREATE TABLE PERSON( PER_ID NUMBER (5, 0) NOT NULL, PER_FIRST_NAME VARCHAR (40) NOT NULL, PER_LAST_NAME VARCHAR (40) NOT NULL, PER_BIRTH_DATE DATETIME , PER_WEIGHT_KG NUMBER (4, 2) NOT NULL, PER_HEIGHT_M NUMBER (4, 2) NOT NULL, PRIMARY KEY (PER_ID) )
SQL Map的設定檔
現在準備好了學習環境,讓我們從學習 SQL Map 的設定檔開始,設定檔是 SQL
MAP的配置資訊統一設定的地方。
SQL Map 設定檔是 XML 檔案,我們可以它設定各種屬性,JDBC DataSource和 SQL
Map。在設定檔中,可以方便地統一配置 DataSource 不同的實現。SQL Map 架構套件括
DataSource的 iBATIS 實現: SimpleDataSource 類, Jakarta DBCP (Commons) , 和可通過 JNDI
上下文尋找的 DataSource(即應用伺服器中的 DataSource) 。詳細的使用方法在以後的章節
討論。在本例中,我們使用 Jakarta DBCP。對於上面的例子,配置非常簡單,如下所示:
SqlMapConfigExample.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://www.ibatis.com/dtd/sql-map-config-2.dtd"> <!-- Always ensure to use the correct XML header as above! --> < sqlMapConfig> <!-- The properties (name=value) in the file specified here can be used placeholders in this config file (e.g. “${driver}”. The file is relative to the classpath and is completely optional. --> <properties resource="examples/sqlmap/maps/SqlMapConfigExample.properties" /> <!-- These settings control SqlMap configuration details, primarily to do with transaction management. They are all optional (see the Developer Guide for more). --> <settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="true" maxRequests="32" maxSessions="10" maxTransactions="5" useStatementNamespaces="false" /> <!-- Type aliases allow you to use a shorter name for long fully qualified class names. --> <typeAlias alias="order" type="testdomain.Order"/> <!-- Configure a datasource to use with this SQL Map using SimpleDataSource. Notice the use of the properties from the above resource --> <transactionManager type="JDBC" > <dataSource type="SIMPLE"> <property name="JDBC.Driver" value="${driver}"/> <property name="JDBC.ConnectionURL" value="${url}"/> <property name="JDBC.Username" value="${username}"/> <property name="JDBC.Password" value="${password}"/> </dataSource> </transactionManager> <!-- Identify all SQL Map XML files to be loaded by this SQL map. Notice the paths are relative to the classpath. For now, we only have one… --> <sqlMap resource="examples/sqlmap/maps/Person.xml" /> </sqlMapConfig>
SqlMapConfigExample.properties
# This is just a simple properties file that simplifies automated configuration # of the SQL Maps configuration file (e.g. by Ant builds or continuous # integration tools for different environments… etc.) # These values can be used in any property value in the file above (e.g. “${driver}”) # Using a properties file such as this is completely optional. driver=oracle.jdbc.driver.OracleDriver url=jdbc:oracle:thin:@localhost:1521:oracle1 username=jsmith password=test
SQL Map的對應檔
現在 DataSource 已經配置好了,並且有了統一的 SQL Map 設定檔,我們還需要 SQL
Map 的對應檔。對應檔包括 SQL 陳述式和參數對象和結果對象的映射。
繼續上面的例子,我們從一個簡單的查詢語句開始,為 Person 類和 PERSON 表之間創
建一個 SQL Map 對應檔。
Person.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd"> <sqlMap namespace="Person"> <select id="getPerson" resultClass="examples.domain.Person"> SELECT PER_ID as id, PER_FIRST_NAME as firstName, PER_LAST_NAME as lastName, PER_BIRTH_DATE as birthDate, PER_WEIGHT_KG as weightInKilograms, PER_HEIGHT_M as heightInMeters FROM PERSON WHERE PER_ID = #value# </select> </sqlMap>
上面的例子是 SQL Map最簡單的形式。它使用了 SQL Map架構中一個特性,根據匹配
的名字將 ResultSet 的列映射成 Java Bean 的屬性(或Map的 key值) 。#value#符號是輸入參
數,該符號表示使用了基本類型的封裝類作為輸入參數(即 Integer,但不僅限於此類型) 。
以上的方法雖然很簡單,但有一些限制,無法指定輸出欄位的資料類型,無法自動地在
結果對象中載入相關的資訊(即 Java Bean 無法使用複雜的屬性) ;以上的方法對效能還有
輕微的不利影響,因為需要讀取 ResultSetMetaData 的資訊。使用 resultMap,可以克服以上
的不足,但現在只需要一個簡單的例子,以後我們再轉向其他不同的方法(無須修改 Java
代碼) 。
大多數的應用不僅需要從資料庫中讀取資料,還需要修改資料。我們已有了一個
SELECT 查詢語句的 mapped statement 簡單例子,下面看看 INSERT,UPDATE 和 DELETE
的 mapped statement 什麼樣子。幸運的是,它們其實沒什麼區別。接下來,我們完成 Person
SQL Map 其他部分,以實現修改資料的功能。
Person.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd"> <sqlMap namespace="Person"> <!-- Use primitive wrapper type (e.g. Integer) as parameter and allow results to be auto-mapped results to Person object (Java Bean) properties --> <select id="getPerson" parameterClass=”int” resultClass="examples.domain.Person"> SELECT PER_ID as id, PER_FIRST_NAME as firstName, PER_LAST_NAME as lastName, PER_BIRTH_DATE as birthDate, PER_WEIGHT_KG as weightInKilograms, PER_HEIGHT_M as heightInMeters FROM PERSON WHERE PER_ID = #value# </select> <!-- Use Person object (Java Bean) properties as parameters for insert. Each of the parameters in the #hash# symbols is a Java Beans property. --> <insert id="insertPerson" parameterClass="examples.domain.Person"> INSERT INTO PERSON (PER_ID, PER_FIRST_NAME, PER_LAST_NAME, PER_BIRTH_DATE, PER_WEIGHT_KG, PER_HEIGHT_M) VALUES (#id#, #firstName#, #lastName#, #birthDate#, #weightInKilograms#, #heightInMeters#) </insert> <!-- Use Person object (Java Bean) properties as parameters for update. Each of the parameters in the #hash# symbols is a Java Beans property. --> <update id="updatePerson" parameterClass="examples.domain.Person"> UPDATE PERSON SET PER_FIRST_NAME = #firstName#, PER_LAST_NAME = #lastName#, PER_BIRTH_DATE = #birthDate#, PER_WEIGHT_KG = #weightInKilograms#, PER_HEIGHT_M = #heightInMeters# WHERE PER_ID = #id# </update> <!-- Use Person object (Java Bean) “id” properties as parameters for delete. Each of the parameters in the #hash# symbols is a Java Beans property. --> <delete id="deletePerson" parameterClass="examples.domain.Person"> DELETE PERSON WHERE PER_ID = #id# </delete> </sqlMap>
使用 SQL Map架構編程
好了,我們完成了所有的設定檔和對應檔,就剩下的應用的編碼工作了。首先要設
置SQL Map, 讀入剛建立好的SQL Map XML設定檔。 為簡化這個工作, 可以使用SQL Map
架構中提供的 Resources類。
String resource = “com/ibatis/example/sql-map-config.xml”;
Reader reader = Resources.getResourceAsReader (resource);
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
以上的 SqlMapClient 對象是安全執行緒,並且應持久生存。對於一個特定的應用,只需
進行一次 SqlMap 配置。因此,它可以作為基類的一個靜態對象(即 DAO對象的基類) ,或
者,如果您想讓它有更大的作用範圍,可以把它封裝在方便使用的類中。例如:
public class MyAppSqlConfig { private static final SqlMapClient sqlMap; static { try { String resource = “com/ibatis/example/sql-map-config.xml”; Reader reader = Resources.getResourceAsReader (resource); sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader); } catch (Exception e) { // If you get an error at this point, it matters little what it was. It is going to be // unrecoverable and we will want the app to blow up good so we are aware of the // problem. You should always log such errors and re-throw them in such a way that // you can be made immediately aware of the problem. e.printStackTrace(); throw new RuntimeException (“Error initializing MyAppSqlConfig class. Cause: ”+e); } } public static getSqlMapInstance () { return sqlMap; } }
從資料庫讀取對象
既然 SqlMap對象已完成初始化,就可以方便地使用它了。首先,我們用它從資料庫中
讀取一個 Person 對象。 (在本例中,假設 PERSON 表中已存在 10 條記錄,PER_ID 從 1 到
10) 。
要從資料庫中得到一個 Person 對象,只需要 SqlMap 執行個體,mapped statement 的名字和
一個 Person ID號。讓我們讀入 PER_ID 是 5 的 Person 對象。
…
SqlMapClient sqlMap = MyAppSqlMapConfig.getSqlMapInstance(); // as coded above
…
Integer personPk = new Integer(5);
Person person = (Person) sqlMap.queryForObject (“getPerson”, personPk);
…
把對象寫入資料庫
現在已有了一個從資料庫中讀出的Person對象, 接著修改Person對象的height和weight
屬性,並將它寫入資料庫。
…
person.setHeightInMeters(1.83); // person as read from the database above
person.setWeightInKilograms(86.36);
…
sqlMap.update(“updatePerson”, person);
…
要刪除這個 Person 對象,也很容易。
…
sqlMap.delete(“deletePerson”, person);
…
類似地,也可以建立一個新的 Person 對象。
Person newPerson = new Person();
newPerson.setId(11); // you would normally get the ID from a sequence or custom table
newPerson.setFirstName(“Clinton”);
newPerson.setLastName(“Begin”);
newPerson.setBirthDate (null);
newPerson.setHeightInMeters(1.83);
newPerson.setWeightInKilograms(86.36);
…
sqlMap.insert (“insertPerson”, newPerson);
…
好了,快速入門課程終於學完了。
下一步…
至此本教程結束了。,還有 JPetStore 4,它是一個完整的 Web應用例子,基於 Jakarta Struts,iBatis DAO
2.0 和 SQL Maps 2.0。
附錄:容易出錯的地方
本附錄是譯者添加的,列出了初學者容易出錯的地方,作為完成快速入門課程後的學習
筆記,可以讓初學者少走些彎路。僅供參考。
1) 在 parameterMap 和 resultMap 中,欄位資料類型是 java.sql.Types 類定義的常量名
稱。常用的資料類型包括 BLOB,CHAR,CLOB,DATE,LONGVARBINARY,
INTEGER,NULL,NUMERIC,TIME,TIMESTAMP 和 VARCHAR 等。
2) 對於資料表中 NULLBALE 的欄位,必須在 parameterMap 和 resultMap 中指定欄位
的資料類型。
3) 對於資料類型是 DATE, CLOB 或 BLOB 的欄位, 最好在 parameterMap 和 resultMap
中指定資料類型。
4) 對於二進位類型的資料,可以將 LONGVARBINARY 映射成 byte[]。
5) 對於文本類型較大的資料,可以將 CLOB 映射成 String。
6) Java Bean 必須擁有預設的構造器(即無參數的構造器)。
7) Java Bean 最好實現 Serializable 介面,以備應用的進一步擴充。