赞
踩
使用Jena-TDB存储RDF本体、知识图谱文件
by 龙前尘
实验环境:win8、Java 1.8、Jena/Jena-TDB 3.0.1
转载请注明地址:
http://blog.csdn.net/svenhuayuncheng/article/details/78751300
RDF是目前通用的本体或知识图谱存储格式,其使用业内统一标准的RDF、RDFS规范,进行知识的组织。
固然,可以将RDF简单地固化到.nt文件,甚至可以直接存储到.txt文件。但若对于RDF有CRUD操作,那么简单地使用文档,是事倍功半的。
为了方便用户管理本体或知识图谱,将RDF固化到一个通用的数据库中,才是最优做法。
将RDF固化,有多种方法,例如基于图数据库的:AllegroGraph1、Neo4j2;或者基于存储固定结构的数据库:关系型数据库、NoSQL数据库;或者基于索引的ES、基于缓存的Redis等。
作为Java中本体文件的管理编辑工具,Jena自然也提供了固化三元组的工具,并且共支持三种模式:RDB、SDB、TDB。
其中,由于速度较慢,RDB(关系数据库)已逐渐式微;SDB是使用 SQL 数据库存储和查询 RDF 数据的模块;TDB是使用triple store的形式对RDF数据提供持久性存储(persistent store)。根据官方文档所述,相比较于SDB,TDB更快、更具扩展性,并且官方对于TDB的支持力度更大3。在Jena官网上,对于SDB和RDB的documentation也不多,而TDB的文档相对详尽。因此,本文选择TDB来做实验,尝试对RDF三元组进行固化和查询操作。
TDB,是Jena用于RDF存储和查询的模块,并且支持所有Jena API。在单机上,TDB可被用于高性能的RDF存储,并且可以使用命令行或者Java API的形式来操作TDB模块。此外,TDB采用了事务(是的,类似于关系型数据库的transaction)来封装数据库操作,防止进程意外终止、系统崩溃等特殊情况带来的数据损坏。同一时间,一个TDB数据集只能被一个JVM虚拟机进程访问,否则会造成数据破坏(脏数据)4。
如果想要多个进程同时访问TDB数据集,需要使用提供SPARQL服务的Fuseki5模块来操作。Fuseki提供SPARQL协议来查询、升级、以及通过HTTP的REST 服务来升级TDB数据。
TDB存储的本体数据集由node表、Triple和Quad索引、prefixes表组成,存放在指定的文件系统目录下。TDB采用B+树维护三种基本形式的Triple索引:SPO、POS和OSP(S、P、O分别代表Subject、Predicate和Object)。若存在命名图(Named Graph),则同时维护相应的Quad索引(G表示Graph):GOSP、SPOG、GSPO、OSPG、GPOS和POSG6。
在实际操作中,以下几点非常重要。
简单地说,操作过程为:新建Dataset对象,将RDF文件的三元组读取到Model中,再将Model对象固化到TDB文件,从而完成数据持久化。
<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-core</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-tdb</artifactId>
<version>3.0.1</version>
</dependency>
TDB操作类
import com.qa.demo.systemController.FaqDemo;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.ReadWrite;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.tdb.TDBFactory;
import org.apache.jena.util.FileManager;
import org.nlpcn.commons.lang.util.logging.Log;
import org.nlpcn.commons.lang.util.logging.LogFactory;
import java.util.*;
/**
* Created time: 2017_11_09
* Author: Devin Hua
* Function description:
* To accomplish persistence by storing RDF txt file with TDB .
*/
public class TDBPersistence {
public static final Log LOG = LogFactory.getLog(FaqDemo.class);
public Dataset dataset = null;
/**
* 建立TDB数据文件夹;
*/
public TDBPersistence(String tdbName) {
dataset = TDBFactory.createDataset(tdbName);
}
/**
* 将rdf文件加载到model中;
*/
public void loadModel(String modelName, String rdfFilePath, Boolean isOverride) {
int result;
Model model = null;
dataset.begin(ReadWrite.WRITE);
try {
//已有同名model,且不需要使用新的三元组覆盖旧TDB文件;
if (dataset.containsNamedModel(modelName) && (!isOverride)) {
result = 1;
}
//没有同名model,或者有同名文件需要覆盖;
else {
if (dataset.containsNamedModel(modelName))
result = 2;
else
result = 3;
//移除已有的model;
dataset.removeNamedModel(modelName);
//建立一个新的TDB Model,一个TDB可以有多个model,类似数据库的多个表;
model = dataset.getNamedModel(modelName);
//事务开始;
model.begin();
//读取RDF文件到model中;
FileManager.get().readModel(model, rdfFilePath);
//将事务提交;
model.commit();
//务必记得将dataset的事务提交,否则无法完成增删改查操作;
dataset.commit();
}
} catch (Exception e) {
LOG.error(e.toString());
result = 0;
} finally {
if (model != null && !model.isEmpty())
model.close();
dataset.end();
}
switch (result) {
case 0:
LOG.error(modelName + ":读取model错误!");
break;
case 1:
LOG.info(modelName + ":已有该model,不需要覆盖!");
break;
case 2:
LOG.info(modelName + ":已有该model,覆盖原TDB文件,并建立新的model!");
break;
case 3:
LOG.info(modelName + ":建立新的TDB model!");
break;
}
}
/**
* 删除Dataset中的某个model;
*/
public void removeModel(String modelName) {
if (!dataset.isInTransaction())
dataset.begin(ReadWrite.WRITE);
try {
dataset.removeNamedModel(modelName);
dataset.commit();
LOG.info(modelName + ":已被移除!");
} finally {
dataset.end();
}
}
/**
* 关闭TDB连接;
*/
public void closeTDB() {
dataset.close();
}
/**
* 判断Dataset中是否存在model;
*/
public boolean findTDB(String modelName) {
boolean result;
dataset.begin(ReadWrite.READ);
try {
if (dataset.containsNamedModel(modelName))
result = true;
else
result = false;
} finally {
dataset.end();
}
return result;
}
/**
* 列出Dataset中所有model;
*/
public List<String> listModels() {
dataset.begin(ReadWrite.READ);
List<String> uriList = new ArrayList<>();
try {
Iterator<String> names = dataset.listNames();
String name;
while (names.hasNext()) {
name = names.next();
uriList.add(name);
}
} finally {
dataset.end();
}
return uriList;
}
/**
* 获得Dataset中某个model;
*/
public Model getModel(String modelName) {
Model model;
dataset.begin(ReadWrite.READ);
try {
model = dataset.getNamedModel(modelName);
} finally {
dataset.end();
}
return model;
}
/**
* 获取默认模型;
*/
public Model getDefaultModel() {
dataset.begin(ReadWrite.READ);
Model model;
try {
model = dataset.getDefaultModel();
dataset.commit();
} finally {
dataset.end();
}
return model;
}
}
单元测试类
import org.apache.jena.rdf.model.Model;
import org.junit.jupiter.api.Test;
import java.util.List;
class TDBPersistenceTest {
@Test
void listModels() {
//TDB的数据文件夹地址;
String TDBPath = "src\\main\\resources\\data\\kbfile\\TDB";
TDBPersistence tdbPersistence = new TDBPersistence(TDBPath);
List<String> models = tdbPersistence.listModels();
if (models == null || models.isEmpty() || models.size() == 0)
System.out.println("Dataset中不存在非默认model!");
else {
for (String model : models) {
System.out.println("model: " + model);
}
}
tdbPersistence.closeTDB();
}
@Test
void loadModel() {
//TDB的数据文件夹地址;
String TDBPath = "src\\main\\resources\\data\\kbfile\\TDB";
//在Dataset中存放model的名字;
String modelName = "TDB_agriculture";
//表示若有同名model,是否需要覆盖;
Boolean flag = true;
//rdf三元组文件的路径;
String rdfPathName = "src/main/resources/data/kbfile/NT_triplets.nt";
//建立对象;
TDBPersistence tdbPersistence = new TDBPersistence(TDBPath);
//新建model;
tdbPersistence.loadModel(modelName, rdfPathName, flag);
//事务完成后必须关闭Dataset;
tdbPersistence.closeTDB();
}
}
在实际操作中,可能会报错:
Code: 4/UNWISE_CHARACTER in PATH: The character matches no grammar rules of URIs/IRIs. These characters are permitted in RDF URI References, XML system identifiers, and XML Schema anyURIs.
遇到这种情况,一般是RDF文件地址写错。注意使用文件的相对路径时,可以参考上述示例代码来写。在代码中,笔者也有提及,大家特别留意。
此外,当完成写操作后,都必须执行model.commit()或dataset.commit(),来完成事务提交,否则TDB固化的数据不会update,也请大家注意。
以上,欢迎大家探讨、指正!
注:TDB操作代码部分参考于博客导入本体到Jena TDB数据库
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。