企業最重要的資產應該是數據信息,但現在的企業應用除了需要存儲數據外,還經常要求跟蹤數據變化整個過程,並會擴展到一系列相關的要求,如數據變化的原因、變化的時間等,而且在許多情況下是對以文檔形式存儲的數據進行跟蹤。使用SubVersion可以滿足這些貌似普通但實際上很復雜的要求
來自數據的挑戰
企業應用存儲了關鍵數據,而且應用程序並不僅限於對數據進行插入、讀取、更新和刪除操作(即CRUD),應用程序還期望能夠存儲數據更改的歷史記錄。此外,企業按照一系列的業務或者規定的要求,不但要求存儲數據資產更改結果的歷史,而且要求存儲是誰,在什麼時候,因為什麼原因,如何改變了數據等等諸如此類的跟蹤信息。
應用數據的形式和尺寸也有很多變數,既有簡單數據,如字符串和數字型,也有復雜的類型,如使用Blob或Cblob類型來存儲文檔。典型的應用程序要處理大量的上傳給程序處理的以文檔形式存儲的數據,如果用傳統的歷史表等方式來跟蹤諸如復雜類型的文檔的變化,簡直就是做一場惡夢。
使用歷史表進行跟蹤
關系數據庫是存儲數據的首選,可以高效地組織、存儲、檢索數據信息,由於應用程序將數據存放在關系數據庫中,當然就順理成章的嘗試用它來存放歷史跟蹤數據,一般是使用帶有時間戳的數據表來存放所有的重要數據表。在更新主表的時候會把舊數據推入歷史表中,這個過程一般是通過觸發器或由應用程序自己來完成。
使用歷史表存儲歷史信息,會存在以下問題:
.關系型數據庫和關系模型會提高數據存儲和檢索的效率,但是歷史表顯然不適合使用關系型數據庫。
.數據庫不支持版本控制。應用程序不得不使用觸發器或其它定制的技術來仔細的存放數據(,以便實現版本控制功能)。
.必須由應用程序親自檢測版本之間的變化,從歷史表中檢索歷史數據進行互相比較。
關系數據庫依舊是存儲和檢索業務數據的倉庫,它們擅長於管理數據。以上列舉的缺點僅限於用關系數據模型存儲多個不同的版本的數據並進行歷史數據跟蹤的情況下。
Subversion 和 JavaSVN
Subversion是一個可以代替CVS(一個傳統的版本控制系統)的版本控制系統。Subversion使用稱作倉庫的樹狀結構來存儲文件和目錄。Subversion會跟蹤對倉庫中信息的所有改變,它具有一個中央倉庫,允許進行並發更新,允許通過http或https使用WebDAV協議來訪問倉庫,可以避免使用過程中的防火牆的干擾。Subversion的理念就是“拷貝-編輯-合並”,這就意味著在修改時不需要鎖定被修改的對象。
(譯者注:關於WebDAV,是Web-based Distributed Authoring and Versioning的縮寫,是一個標准HTTP協議的擴展,通過web技術把目錄和文件作為可讀些的對象進行共享讀寫,把web變成一個可讀寫的媒體。RFCs2518和3253描述了WebDAV/DeltaV 對於HTTP的擴展,網址http://www.webdav.org/。)
JavaSVN是一個純Java的Subversion客戶端類庫,提供與Subversion交互的基於Java程序的應用程序接口(API), JavaSVN既提供了進行直接讀取Subversion倉庫的底層接口,也提供了從Subversion倉庫檢出工作拷貝的高層接口。
現在,應用程序可以使用結合了關系型數據庫和Subversion的方式來滿足數據存儲和變化跟蹤的需求了,對數據庫的更新同時會將變化情況提交到Subversion中,Subversion將是記錄變化的主要數據源,關系數據庫則用於除此以外所有的其他存儲。這樣做還有一個優勢,由於Subversion使用“拷貝-編輯-合並”模式,這樣每次從關系數據庫中檢索數據時不再要求鎖定目標表了。
實例學習
現在讓我們來確定一下要解決的問題和解決方案,並使用實例來說明如何使用Subversion 和JavaSVN。我們使用JavaSVN將一個簡單的領域對象存儲到Subversion 中,檢索以前的版本,並顯示兩個版本的差異。我們示例的領域對象是以下所示的貸款數據。在本文結尾的“資源”部分有完整的源代碼鏈接。
public class LoanData extends BaseTrackingObject {
private String loanId;
private double loanAmount;
private float loanRate;
private int loanTerm;
......
......
}
在這裡,使用抽象的BaseTrackingObject類來定義通用的跟蹤數據,如修改用戶、修改日期、修改原因等。其中定義了設置和取得objectId抽象方法,把它用作主鍵來訪問領域對象;定義了一個命名為getXmlRepresentation的工具方法,用於把對象轉換成XML格式,進而用於在Subversion中存儲和檢索數據。
初始化JavaSVN
SVNManager類是通向Subversion的路由,用於在不使用工作拷貝的情況下,通過底層JavaSVN接口直接訪問Subversion倉庫,通過初始化JavaSVN類庫來可以使用HTTP(S)或SVN(S)與Subversion倉庫進行交互。在這裡,我們選擇使用HTTP (WebDAV),因為可以減少在處理防火牆方面的工作。
庫的初始化工作要首先調用的是方法DAVRepositoryFactory.setup()。SVNRepository類包含了所有直接訪問Subversion倉庫的方法,將Subversion倉庫樹狀結構的根路徑提供給SVNRepositoryFactory類後,就完成了這個類的初始化,而ISVNAuthenticationManager類的作用是向SVNRepository提供訪問Subversion倉庫的授權信息。
public void initRepository() {
//initialize the system to work over http
DAVRepositoryFactory.setup();
............
//point to the root folder of repository
SVNURL svnUrl = SVNURL.parseURIEncoded
("http://localhost/repos/");
//initialize the SVNRepository
theRepository = SVNRepositoryFactory.
create(svnUrl);
//Creates the Auth manager with our user
//and password credentials
ISVNAuthenticationManager authManager =
new BasicAuthenticationManager
(name, password);
//provides the credentials to the
//SVNRepository
theManager.setAuthenticationManager
(authManager);
........
}
在Subversion中存儲數據
Subversion需要使用層次結構存儲數據,這樣我們先要設定一下領域實體的層次結構,這裡使用一個命名為“DomainObjects”的文件夾來存放領域數據。領域對象類將會檢測這個目錄下存放領域對象的所有子目錄,而每個獨立的域對象被以XML格式進行存儲,並以其主鍵進行命名。
為存儲LoanData域對象,我們先要執行SVNManager對象的checkInObject方法,通過SVNRepository 執行的ISVNEditor對象來在Subversion倉庫中的建立和更新域對象的版本,但只有在closeEdit被調用後,才會提交所有的操作。SVNDeltaGenerator類用於獲取當前版本與被更新版本之間的差異,Subversion通過存儲版本間差異部分的形式存放新的版本,這樣會使提高網絡效率。
public SVNResult checkInObject(
BaseTrackingObject obj){
.....
//Obtain the editor handle from the
//repository
ISVNEditor editor = theRepository.
getCommitEditor(obj.
getModificationReason(), null);
....
//create the file at the specified path
editor.addFile(path, null, -1);
}
else {
//file is already present, so open
//the file in the repository
editor.openFile(path, -1);
}
....
String checksum = deltaGenerator.
sendDelta(path,
new ByteArrayInputStream(
obj.getXmlRepresentation().
getBytes()),
editor, true);
.....
editor.closeEdit();
...
}
檢索變化歷史
為檢索指定領域對象的歷史版本,需要調用SVNManager類的getRevisionLog方法; SVNRepository類的getLatestRevision方法可以得到當前版本號;SVNManager.log方法可以檢索每個版本的日志,日志可以包含版本修訂的日期、修改人、修改的內容等信息;SVNManager.getFile方法可以從Subversion倉庫中取得領域對象指定版本的所有內容。
public List getRevisionLog(BaseTrackingObject
obj, long startRevision,
long endRevision) {
.....
if(endRevision==-1) {
//obtain the latest revision from
//the repository
endRevision =
theRepository.getLatestRevision();
}
//retrieve the various log entries for
//the particular object path
Collection revisionLogs = theRepository.
log(new String[] {path}, null,
startRevision, endRevision,
false, true);
....
//Obtain the contents of the object at
//the specified revision
theRepository.getFile(path, revisionInfo.
getRevisionNumber(),
new Properties(),
xmlFileContentsStream);
....
}
檢索版本間的差異
SVNManager.showDifferences方法用來檢測兩個版本之間的差異,是通過調用JavaSVN 的SVNDiffManager類來去的差異,也可以通過SVNClientManager來引用並執行這個類,SVNDiffManager的doDiff方法有一個默認的實現,可以通過參數指定輸出流參數的形式取得固定格式的差異結果,我們也可以使用ISVNEditor來實現一個自己的差異比較方法。在這個例子裡,我們使用默認的實現。
public String showDifferences(
BaseTrackingObject obj,long revision1,
long revision2) {
....
//Create an instance of SVNClientManager
//providing authentication
SVNClientManager ourClientManager =
SVNClientManager.newInstance(
options, "name", "password");
//Obtain the handle to DiffClient
//from the client manager
SVNDiffClient theDiff = ourClientManager
.getDiffClient();
....
theDiff.doDiff(svnUrl, SVNRevision.
create(revision1), svnUrl,
SVNRevision.create(revision2),
false, false, diffStream);
....
}
結論
在企業級應用裡,不但要完成數據的存儲和檢索,還要實現對數據變化歷史的跟蹤。傳統方法是使用關系數據庫來完成這個工作,但是這不是一個“優雅”的方案。在我們的貸款數據處理的例子裡,Subversion提供了跟蹤數據變化的支持。JavaSVN的API用來完成數據存儲、檢索、獲取版本間差異和日志等任務。
我們的例子只是一個簡單的性能演示,Subversion提供了豐富的功能支持,完全可以應用於企業級應用。祝你探索地更開心!