iBATIS SQL Maps入門教程
準備使用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);
…
好了
容易出錯的地方
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接口,以備應用的進一步擴展。