iBATIS 3 內的新特性

簡介: iBATIS 是一個項目,主要用於數據訪問對象(data access object,DAO)和對象關係映射(object-relational mapping,ORM)。您可以使用它來輕鬆處理 Java™ 對象和數據庫。對 iBATIS 3 beta 9 的更新在 2010 年 1 月 23 日發佈。iBATIS 3 是 iBATIS 的完全重寫版幷包括了一些重大更新。本文將介紹 iBATIS 3 內的一些新特性。

如果您編寫 Java 代碼的時間已經不短,那麼您可能會回憶起這樣一些日子:您編寫了許多包含邏輯方法的類,這些方法會將 Java 對象內的數據映射到關係型數據庫管理系統(RDBMS)內的數據或從 RDBMS 內的數據映射到 Java 對象內的數據。而現在,對於大多數情況,這種人工干預是不受鼓勵的,而是推薦您使用 ORM 工具作爲一種最佳實踐。

ORM 工具讓您可以配置關係數據庫內的數據元素與 Java 對象屬性之間的映射。配置好後,這些工具讓您可以安心使用 Java 對象,而無需擔心 Java 類的屬性內的數據是如何存儲的或是如何檢索的,從而把您從大量重複代碼的編寫、調試和錯誤處理中解放了出來。

本文介紹了 iBATIS 3 內的新特性,iBATIS 3 是來自 Apache Foundation 的一個 ORM 工具,可用來構建連接到數據庫的 Java 應用程序。要最大程度地利用本文,建議使用 Java Development Kit (JDK) V5 或更新版本;Eclipse V3.4 或更新版本。本文使用的是 iBATIS 3 beta 9。iBATIS 站點表明該 beta 9 非常接近於通用版本 (GA),所以在 GA 版本可用後,本文中的例子也應適用於 GA 版本。

由於 iBATIS 的主要目的是進行對 RDBMS 的映射,所以還需要一個數據庫以便獲得這些例子的全貌。本文中的這個例子選擇使用 Apache Derby 作爲數據庫。有一點很值得注意,除了將您從編寫使用 Java Database Connectivity (JDBC) 的重複 Java 代碼中解放出來,ORM 工具的另一個優勢是能提供更好的數據層抽象。只需對 iBATIS 配置稍作更改以及對正確的 JDBC 庫加以引用,您就可以將本文中的例子用於其他的數據庫。ATIS 概覽

iBATIS 3 是一個持久框架,可用來配置 Java 類的屬性和 RDBMS 內的表列之間的映射。在配置時,此框架負責處理 JDBC 連接和分配。可以使用 XML 文件配置 iBATIS 3。iBATIS 可以從 iBATIS 站點(參見 參考資料)以壓縮歸檔文件(ZIP)的格式下載得到。在這個歸檔文件內是一個 Java Archive (JAR) 文件,可將其包括在 Java 對象內來提供所需的類。

清單 1 內所示的是一個在示例應用程序內使用的 Java 類。 


				
package com.ibm.developerWorks.examples.ibatis.model;

public class Automobile {

    private int id;
    private String make;
    private String model;
    private int year;

    public Automobile() {
        super();
    }

    public Automobile(final int id, final String make, final String model, 
        final int year) {

        super();
        this.id = id;
        this.make = make;
        this.model = model;
        this.year = year;
    }

    public int getId() {
        return id;
    }

    public String getMake() {
        return make;
    }

    public String getModel() {
        return model;
    }

    public int getYear() {
        return year;
    }

    public void setId(final int id) {
        this.id = id;
    }

    public void setMake(final String make) {
        this.make = make;
    }

    public void setModel(final String model) {
        this.model = model;
    }

    public void setYear(final int year) {
        this.year = year;
    }

    @Override
    public String toString() {
        return "Automobile [id=" + id + ", make=" + make + ", model=" + model + ", 
				    year=" + year + "]";
    }
}
            

這個 Automobile 類是一個簡單的 Java 對象(plain old Java object,POJO),包含了應用程序所用的數據。iBATIS 框架在配置後就能將這個對象持久化到數據庫或作爲一個方法(用來從數據庫選擇此對象)的結果返回此對象。

清單 2 中展示的這個 SQL 腳本創建了示例數據庫表。


清單 2. 用來創建 automobiles 表的 SQL 腳本
				
