程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> iBATIS 3內的新特性:將iBATIS用作應用程序內的一種持久框架

iBATIS 3內的新特性:將iBATIS用作應用程序內的一種持久框架

編輯:關於JAVA

簡介: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 庫加以引用,您就可以將本文中的例子用 於其他的數據庫。

iBATIS 概覽

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

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

清單 1. 示例應用程序內使用的 Automobile 類

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 配置文件。

最後,清單 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。這種對實參的支持使 main() 方法能夠進行一些有對象和 iBATIS 參與的示例 操作。

這個例子使用了 iBATIS Resources 類來從一個 XML 文件加載配置。Resources 類將文件作為一個 Reader 加載並將它傳遞給 SqlSessionFactoryBuilder。SqlSessionFactoryBuilder 能夠構造一個 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. 向運行配置添加實參

當然,也可以從命令行調用這個 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。

XML 配置變更

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

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

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

請注意,對於 iBATIS 的 beta 9 版本,有關從 XML 較老版本移植至新版本的文檔尚在編寫當中。

iBATIS 遷移

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

結束語

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

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved