Java 开发人员是幸运的,因为在 Jena 中他们可以利用一种良好的 RDF 框架(请参阅 参考资料)。Jena 提供了一个编写和读取 RDF 的 API,它可以以多种方式进行保存和持久化。如果您对 Jena 还不熟悉,我强烈建议您在阅读本文之前阅读 Philip McCarthy 撰写的 “Jena 简介”。
Jena 的设计目标是可以良好地处理 RDF 数据模型,正如 JDBC 适合处理关系模型一样。数据库应用程序中编写的大量代码都用来保存 Java 对象,还有一些代码用来从数据库中聚集对象。用 Java 代码编写的语义 Web 应用程序面临一个类似的问题:它们必须实现 Java 对象和 RDF 之间的相互转换。因此,开发人员必须编写大量的代码来消除自身模型(一般为 JavaBeans)和 Jena 的以 RDF 为中心的 API 之间的差异。
![]() |
|
本文展示 Jenabean 的 Java-to-RDF 绑定框架(请参阅 参考资料)如何简化上述过程并减少所需的代码量。您将审视一些 Jena 客户端代码并将其与 Jenabean 的基于 JavaBean 的编程模型对比。首先查看一个简单的例子,我将向您展示如何实现以下操作:
- 将一个 bean 保存为 RDF
- 将其属性与特定的 RDF 属性绑定
- 将其与其他对象关联
- 再次回读 bean
考虑清单 1 中的简单 RDF 示例,为方便阅读,它使用了 N-triple(N3)格式(请参阅 参考资料):
清单 1. RDF 示例(N3 格式) <http://www.ibm.com/developerworks/xml/library/j-jena/> a dc:Article ; dc:creator "Philip McCarthy"^^xsd:string ; dc:subject "jena, rdf, java, semantic web"^^xsd:string ; dc:title "Introduction to Jena"^^xsd:string . |
清单 1 说明了 “Jena 简介” 这篇文章由 Philip McCarthy 撰写并且主题包括 jena、rdf、java 和语义 web。词汇表是 Dublin Core 元数据分类的一部分(请参阅 参考资料)。要使用 Jena 的原始的 Java API 复制这些 RDF 声明,您可能要执行类似清单 2 的工作:
清单 2. 使用原始的 Jena API 断言 RDF 示例 String NS = "http://purl.org/dc/elements/1.1/"; OntModel m = createModel(); OntClass articleCls = m.createClass(NS +"Article"); Individual i = articleCls.createIndividual( "http://www.ibm.com/developerworks/xml/library/j-jena/"); Property title = m.getProperty(NS + "title"); Literal l = m.createTypedLiteral("Introduction to Jena"); i.setPropertyValue(title,l); Property creator = m.getProperty(NS + "creator"); l = m.createTypedLiteral("Philip McCarthy"); i.setPropertyValue(creator,l); Property subject = m.getProperty(NS + "subject"); l = m.createTypedLiteral("jena, rdf, java, semantic web"); i.setPropertyValue(subject,l); m.write(System.out, "N3");清单 2 中每个数值断言需要三行代码:
- 访问属性
- 创建典型的字母
- 断言属性关系
这个代码的优点是使用透明、清晰的方式直接映射到 RDF 概念。这种情况与 JDBC 客户端代码相似,其中 API 直接应用到关系模型。
如果花大量时间研究 Jena,您将会认识到普通的面向对象代码和与 Jena API 交互的客户端代码之间存在差异。您将在 Java 对象设计的方法中进行断言,而不是使用 setPropertyValue 方法设置属性。
清单 3 展示了使用 Jenabean 创建相同的断言:
清单 3. 使用 Jenabean 创建相同的断言 Model m = _ Bean2RDF writer = new Bean2RDF(m); Article article = new Article("http://www.ibm.com/developerworks/xml/library/j-jena/"); article.setCreator("Philip McCarthy"); article.setTitle("Introduction to Jena"); article.setSubject("jena, rdf, java, semantic web"); writer.save(article); m.write(System.out, "N3"); |
我在这里另外包含了 m.write(...,"N3")
行。您可以使用本文附带的代码(请参阅 下载)亲自尝试。
清单 2 和 清单 3 有明显的不同,但 Jena 模型产生了几乎相同的变化。它们都创建了 清单 1 中三元组的一个超集。清单 2 通过调用 m.createClass(...)
声明了一篇新的文章。清单 3 通过创建 Article
— new Article(...)
类的一个新实例实现同样的操作。没有使用 Jenabean,每个属性断言要求您从模型访问属性、创建一个字母并通过调用 setPropertyValue(...)
将声明断言到模型。使用 Jenabean 的 Bean2RDF
转换器,您可以仅调用 JavaBean 的 setter 方法。清单 3 产生了对大多数 Java 开发人员来说非常熟悉的代码。
如您所见,Jenabean 的主要优势是填补了 Java 对象和 RDF 之间的差异。这允许您使用与域模型相同的 bean 以熟悉的 OOP 风格编写语义 Web 应用程序。但这并不是说 Java 对象和 RDF 完全相同。Java 对象和 RDF 以不同的方式表示数据。一个好的绑定工具必须解决以下三个问题。
通常开发人员只关心将一个本体(ontology)复制为一组 Java 对象。Java 对象和 OWL 类之间不存在一对一的关联。OWL 允许多重继承,允许许多类分享相同的属性并允许属性之间相互继承。另外,像关系数据库管理系统(RDBMS)一样,对象和 RDF 在一些方面相似而在其他方面不同。这使我们需要在 Java 代码中生成并使用 RDF。许多工具采用代码生成方法,这些方法涉及读取 RDF 模式或 OWL 本体并在 Java 语言中复制类和属性。这类工具不能实现从现有的 JavaBean 类中断言普通的 RDF 属性。RDF 绑定工具能让您将一个 JavaBean 属性与一个 RDF 属性 URI 随意关联(假设它们的类型兼容)。
任何绑定工具都需要解决的另一个问题是一个对象图(object graph)能够实现多大程度的持久化。将对象保存在一个密集的、具有大量连接的对象图中将导致持久化整个模型。装载也会出现类似的情形。绑定工具必须阻止将完整的 RDF 模型作为 JavaBeans 装载到内存中,但在必要时允许实现这个特性。
循环关系对于 RDF 和对象模型非常常见。在进行持久化和装载时,绑定工具必须能够检测和处理循环。显然,它应该防止无限循环,而且它应该检测以前装载的对象并通过重用这些对象来节省时间。
Jenabean 解决了这三个问题,同时尽量使该过程保持简单。它不需要代码生成阶段、字节码生成步骤,或者实时的字节码处理库。它仅需要 Jena 和它的库。Jenabean 默认使用一种保守策略来保存对象及其直接属性。开发人员必须表明他们希望实现 “深度” 拷贝,以便完整地保存对象及相关内容。或者使用另一种方法,即指定要保存或装载某个实例的特定集合属性。
![]() |
|
如果您熟悉 Hibernate 或其他绑定工具,您也许希望知道在哪里发生这些操作。 Jenabean 使用 Java 注释来声明 bean 如何映射到 RDF。正如其他基于注释的绑定层一样,当您的模型由 Java 对象驱动时,Jenabean 最为有用。当然并不会总出现这种情况,但是如果是这样的话,Jenabean 就可以提供帮助。
Jenabean 提供了许多功能来定制 bean 如何序列化为 RDF,但是如果默认设置符合您的要求,那么就可以开始快速编写和读取 bean。让我们创建一个简单的 JavaBean 示例,使它满足所有必需的要求。正如使用 Java Persistence API (JPA) 或 Hibernate 一样,您需要保证对象有惟一的 ID。Jenabean 需要您将一个单独的注释 — @Id
— 添加到至少一个 bean 字段,使用它充当惟一标识符。清单 4 展示了这个简单的 bean:
package example;
import thewebsemantic.Id;
public class Person {
private String email;
@Id
public String getEmail() { return email;}
public void setEmail(String email) { this.email = email;}
}
|
清单 4 为 Jenabean 提供足够的信息来可靠地保存和装载 Person
类实例。您没有必要扩展任何内容或编写 XML 描述符文件。由于电子邮件地址是惟一的,它是有效的 ID。清单 5 展示如何将 Person
实例保存到 Jena 模型:
清单 5. 使用生成的 RDF 保存 Person
类的实例
Model m = ModelFactory.createOntologyModel();
Bean2RDF writer = new Bean2RDF(m);
Person p = new Person();
p.setEmail("person@example.com");
writer.save(p);
m.write(System.out, "N3");
...
<http://example/Person>
a owl:Class ;
<http://thewebsemantic.com/javaclass>
"example.Person" .
<http://example/Person/taylor_cowan@yahoo.com>
a <http://example/Person> ;
<http://example/email>
"taylor_cowan@yahoo.com"^^xsd:string .
|
Bean2RDF
是一个将对象作为 RDF 编写的 Jenabean 类。它默认情况下是浅(shallow)模式,这意味着它将保存实例和其单一属性。如果 Person
类还没有添加到模型中,它将断言一个新类作为 owl:Class
的实例。注意在清单 5 中 Jenabean 使用example
包作为一个新的本体类的名称空间。第二个断言是一个注释,指明用于创建个体的 Java 类。Person
实例及电子邮件地址都进行了断言。Jenabean 首先为已保存的实例创建 URI。它还处理电子邮件属性并将其断言为一个 string
字母值。
![]() |
|
使用 RDF 表示的个体需要一个 URI,然而 Java 开发人员倾向于使用惟一的 ID。Jenabean 通过将声明的 ID 字段附加到名称空间(这种情况下默认来自包和类名)来提供帮助。创建好 URI 后,您可以使用 RDF2Bean
从模型中装载信息:
RDF2Bean reader = new RDF2Bean(m); Person p2 = reader.load( Person.class,"person@example.com"); |
Jenabean 也可以装载所有的 Person
实例:
Collection<Person> people = reader.load(Person.class); |
这些是在模型中访问 bean 的最简单方法。Jenabean 还支持到 SPARQL(RDF 的 SPARQL 查询语言)结果的绑定。简言之,Jenabean 至少要求 bean 作者指明哪个字段保存的值对于该类型的所有实例是惟一的。保存了 bean 后,将根据类的包和名称为 bean 的类和属性提供默认的 URI。这允许您开始从 Java 层轻松地快速创建 RDF。
到目前为止,我已经向您展示了 Jenabean 如何根据 bean 的类路径和名称创建默认的 URI。Jenabean 还支持声明您希望使用的名称空间。您可以使用 @Namespace
注释将 bean 映射到特定的名称空间。作为演示,我将使用一个目前未被使用的名称空间,Jenabean 自己的项目 URL:
@Namespace("http://jenabean.googlecode.com/")
public class Person { _
<http://jenabean.googlecode.com/Person/person@example.com>
a <http://jenabean.googlecode.com/Person> ;
|
注意,这里为 Person
类及其属性(而不是默认包)提供了新的名称空间,该名称空间与作为 @Namespace
注释的参数提供的名称空间匹配。默认情况下,这个名称空间将会用于类及其属性。
在 RDF 的世界中,可以使用常见的属性;否则,无法开发出语义 Web。通过使用常见的属性,您可以使数据具有更多的语义并使其他人更加熟悉数据。如果一个 spider(Web 爬行变体)遇到我从 Jenabean 项目 URL 名称空间中生成的 RDF 片段,则无法利用这个片段。但您可以使用更常见和熟知的断言对 bean 进行简单地修改。FOAF(Friend of a Friend,FOAF)语言(请参阅参考资料)是一种用于链接各类人员的常见词汇表,它为电子邮件地址提供了一个特殊的属性:foaf:mbox
。现在您所需做的全部工作是使用 Person
bean 中的 @RdfProperty
注释:
@Id
@RdfProperty("http://xmlns.com/foaf/0.1/mbox")
public String getEmail() { return email;}
<http://xmlns.com/foaf/0.1/mbox>
"person@example.com"^^xsd:string .
|
现在,email
属性将其自身断言为一个 foaf:mbox
,它将被其他对您的数据感兴趣的 RDF 代理理解为一个电子邮件地址。
在 OWL 和 RDF 世界中,通过对同一属性的多个断言来表达各种基数的关系。Jenabean 通过使用 java.util.Collection
接口极大简化了这一过程。清单 6 扩展 Person
类来支持朋友关系(使用松散的 foaf:knows
方式):
public Collection<Person> friends = new
LinkedList<Person>();
@RdfProperty("http://xmlns.com/foaf/0.1/knows")
public Collection<Person> getFriends() { return friends;}
public void setFriends(Collection<Person> friends) { this.friends = friends;}
|
这没有什么值得惊奇的 — 只是使用类型为 Collection<Person>
的新字段 friends
,以及相关的 get 和 set 方法。现在您可以创建一个 Person
和多个 friends
,并通过将每个朋友添加到 friends
集合来实现关联。@RdfProperty
注释是可选的,但是如果您希望将其绑定到现有的第三方词汇表,那么它非常重要。注释指定您希望在 Jena 模型中将 “friend” 属性绑定到 foaf:knows
RDF 属性。清单 7 显示如何使用传统的 JavaBean 技巧创建朋友关系:
Model m = ModelFactory.createOntologyModel();
Bean2RDF writer = new Bean2RDF(m);
Person p = new Person();
p.setEmail("person@example.com");
Person f1 = new Person();
f1.setEmail("friend1@example.com");
Person f2 = new Person();
f2.setEmail("friend2@example.com");
p.getFriends().add(f1);
p.getFriends().add(f2);
writer.save(p); // modifies the Jena model
m.write(System.out, "N3");
...
foaf:knows
jb:friend2@example.com, jb@friend1@example.com .
|
通过 Bean2RDF.write(...)
保存了简单的 Person
bean 后,模型中包含遵守 FOAF 规范的新数据。
在 RDF 中,没有定义子节点的顺序,所以不能假设 Jena 会以任何特定的顺序检索朋友列表。如果需要使用有序列表,Jenabean 将把 Java 数组映射到 RDF 序列。作为演示,我将为 Person
类提供一组电话号码,表示为一个 String
数组,如清单 8 所示:
private String[] phoneNumbers; public String[] getPhoneNumbers() { return phoneNumbers;} public void setPhoneNumbers(String[] phoneNumbers) { this.phoneNumbers = phoneNumbers;} jb:phoneNumbers [ a rdf:Seq ; rdf:_1 "321-321-3210"^^xsd:string ; rdf:_2 "321-321-3211"^^xsd:string ] ; |
Jenabean 帮助您开始使用熟悉的 Jenabean 模型读取和编写 RDF。本文阐述了以下基本原理:如何创建、保存并读取简单的 bean。这只是使用 Java 语言编写语义 Web 应用程序过程中的一小部分。访问 Jenabean 项目的主页了解更多相关知识、提供反馈或者寻求帮助(请参阅 参考资料)。