关系数据库依旧是存储和检索业务数据的仓库,它们擅长于管理数据。以上列举的缺点仅限于用关系数据模型存储多个不同的版本的数据并进行历史数据跟踪的情况下。
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提供了丰富的功能支持,完全可以应用于企业级应用。祝你探索地更开心!
资源