CREATE TABLE automobiles (
    id INT NOT NULL,
    make VARCHAR(255) NOT NULL,
    model VARCHAR(255) NOT NULL,
    model_year INT NOT NULL
);
            

執行這個數據庫腳本就能在數據庫內創建這個表。若使用 Derby 作爲數據庫,就可以使用 Derby 附帶的位於 bin 文件夾內的命令行實用工具運行此腳本(參見清單 3)。在運行這個例子之前,請確保將 DERBY_HOME 變量指定爲 Derby 安裝到的那個目錄的完整路徑,並將這個 SQL 腳本保存到名爲 create.sql 的一個文件內。


清單 3. 使用 Derby 命令行 ij 工具來運行 create.sql
				
$ cd $DERBY_HOME/bin
$ ./ij
> connect 'jdbc:derby:/tmp/MyDB';
> run create.sql
            

清單 4 內所示的這個 XML 映射文件允許您將 Java 類內的屬性映射到數據表內的數據列。


清單 4. XML 映射文件(automobile-mapper.xml)
				
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
    "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.ibm.developerWorks.examples.ibatis.model.Automobile">
    <resultMap type="Automobile" id="automobileResult">
        <result column="id" property="id" />
        <result column="make" property="make" />
        <result column="model" property="model" />
        <result column="model_year" property="year" />
    </resultMap>
    <select id="select" parameterType="int" resultType="Automobile"
        resultMap="automobileResult">
        select * from
        automobiles where id = #{id}
  </select>
    <insert id="insert" parameterType="Automobile">
        insert into automobiles (id,
        model, make, model_year)
        values (#{id}, #{model}, #{make}, #{year})
    </insert>
    <delete id="delete" parameterType="int">
        delete from automobiles where
        id = #{id}
  </delete>
    <delete id="deleteAll">
        delete from automobiles
  </delete>
</mapper>
            

這個 XML 映射文件包含 <select><insert><delete> 元素,三個元素內包含的代碼看上去像是常規的 ANSI SQL。這些 XML 元素名對應於 SQL 語句的類型 —<insert> 元素對應於 SQL INSERT 語句,以此類推。參數在 SQL 代碼內由 #{parameter} 定義,其中 parameter 是 Java 類內字段的名字。比如,Automobile 對象具有一個名爲 make 的字段,所以可以使用 #{make} 將此字段內存儲的值傳遞到這個 SQL 語句。

iBATIS 3 的一個新特性是在 Java 接口使用註釋能夠執行相同的配置。我將在稍後介紹如何使用 Java 5 註釋來代替 XML 配置文件(參見 Java 5 特性)。

最後,清單 5 展示了 iBATIS 3 的這個 XML 配置文件,其中指定了數據庫的名稱、要使用的驅動程序的類型以及其他的一些數據庫屬性,比如憑證。映射文件的名稱,比如 清單 4 內所示的名稱,是在配置文件的 <mappers> 元素中列出的。


清單 5. XML 配置文件(ibatis-config.xml)
				
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN"
  "http://ibatis.apache.org/dtd/ibatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias type="com.ibm.developerWorks.examples.ibatis.model.Automobile"
            alias="Automobile" />
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" 
                    value="org.apache.derby.jdbc.EmbeddedDriver" />
                <property name="url" value="jdbc:derby:/tmp/MyDB" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="automobile-mapper.xml" />
    </mappers>
</configuration>
            

如果您使用 Java 註釋方法來定義對象和數據庫之間的映射,那麼您將無需這個配置文件,因爲 iBATIS 3 提供了一個 Java 應用程序編程接口(API),讓您可以以編程方式完成此配置。

使用 iBATIS 的好處

使用 iBATIS 的一個好處是 XML 配置讓它得以成爲一個很好的可用來將對象映射到現有關係數據庫的 ORM 框架。有了 Mapper 類以及映射文件,重點就變成了將對象映射到現有的數據結構,而不是使一個數據結構遵從這個對象的結構。雖然配置成本要比使用一個使開發人員更獨立於數據結構的框架多了一些,但是單獨設計數據庫和對象模型的確有其自身的優勢。優秀的關係數據庫人員和對象模型設計者可能具有相互競爭的目標,從而使得其各自的實現也具有很大的差異。

過去,我也曾在項目中大量使用過 iBATIS,這些項目中的數據庫使用的是關係結構和存儲過程,並且開發人員對數據庫的設計也沒有多少控制。

創建示例 Java 項目

要測試本文中的這些例子,需要創建一個空的 Java 項目。在這個新的 Java 項目內,創建一個包括了 main() 方法的類,如清單 6 所示。


清單 6. Main
				
package com.ibm.developerWorks.examples.ibatis;

import java.io.IOException;

import javax.sql.DataSource;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.derby.jdbc.EmbeddedDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import com.ibm.developerWorks.examples.ibatis.data.AutomobileMapper;
import com.ibm.developerWorks.examples.ibatis.model.Automobile;

public class Main {

    private static final String CREATE = "create";
    private static final String DELETE = "delete";
    private static final String IBATIS_CONFIG = "ibatis-config.xml";
    private static final String SHOW = "show";

    @SuppressWarnings("static-access")
    private static Options createOptions() {
        Options options = new Options();
        options.addOption(CREATE, false, "creates the objects in the database");
        options.addOption(OptionBuilder.withArgName(SHOW).hasArg().withDescription(
                "shows the specified automobile").withLongOpt(SHOW).create());
        options.addOption(DELETE, false, "deletes all of the objects in database");
        return options;
    }

    private static SqlSessionFactory createSqlMapper() throws IOException {
        Reader reader = Resources.getResourceAsReader(IBATIS_CONFIG);
        return new SqlSessionFactoryBuilder().build(reader);
    }

    public static void main(final String[] args) {
        Options options = createOptions();
        try {
            CommandLine cmd = new GnuParser().parse(options, args);

            SqlSession session = createSqlMapper().openSession();
            
            try {
                if (cmd.hasOption(CREATE)) {
                    System.out.println("Creating the objects in the database...");
                    // Create the automobiles
                    session.insert(Automobile.class.getName() + ".insert", new
                        Automobile(1, "Toyota", "Tercel", 1993));
                    session.insert(Automobile.class.getName() + ".insert", new
                        Automobile(2, "Honda", "CR-V", 2000));
                    session.insert(Automobile.class.getName() + ".insert", new
                        Automobile(3, "Chevrolet", "Impala", 1964));
                    session.insert(Automobile.class.getName() + ".insert", new
                        Automobile(4, "Dodge", "Pickup", 1946));

                    session.commit();

                } else if (cmd.hasOption(SHOW)) {

                    Automobile auto = (Automobile) session.selectOne(
                        Automobile.class.getName() + ".select", cmd.getOptionValue(SHOW));
                    
                    if (auto == null) {
                        System.out.println("No matching results found!");
                    } else {
                        System.out.println(auto);
                    }

                } else if (cmd.hasOption(DELETE)) {

                    session.delete(Automobile.class.getName() + ".deleteAll");
                    session.commit();

                } else {
                    System.out.println("Doing nothing.");
                }

            } finally {
                session.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
            

爲了避免隨意的實參解析代碼,main 方法使用了 Apache Commons CLI 項目來解析從命令行發送給它的這些實參。Apache Commons CLI 項目的使用可以使此示例得以響應不同的命令行實參,比如 --create--delete--show。有關 Apache Commons CLI 項目的更多信息,請參見 參考資料。這種對實參的支持使 main() 方法能夠進行一些有對象和 iBATIS 參與的示例操作。

這個例子使用了 iBATIS Resources 類來從一個 XML 文件加載配置。Resources 類將文件作爲一個 Reader 加載並將它傳遞給 SqlSessionFactoryBuilderSqlSessionFactoryBuilder 能夠構造一個 SqlSessionFactory,而後者則又被用來創建這些 SqlSession 對象,以讓您的代碼可以通過映射類內定義的方法與數據庫交互。

在編譯和運行示例代碼前,您需要導入 iBATIS 3、Apache Commons CLI 和 Apache Derby JAR 文件(ibatis-3-core-x.jar、commons-cli-1.2.jar 和 derby.jar)。

運行這個例子

通過從 Eclipse 運行主類,就可以運行這個例子了。如果想要向 Java 調用添加實參,可以打開 Run > Run Configurations 並找到 Java Application/Main run 配置。在 Arguments 選項卡,指定想要在 Program Arguments(如圖 1 所示)內提供的實參。


圖 1. 向運行配置添加實參
這個屏幕快照顯示了參數 ‘--show 3’ 被添加到 Eclipse 上的應用程序 Arguments 選項卡

當然,也可以從命令行調用這個 Java 應用程序,不過請記住將 classpath 設置爲包括 iBATIS 3、Apache Commons CLI 以及 Apache Derby JAR 文件。有關如何調用應用程序的例子,可以參見清單 7。


清單 7. 從命令行運行應用程序
				
$ java -classpath {jars} com.ibm.developerWorks.examples.ibatis.Main --create
Creating the objects in the database...
            

在執行這個 Java 應用程序時,可以看到 --create 創建了四個新 Automobile 對象並將它們添加到數據庫的 automobiles 表。使用 --delete 實參可以從數據庫刪除這些對象。使用具有 ID 的 --show 運行這個 SQL 腳本以獲得匹配的數據庫記錄,創建一個具有數據的 Automobile 對象並將結果顯示到控制檯。

XML 配置示例正常工作後,您就可以開始瞭解 iBATIS 3 的另一個關鍵的新特性:Java 註釋支持。

Java 5 特性

iBATIS 3 帶來了一些新的變化,允許您利用 Java 5 註釋。通過使用註釋,您可以創建 mapper 接口來供您在 Java 代碼內進行從對象到數據庫的全部映射。清單 8 所示的代碼展示了這個用於配置(並非 XML 配置)的 AutomobileMapper 接口。


清單 8. AutomobileMapper 接口
				
package com.ibm.developerWorks.examples.ibatis.data;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

import com.ibm.developerWorks.examples.ibatis.model.Automobile;

public interface AutomobileMapper {

	@Select("select id, make, model, model_year as \"year\" from automobiles 
	    where id = #{id}")
	Automobile selectAutomobile(final int id);
	
	@Insert("insert into automobiles(id,make,model,model_year) 
	    values (#{id}, #{make}, #{model}, #{year})")
	void insertAutomobile(final Automobile arg);
	
	@Delete("delete from automobiles")
	void deleteAll();
	
}
            

Automobile mapper 的 XML 配置中,resultMap 元素用來映射 model_year 數據庫列與 Automobile 對象上的 year 字段。這個映射相當簡單,可以在註釋內進行,只要使用爲該列賦別名的 SQL 特性就可以了,這個特性可由在 @Select 註釋內定義的 SQL 完成。

AutomobileMapper 接口內的 Select 註釋將 selectAutomobile 方法映射到用來根據給定值從 automobiles 表選擇一個記錄的 SQL。這個值被指定爲實參的 id 參數並被作爲 #{id} 包括在這個 SQL 語句內,正如其在 XML 配置中那樣。使用 Java 接口映射這些 SQL 方法的一個極大的好處是 feedbac 會以編譯時錯誤的形式出現在 Java 編輯器中。這樣一來,我們就可以確認這些方法均能返回正確的類型,而 XML 配置通常需要先執行代碼才能找到錯誤。

此外,iBATIS 3 現在還支持接口繼承,允許對 Java 接口進行優化以減少代碼重複。

iBATIS 文檔中有這樣的一個提示,即對於較小且較爲簡單的項目,註釋可以更爲簡單和易讀。不過,較 XML 配置而言,註釋的功能相對有限。若項目中包含複雜的對象或複雜的數據庫結構,請考慮使用 XML 配置,而不是 Java 註釋。

其他的 API 變更

iBATIS 的基於註釋的配置要求實例化也要稍微不同。我們不再使用 Reader 類來讀取 XML 配置,而是要向 Configuration 對象添加映射程序,如清單 9 所示。


清單 9. 使用了註釋配置的新 Java 代碼
				
package com.ibm.developerWorks.examples.ibatis;

// snipped imports

public class Main {

    // snipped constants declarations--didn't change

    // new method for creating data source
    private static DataSource createDataSource() { EmbeddedDataSource 
    dataSource = new org.apache.derby.jdbc.EmbeddedDataSource(); 
    dataSource.setDatabaseName("/tmp/MyDB"); return dataSource; }

    @SuppressWarnings("static-access")
    private static Options createOptions() {
		   // snipped... no changes
    }

    private static SqlSessionFactory createSqlMapper() throws
     IOException { DataSource datasource = createDataSource();
      TransactionFactory transaction = new JdbcTransactionFactory();
       Configuration configuration = new Configuration(new Environment
       ("development", transaction, datasource)); configuration
       .addMapper(AutomobileMapper.class);
        return new SqlSessionFactoryBuilder().build(configuration);
    }

    public static void main(final String[] args) {
        Options options = createOptions();
        try {
            CommandLine cmd = new GnuParser().parse(options, args);

            SqlSession session = createSqlMapper().openSession();
            AutomobileMapper mapper = session.getMapper(AutomobileMapper.class);
            
            try {
                if (cmd.hasOption(CREATE)) {
                    System.out.println("Creating the objects in the database...");
                    // Create the automobiles
                    mapper.insertAutomobile(new Automobile
                    (1, "Toyota", "Tercel", 1993)); 
                    mapper.insertAutomobile(new Automobile
                    (2, "Honda", "CR-V", 2000)); 
                    mapper.insertAutomobile( new Automobile(3, 
                    "Chevrolet", "Impala", 1964)); 
                    mapper.insertAutomobile(new Automobile(4, "Dodge", "Pickup", 1946));

                    session.commit();

                } else if (cmd.hasOption(SHOW)) {
Automobile auto = mapper.selectAutomobile( Integer.parseInt(cmd.getOptionValue(SHOW)));

                    if (auto == null) {
                        System.out.println("No matching results found!");
                    } else {
                        System.out.println(auto);
                    }

                } else if (cmd.hasOption(DELETE)) {

                    mapper.deleteAll();
                    session.commit();

                } else {
                    System.out.println("Doing nothing.");
                }

            } finally {
                session.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
            

提供給 addMapper() 方法的 AutomobileMapper 是與 清單 8 中所示的相同的 Java 接口。

Configuration 對象還需要 DataSource 實現。在本例中,DataSource 實現是在靜態 createDataSource() 方法中輕鬆創建的。不過,在生產應用程序中,應該對之進行修改以使數據源信息 — 比如數據庫名 — 更爲動態。如果是在一個負責管理持久性的應用服務器或 Web 服務器內構建應用程序,則可以使用 Java Naming and Directory Interface (JNDI) 來獲得 DataSource


如果在從之前的 iBATIS 版本升級到新版本時決定爲 iBATIS 使用 XML 配置而不是 Java 註釋,那麼就會發現新舊版本間的 XML 存在一些關鍵區別。

iBATIS 的之前版本使用了 parameterMap(類似於 resultMap)來映射這些方法的參數。不過,已經不建議使用 parameterMap 元素,並且不應該繼續使用它。相反,應該在 parameterType 內包括對象的類型並使用標準標記訪問類型的屬性(比如,爲 Java 對象上的 id 字段使用 #{id})。

配置以及 mapper 文件的根元素均被更新以包括新的 Document Type Definition (DTD) 聲明,並且這些元素中的一些已被移動了位置以提供更好的組織性。

有關 iBATIS XML 配置變更的更多信息,請參看 參考資料。請注意,對於 iBATIS 的 beta 9 版本,有關從 XML 較老版本移植至新版本的文檔尚在編寫當中。

iBATIS 遷移

iBATIS Schema Migrations System(iBATIS 遷移)項目並非是用來將 XML 配置的較老模式遷移到新模式的項目。相反,該項目旨在隨着數據庫的發展在將數據庫從一個版本轉變到另一個版本時簡化數據庫變更的遷移。您可以使用這個工具來生成可供您自動應用變更的 SQL 腳本,這可極大地減少錯誤。

有關 iBATIS Migrations 項目的更多信息,請參見 參考資料

結束語

iBATIS 3 是一個 ORM 持久框架,用來將 Java 對象內的屬性映射到數據庫內的表列。iBATIS 以映射爲中心,重點在於映射一個優秀的對象模型與一個優秀的關係型數據庫設計。

iBATIS 3 內的一個新特性是使用 Java 註釋進行映射,這就使得映射更爲整潔也更爲直觀,而且可以以 Java 源代碼的形式爲很多項目所用。iBATIS 3 還提供了使用 XML 配置文件進行對象映射的功能。這種雙重配置方法的存在讓您可以爲自己的項目選用最爲簡單的配置 iBATIS 的方式。



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