在企業應用程式中,持久化資料是常態。傳統關聯式資料庫擅長處理結構化資料,但面對影像、文件等二進位大物件時往往力不從心。雖然檔案系統能提供較佳效能,卻缺乏查詢語言、關聯模型與交易支援。隨著系統擴展,開放第三方存取儲存資料的需求日益迫切,但自行實作往往耗時且複雜,內部儲存結構更易滲漏至 API 設計,影響資料檢索與遍歷方式。
Java 內容倉庫 API (JCR,JSR-170) 旨在以實作無關的方式解決上述問題。無論底層資源為資料庫、本地或虛擬檔案系統,API 皆保持一致。JCR 在資料儲存之上提供存取控制、版本控管、事件通知、全文檢索等內容服務。專家群包含 Day Software、Vignette、IBM、Oracle 等重量級廠商,使其極可能成為內容管理與文件儲存的事實標準。

經過兩年半努力,JSR-170 於 2005 年 6 月定案,javax.jcr 套件約含 50 個類別 (多為介面與例外)。2006 年釋出參考實作 Jackrabbit 1.0 版。
JCR 以「倉庫」為核心,採樹狀結構儲存資訊,由節點與屬性組成。節點對應目錄,屬性對應檔案;每個節點有一父節點、任意子節點與屬性;屬性值支援布林、日期、浮點數、長整數、字串與串流等型別。唯有屬性儲存實際資料,節點則構成路徑。


核心類別為 Session,代表客戶端與倉庫的連線,包含工作區名稱與憑證。Session 涵蓋 Level 1/2 方法,若底層不支援則拋出例外。javax.jcr.query、nodetype、version、observation、lock 等套件分別處理查詢、節點型別與選用功能。javax.jcr.util 提供 ItemVisitor 實作,應用訪問者模式。
Spring Modules JCR 模組簡化 JSR-170 開發,核心特性:
JcrTemplate:封裝 Session 管理、例外轉換 (檢查型轉非檢查型)、執行緒綁定 Session,支援交易環境。RepositoryFactoryBean:配置、啟動/停止倉庫實例,預設支援 Jackrabbit、Jeceira,可擴充其他實作。SessionFactory:統一 Repository、Credentials、Workspace,支援自動註冊監聽器與自訂命名空間。OpenSessionInView 攔截器/過濾器:每請求單一 Session,管理工作外部化。<bean id="repository" class="org.springmodules.jcr.jackrabbit.RepositoryFactoryBean">
<property name="configuration" value="classpath:jackrabbit-repo.xml"/>
<property name="homeDir" ref="./tmp/repo"/>
</bean>
生產環境常將倉庫註冊於 JNDI,可用 JndiObjectFactoryBean 或 Spring 2.0 <jndi:lookup> 取得。
<bean id="jcrSessionFactory" class="org.springmodules.jcr.JcrSessionFactory">
<property name="repository" ref="repository"/>
<property name="credentials">
<bean class="javax.jcr.SimpleCredentials">
<constructor-arg index="0" value="bogus"/>
<constructor-arg index="1">
<bean factory-bean="password" factory-method="toCharArray"/>
</constructor-arg>
</bean>
</property>
</bean>
<bean id="jcrTemplate" class="org.springmodules.jcr.JcrTemplate">
<property name="sessionFactory" ref="jcrSessionFactory"/>
<property name="allowCreate" value="true"/>
</bean>
public Node importFile(final Node folderNode, final File file, final String mimeType, final String encoding) {
return (Node) jcrTemplate.execute(new JcrCallback() {
public Object doInJcr(Session session) throws RepositoryException, IOException {
JcrConstants jcrConstants = new JcrConstants(session);
Node fileNode = folderNode.addNode(file.getName(), jcrConstants.getNT_FILE());
Node resNode = fileNode.addNode(jcrConstants.getJCR_CONTENT(), jcrConstants.getNT_RESOURCE());
resNode.setProperty(jcrConstants.getJCR_MIMETYPE(), mimeType);
resNode.setProperty(jcrConstants.getJCR_ENCODING(), encoding);
resNode.setProperty(jcrConstants.getJCR_DATA(), new FileInputStream(file));
Calendar lastModified = Calendar.getInstance();
lastModified.setTimeInMillis(file.lastModified());
resNode.setProperty(jcrConstants.getJCR_LASTMODIFIED(), lastModified);
session.save();
return resNode;
}
});
}
程式碼包裝於 JcrCallback,JcrTemplate 自動處理 Session 開關、交易回滾與例外。硬編碼字串透過 JcrConstants 解析,增強健壯性。
JCR 視交易為選用功能,Jackrabbit 透過每個 Session 暴露 javax.transaction.XAResource 實作。Spring Modules 提供 LocalTransactionManager:
<bean id="jcrTransactionManager" class="org.springmodules.jcr.jackrabbit.LocalTransactionManager">
<property name="sessionFactory" ref="jcrSessionFactory"/>
</bean>
搭配 TransactionProxyFactoryBean 進行宣告式交易劃分。若需 JTA,可使用 Jackrabbit 捐贈套件的 JCA 連接器與 Jencks 等可插拔容器。
允許直接使用 JCR API 的程式碼透明地參與交易驅動 Session。配置 allowNonTxRepository 可支援非交易模式。
透過 SessionHolderProvider / SessionHolderProviderManager 實現可插拔支援。預設使用 JDK Service Provider 機制自動發現 (如 JackRabbitSessionHolderProvider),亦可改用 ListSessionHolderProviderManager 手動指定提供者清單,便於測試與客製化。
JSR-283 將聚焦聯邦化、遠端協定、擴充內容模型。生態系持續豐富:ORM 風格的物件映射框架、建構於 JCR 之 WebDAV 伺服器、Alfresco/BEA Portal/IBM Domino 等產品的 JCR 連接器。Spring Modules 路線圖包含 Acegi 安全整合、Spring 2.0 命名空間支援、更多 JCR 實作整合。Java 內容倉庫的未來一片光明。