持久化(Persistence)
持久化框架的概述
EMF持久化API
XML资源
EMF针对基于XML资源的基础接口,XMLResource,扩展Resource加入了数个与持久化形式相关的方法。基础实现,XMLResourceImpl,可以用来将任一EMF 序列化为XML。它产生和消耗(consume)的XML格式,以及它的性能特征,可以有很多种方式来控制。
首先讨论这个实现提供的默认序列化格式,然后是其支持的多个保存和加载的选项,以及元数据如何被用来自定义(customize)序列化格式,最后,讨论这个实现的一些其他特征,包括其支持的默认选项,外在的ID和DOM转换。
默认序列化格式
为了说明XMLResourceImpl的默认输出格式,我们先创建一个对象并保存,如下:
USAddress address = EPO2Factory.eINSTANCE.createUSAddress();
URI uri = URI.createPlatformResourceURI("/project/out.xml", true);
Resource resource = new XMLResourceImpl(uri);
resource.getContents().add(address);
resource.save(null);
创建USAddress类的实例,将其加入到XML资源的内容中,然后保存资源。注意此处是显式地(explicitly)实例化XMLResourceImpl,仅仅是为了说明。在练习时,你应该总是通过ResourceSet接口创建资源。
out.xml中生成的序列化,如下:
<?xml version="1.0" encoding="ASCII"?>
<epo2:USAddress xmlns:epo2="http://www.example.com/epo2.ecore"/>
首先,在XML声明中指定的编码是ASCII。这是默认的,但并不是特别好的选择。最常使用的是Unicode编码比如UTF-8。
接着看XML声明,使用命名空间,将单个的USAddress序列化成为一个元素。命名空间的前缀(”epo2“)和相应的URI(”http://www.example.com/epo2.ecore“)来自于模型中EPackage的nsPrefix和nsURI属性。
属性和元素的内容依赖于对应EMF对象的属性和引用。可以回顾USAddress定义的四种简单属性:street, city, state和zip。也许会疑惑为什么它们没有出现在序列化中,这是因为它们之前没有被设置(set)。默认情况下,XMLResourceImpl 序列化器(serializer)仅仅包括被设置的属性和引用(例如反射式的EObject.eIsSet()方法返回为真时的)。这种手段通过只包括在加载时需要用来还原(restore)对象状态(state)的最少数量的信息,最小化了XML文件。
反序列化
XMLResourceImpl加载器使用SAX(the Simple API for XML)来提供加载支持。当文档被解析时,SAX事件被用来在内存中建立 EMF 对象。与之对比的是,DOM(the Document Object Model)会构建一个中介(intermediary)模型,消耗更多的内存以及更长的时间来运行。
在加载过程中,命名空间URIs从声明中读取然后被用来定位对应的Ecore包。这经常涉及到查询包注册,如果加载的资源在资源集合中就是一个重点内容注册,否则就是全局注册。然而,当没有被注册的匹配的包时,加载器自动地使用命名空间URI来将一个资源加载到资源集合中。通常而言,仅仅试图从URI直接加载,如果提供包含EPackage实例的EMF资源,则局部地注册包。这是因为序列化的Ecore模型就是另一个模型,Ecore 元模型的实例,所以才能像其他EMF数据一样被加载。
(未完待续)
选项
动态EMF
扩展的元数据
其他特征
EMF资源和资源工厂实现
EMF提供数个基于XML(XML-based)的资源和资源工厂实现。资源负责持久化它包含的对象,资源工厂被用来创建和配置资源的一种类型。根本上,是资源工厂决定了为其注册的URI类型生成的和使用的持久化格式。
基础XML
泛型XML
XMI
XMIResourceImpl继承XMLResourceImpl以支持XML Metadata Interchange (XMI) 2.0 标准。EMF将模型映射到“普通的”XML序列化的方式深受XMI启发,所以两者之间没有大的区别。
<?xml version="1.0" encoding="ASCII"?>
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
xmlns:epo2="http://www.example.com/epo2.ecore">
<epo2:PurchaseOrder xmi:id="_xNSb8KfZEdm0dNl1iq3EdQ"
comment="rush order">
<items xmi:id="_xO0F8KfZEdm0dNl1iq3EdQ" productName="apple"/>
</epo2:PurchaseOrder>
<epo2:PurchaseOrder xmi:id="_xO0F8afZEdm0dNl1iq3EdQ"
comment="special promotion"
previousOrder="_xNSb8KfZEdm0dNl1iq3EdQ">
<items xmi:id="_xO0F8qfZEdm0dNl1iq3EdQ" productName="orange"/>
</epo2:PurchaseOrder>
</xmi:XMI>
注意到根元素有额外的命名空间声明,介绍了XMI的命名空间,同来xmi前缀来标识。XMI序列化中的根元素总是包含一个版本属性,来标识使用的XMI的版本。
也许最引人注目的特点是根(root)中额外的xmi:XMI元素。这个元素是XMIResourceImpl引进的,因为两个对象被直接添加到资源的内容列表中。格式良好(well-formed)*XML文档必须有且仅有一个根元素,所以XMI定义了这个元素来处理需要表示多个顶级对象的情况。我们从没有使用普通的***XMLResourceImpl实现这种事情,原因如下:它只是简单地持久化了资源里的第一个对象,而忽视了剩下的对象。
注意到这个例子中,使用了自动生成的UUIDs。我们使其生效,通过上一章中其他特征建议的方法。注册了资源工厂,其创建了XMLResourceImpl简单子类的实例,其中重写了useUUIDs() 使其返回为真:
ResourceSet rs = new ResourceSetImpl();
rs.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
Resource.Factory.Registry.DEFAULT_EXTENSION,
new ResourceFactoryImpl()
{
public Resource createResource(URI uri)
{
return new XMIResourceImpl(uri)
{
protected boolean useUUIDs() { return true; }
};
}
});
在XML中使用UUIDs相当普遍,尽管规范中没有要求。EMF的基础XMLResource实现不使用它们,因为默认的基于索引(index-based)的段路径(fragment paths)通常更高效。
XMI资源有个接口,XMLResource,它定义一个额外的选项关键字(option key):OPYION_USE_XMI_TYPE取的是布尔值,仅用于save()。如果它是有效的,xmi:type属性将被用来标识引用的类型,而不是xsi:type。
当在Eclipse中运行时,XMIResourceFactoryImpl被注册为全局注册中的默认资源工厂。换句话说,XMI是默认的持久化格式,当没有其他针对特定URI模式(scheme)或者文件扩展名(file extension)的持久化格式时,使用XMI。
Ecore
EcoreResourceFactoryImpl是创建普通XMIResourceImpl实例的简单资源工厂实现,然后设置编码为UTF-8并使某些默认的XMIResourceImpl生效。特别地,编码属性类型(encoded attributes style)用于引用,行宽度设置为80。