企業應用除 CRUD 外,常需追蹤數據變更全歷史:誰、何時、因何原因、如何修改。傳統方案使用「歷史表 + 觸發器/應用層寫入」存在缺陷:
Subversion (SVN):中心化版本控制系統,採用「複製-編輯-合並」模式,無需鎖定文件,支援 HTTP/HTTPS (WebDAV) 穿透防火牆,樹狀倉庫結構天然適合層次化業務數據。
JavaSVN:純 Java 實現的 SVN 客戶端庫,提供低層倉庫直接訪問 API 及高層工作副本操作 API,無需依賴本地命令行工具,跨平台部署極簡。
BaseTrackingObjectpublic abstract class BaseTrackingObject {
protected String modifiedBy; // 修改人
protected Date modifiedDate; // 修改時間
protected String modificationReason; // 修改原因
public abstract String getObjectId(); // 業務主鍵,作為 SVN 文件名
public abstract String getXmlRepresentation(); // 序列化為 XML 存入 SVN
}
LoanData
public class LoanData extends BaseTrackingObject {
private String loanId;
private double loanAmount;
private float loanRate;
private int loanTerm;
// getter/setter + getObjectId() 返回 loanId + getXmlRepresentation() 序列化
}
SVNManager.initRepository())
DAVRepositoryFactory.setup(); // 支援 HTTP/WebDAV
SVNURL svnUrl = SVNURL.parseURIEncoded("https://localhost/repos/");
SVNRepository repository = SVNRepositoryFactory.create(svnUrl);
ISVNAuthenticationManager auth = new BasicAuthenticationManager(user, pass);
repository.setAuthenticationManager(auth);
checkInObject)
public SVNResult checkInObject(BaseTrackingObject obj) {
String path = "DomainObjects/" + obj.getClass().getSimpleName() + "/" + obj.getObjectId() + ".xml";
ISVNEditor editor = repository.getCommitEditor(obj.getModificationReason(), null);
if (!repository.hasFile(path, -1)) editor.addFile(path, null, -1);
else editor.openFile(path, -1);
SVNDeltaGenerator deltaGen = new SVNDeltaGenerator();
deltaGen.sendDelta(path,
new ByteArrayInputStream(obj.getXmlRepresentation().getBytes()),
editor, true); // 僅傳輸差異,提升網絡效率
editor.closeEdit(); // 真正提交版本
}
getRevisionLog)
public List getRevisionLog(BaseTrackingObject obj, long startRev, long endRev) {
if (endRev == -1) endRev = repository.getLatestRevision();
Collection logs = repository.log(new String[]{path}, null, startRev, endRev, false, true);
// 遍歷 logs 獲取每版本修改人、日期、日誌信息
// 可通過 repository.getFile(path, rev, props, outputStream) 恢復指定版本完整內容
}
showDifferences)
SVNClientManager clientMgr = SVNClientManager.newInstance(options, user, pass);
SVNDiffClient diffClient = clientMgr.getDiffClient();
diffClient.doDiff(svnUrl, SVNRevision.create(rev1), svnUrl, SVNRevision.create(rev2),
false, false, diffOutputStream); // 輸出標準 unified diff 格式
/repos/DomainObjects/{EntityType}/{PK}.xml,清晰分離業務實體類型。AuthzSVNAccessFile 實現路徑級權限控制。svnadmin hotcopy + 增量導出 svnadmin dump --incremental,異地存儲。svnadmin pack 壓縮修訂文件。svn2git 保留完整歷史,但需注意 SVN 的中央化鎖定語義差異。將 Subversion 引入業務層,利用其成熟的版本控制內核解決「數據變更歷史追蹤」難題,是企業級應用架構演進的明智選擇。JavaSVN 純 Java 特性消除了平台依賴,配合「雙引擎」架構,讓關係數據庫專注當前狀態服務,SVN 專注歷史版本管理,職責清晰、技術棧穩健。