企業應用核心難題:將豐富領域模型 (Java 物件圖) 持久化到關聯式資料庫。手寫 JDBC/SQL 維護成本高、易錯、難移植。Castor JDO (Java Data Objects) 作為早期開源 ORM 先驅 (1999 首發),提供 100% 純 Java、基於 XML 映射檔、支援多種資料庫的資料綁定框架,至今仍是理解 ORM 核心概念的優秀教材。
public class Product implements Persistent {
private int _id;
private String _name;
private float _price;
private ProductGroup _group;
private Database _db; // Castor 注入
// 關聯集合
private Vector _details = new Vector(); // 1:N ProductDetail
private Vector _categories = new Vector(); // M:N Category
// Getter/Setter...
// 關聯管理便利方法
public void addDetail(ProductDetail detail) {
_details.add(detail);
detail.setProduct(this); // 維護雙向關聯
}
public void addCategory(Category category) {
if (!_categories.contains(category)) {
_categories.addElement(category);
category.addProduct(this);
}
}
}
映射檔 `mapping.xml` 定義類↔表、屬性↔欄位、關聯↔外鍵/關聯表對應。Castor 利用自家 Castor XML 解析映射檔,實現「XML 配置 Java 物件」元模型。
<class name="myapp.Product" identity="id">
<description>Product definition</description>
<map-to table="prod" xml="product" />
<field name="id" type="integer">
<sql name="id" type="integer" />
</field>
<field name="name" type="string">
<sql name="name" type="char" />
</field>
<field name="price" type="float">
<sql name="price" type="numeric" />
</field>
<!-- 多對一:多產品屬同一群組 -->
<field name="group" type="myapp.ProductGroup">
<sql name="group_id" />
</field>
<!-- 一對多:一產品多明細 -->
<field name="details" type="myapp.ProductDetail"
required="true" collection="vector">
<sql many-key="prod_id" />
</field>
<!-- 多對多:產品-分類 -->
<field name="categories" type="myapp.Category"
required="true" collection="vector">
<sql name="category_id" many-table="category_prod" many-key="prod_id" />
</field>
</class>
| 屬性/元素 | 作用 |
|---|---|
| `identity` | 指定主鍵屬性 (對應資料庫 PK) |
| `map-to table` | 對應資料庫表名 |
| `field type` | Java 型別,決定 TypeConverter 選擇 |
| `sql name/type` | 資料庫欄位名與 SQL 型別 |
| `collection="vector"` | 集合型別 (支援 vector/arraylist/set/map) |
| `many-key` | 一對多關聯的外鍵欄位 (在「多」方表中) |
| `many-table/many-key` | 多對多關聯的中間表與雙向外鍵 |
| `depends` | 標記從屬物件 (生命週期綁定主物件) |
<!-- Computer 繼承 Product -->
<class name="myapp.Computer" extends="myapp.Product" identity="id">
<map-to table="computer" xml="computer" />
<field name="id" type="integer"><sql name="id" type="integer"/></field>
<field name="cpu" type="string"><sql name="cpu" type="char"/></field>
</class>
<class name="myapp.ProductDetail" identity="id" depends="myapp.Product">
<map-to table="prod_detail" xml="detail" />
<field name="product" type="myapp.Product">
<sql name="prod_id" />
</field>
...
</class>
Castor 實作 ODMG 3.0 OQL 子集,語法類 SQL 但查詢物件圖而非資料表,支援多資料庫自動轉譯 SQL。
// 所有 Computer
SELECT c FROM myapp.Computer c
// ID=1234 的 Computer
SELECT c FROM myapp.Computer c WHERE c.id = $1
query.bind(1234)
// 名稱模糊匹配
SELECT c FROM myapp.Computer c WHERE c.name LIKE $1
query.bind("%abcd%")
// ID 列表查詢
SELECT c FROM myapp.Computer c WHERE c.id IN LIST ($1, $2, $3)
query.bind(97); query.bind(11); query.bind(7)
支援別名 (c)、參數綁定 ($1, $2...)、路徑導航 (c.group.name)。
單一資料庫事務邊界內完成載入/修改/提交。適用於短時業務操作。
解決 Web 應用「讀取 → 用戶思考/編輯 → 提交」跨越長時間 (甚至天) 的場景,避免長時間持有資料庫寫鎖。
// 第一階段:唯讀載入 (短事務 1)
public LineItem getLineItem() {
db.begin();
LineItem item = (LineItem) db.load(LineItem.class, id, Database.ReadOnly);
db.commit();
return item; // 物件脫離事務上下文,用戶可自由編輯
}
// 第二階段:更新提交 (短事務 2)
public void updateOrder(LineItem item) {
db.begin();
db.update(item); // 重新附加到事務上下文
db.commit();
}
DB2、HypersonicSQL、Informix、InstantDB、Interbase、MySQL、Oracle、PostgreSQL、SAP DB、SQL Server、Sybase、Generic (標準 JDBC 回退)。新增資料庫支援只需實作對應的 `TypeConverter` 與 SQL 方言適配。
| 時代 | 代表技術 | 核心貢獻 |
|---|---|---|
| 2000-2005 | Castor JDO, Hibernate 1/2 | 確立 ORM 基礎概念:映射檔、Session/Database、延遲載入、查詢語言 |
| 2006-2010 | Hibernate 3, JPA 1.0, TopLink/EclipseLink | 標準化 (JPA)、註解配置、實體管理器、Criteria API |
| 2011-至今 | Spring Data JPA, Hibernate 5/6, jOOQ, MyBatis | Repository 抽象、反應式、Kotlin 協程、類型安全 SQL |