本体的存储方法或称本体持久化,大致分为基于内存的方式、基于文件的方式、基于数据库的方式和专门的管理工具方式4种(傅柱等, 2013)。其中,基于数据库的方式又有基于关系数据库、基于面向对象数据库、基于Native XML数据库和基于NoSQL的三元组数据库(Triple Store)4种主要方式。基于数据库的本体持久化方式充分利用了数据库的安全可靠(数据保密、数据完整性、并发控制、故障恢复等)、高效、易于管理并易于与应用系统集成等优点,是主要的本体持久化方式。

在本体中,数据被表示为一系列由主语(subject)、谓词(predicate)和宾语(object)组成的陈述(statement),即三元组(triple)的集合。基于关系数据库的本体持久化使用二维表对本体的三元组进行处理,不可避免地需要对本体中的复杂关系进行不自然的分解,而查询时又需要将基于图的查询转换为关系查询(傅柱等, 2013) ,需要进行大量的关联、连接操作,因而,现有基于关系数据库的本体存储方法都存在大规模存储、更新、修改和查询效率低、数据库操作代价大等问题(李勇和李跃龙, 2008)。因此,效率、性能更优越的专门或扩展了RDF存储、查询甚至推理能力的非关系型三元组数据库(Triple Store,或称图数据库),如GraphDB (OWLIM) [1]、Virtuoso Universal Server[2]、AllegroGraph[3]、Jena TDB(Triple DB)等(Rohloff et al., 2007)目前已逐渐成为本体存储主流工具。本文采用基于Jena 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和POSG。

如上图所示,本体应用程序首先通过URI(Uniform Resource Identifier)地址映射文件实现本体URI与其对应的本体模块文件存放的文件系统地址的映射,然后使用自定义的Java类TDBPortal通过程序或配置文件读取各本体模块文件持久化到TDB。TDB中数据可通过TDBPortal实现增删改查操作,和进一步应用于本体查询与推理等操作;或作为SPARQL(SPARQL Protocol and RDF Query Language)服务器Jena Fuseki的数据源,对外提供基于HTTP的SPARQL查询服务。

下面介绍TDBPortal

  package cn.geodata.ont.tdb;

  import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import org.apache.commons.lang3.StringUtils;
import org.apache.jena.riot.RDFDataMgr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import cn.geodata.ont.file.OntFile; import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.query.ReadWrite;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.tdb.TDBFactory;
import com.hp.hpl.jena.tdb.base.file.Location; /**
* @TODO TDB CRUD操作,包含事务
* @author Zhiwei HOU
* @date 2015年12月2日
*/
public class TDBPortal
{
final static Logger logger = LoggerFactory.getLogger(TDBPortal.class); // 必须close
Dataset ds = null; /**
* 连接TDB
*
* @param tdbPath TDB目录或配置文件tdb-assembler.ttl路径. tdbPath可以通过配置文件进行设置
* @param useAssemblerFile 是否使用配置文件连接
*/
public TDBPortal(String tdbPath, boolean useAssemblerFile)
{
if (!useAssemblerFile)
{
Location location = Location.create(tdbPath);
ds = TDBFactory.createDataset(location);
}
else
ds = TDBFactory.assembleDataset(tdbPath);
} public TDBPortal(String tdbPath)
{
Location location = Location.create(tdbPath);
ds = TDBFactory.createDataset(location);
} /**
* 往模型中添加内容。不载入引用本体
*
* @param modelUri 本体的uri
* @param sourcePath 本体文件实际地址
* @param override 是否覆盖
* @return
* @Houzw at 2016年4月1日下午11:36:13
*/
public int loadModel(String modelUri, String sourcePath, Boolean isOverride)
{
Model model = null;
ds.begin(ReadWrite.WRITE);
try
{
if (ds.containsNamedModel(modelUri))
{
if (isOverride)// 覆盖
{
removeModel(modelUri);//只是移除地址,实际数据不会移除
loadModel(modelUri, sourcePath, false);
}
}
else
{
model = ds.getNamedModel(modelUri);// 没有则创建一个,model不会为null
model.begin();
RDFDataMgr.read(model, sourcePath);
model.commit();
}
// 已有,但是不覆盖,则直接返回
ds.commit();
logger.info("本体模型数据已经导入");
return 1;
}
catch (Exception e)
{
return 0;
}
finally
{
if (model != null)
model.close();
ds.end();
}
} /**
* 导入本体。OntModel不支持事务。同时载入引用本体
*
* @param modelUri 模型uri
* @param sourcePath 本体文件(集成文件)地址
* @param override 是否覆盖
* @return
* @Houzw at 2016年4月1日下午11:36:09
*/
public int loadOntModel(String modelUri, String sourcePath, Boolean isOverride)
{
OntModel model = ModelFactory.createOntologyModel();// 不支持事务
ds.begin(ReadWrite.WRITE);
try
{
if (ds.containsNamedModel(modelUri))
{
if (isOverride)// 覆盖
{
removeModel(modelUri);
loadOntModel(modelUri, sourcePath, false);
}
}
else
{
model = OntFile.loadOntModelWithLocMapper(sourcePath);//导入本体文件
ds.addNamedModel(modelUri, model); }
// 已有,但是不覆盖,则直接返回
ds.commit();
System.out.println(modelUri + " 已导入");
logger.info(modelUri + " 已导入");
return 1;
}
catch (Exception e)
{
System.out.println(e.getLocalizedMessage());
logger.error(e.getLocalizedMessage());
return 0;
}
finally
{
ds.end();
}
} public Model getDefaultModel()
{
ds.begin(ReadWrite.READ);
Model model;
try
{
model = ds.getDefaultModel();
ds.commit();
}
finally
{
ds.end();
}
return model;
} /**
* 获取指定模型
*/
public Model getModel(String modelUri)
{
Model model = null;
ds.begin(ReadWrite.READ);
try
{
model = ds.getNamedModel(modelUri);
}
finally
{
ds.end();
}
return model;
} public void loadDefaultModel(String sourcePath)
{
Model model = null;
ds.begin(ReadWrite.WRITE);
try
{
model = ds.getDefaultModel();
model.begin();
if (!StringUtils.isBlank(sourcePath))
RDFDataMgr.read(model, sourcePath);
model.commit();
ds.commit();
}
finally
{
if (model != null)
model.close();
ds.end();
}
} public void removeModel(String modelUri)
{
if (!ds.isInTransaction())
ds.begin(ReadWrite.WRITE);
try
{
ds.removeNamedModel(modelUri);
ds.commit();
System.out.println(modelUri + " 已被移除");
logger.info(modelUri + " 已被移除");
}
finally
{
ds.end();
}
} /**
* 列出所有模型的uri
*/
public List<String> listModels()
{
ds.begin(ReadWrite.READ);
List<String> uriList = new ArrayList<>();
try
{
Iterator<String> names = ds.listNames();// DefaultModel没有name
String name = null;
while (names.hasNext())
{
name = names.next();
uriList.add(name);
}
}
finally
{
ds.end();
}
return uriList;
} /**
* 必须关闭TDB连接
*/ public void close()
{
ds.close();
}
}

以上简单介绍了基于Jena TDB的本体存储。目前我对AssemblerFile配置文件的配置还没有深入的研究,了解的朋友可以告诉我,O(∩_∩)O谢谢

——————————————————————————————————————

补充:

TDB 也提供了命令行的方式导入本体数据,具体参考官方文档或参考http://blog.csdn.net/rk2900/article/details/38342181

水平有限,错误难免,多指教。TDB 的具体内容可查阅其官方文档


[1] http://ontotext.com/products/graphdb/

[2] http://www.openlinksw.com/

[3] http://franz.com/

导入本体到Jena TDB数据库的更多相关文章

  1. Jena TDB 101 Java API without Assembler

    Update on 2015/05/12 ongoing tutorials site on https://github.com/zhoujiagen/semanticWebTutorialUsin ...

  2. Jena TDB 102

    1 Introduction TDB is a RDF storage of Jena. official guarantees and limitations TDB support full ra ...

  3. Jena TDB assembler syntax

    1 introduction Assembler is a DSL of Jena to specify something to build, models and dataset, for exa ...

  4. paip.导入数据英文音标到数据库mysql为空的问题之道解决原理

    paip.导入数据英文音标到数据库mysql为空的问题之道解决原理 #---原因:mysql 导入工具的bug #---解决:使用双引号不个音标括起来. 作者 老哇的爪子 Attilax 艾龙,  E ...

  5. 一、导入、导出远程Oracle数据库

    一.导入.导出远程Oracle数据库  其语法实示例如下:    imp/exp [username[/password[@service]]]   其中service是服务实例名,关于如何创建服务实 ...

  6. paip.将数据导入到在英语语音数据库mysql道路解决空原则问题

    paip.将数据导入到在英语语音数据库mysql道路解决空原则问题 #---原因:mysql 导入工具bug #---解决:不要使用双引号括注音. 笔者 老哇爪 Attilax 艾龙.  EMAIL: ...

  7. EXCEL导入GridView,然后再汇入数据库.

    原文:EXCEL导入GridView,然后再汇入数据库. 近日项目中有一个多笔料号要输入,我做了一个用javascript复制输入框的功能,可以输入多笔料号. 但是使用者反馈,料号太多,可能几百个料号 ...

  8. 【exp/imp】将US7ASCII字符集的dmp文件导入到ZHS16GBK字符集的数据库中

    [exp/imp]将US7ASCII字符集的dmp文件导入到ZHS16GBK字符集的数据库中 1.1  BLOG文档结构图 1.2  前言部分 1.2.1  导读和注意事项 各位技术爱好者,看完本文后 ...

  9. (转)如何将 Excel 文件导入到 Navicat for MySQL 数据库

    场景:工作中需要统计一段时间的加班时长,人工统计太过麻烦,就想到使用程序实现来统计 1 如何将 Excel 文件导入到 Navicat for MySQL 数据库 Navicat for MySQL  ...

随机推荐

  1. javascript进阶修炼之一——javascript必备操做

    动态选择方法及属性 使用方括号操作符,比点操作符功能更强大.因为可以在[ ]方括号中使用任何代表成员名称的内容访问对象.包括字面量,保存着成员名称的变量,名称组合,三元操作符.所有这些内容都会被处理成 ...

  2. HDU3047 Zjnu Stadium 【带权并查集】

    HDU3047 Zjnu Stadium Problem Description In 12th Zhejiang College Students Games 2007, there was a n ...

  3. BZOJ3141 Hnoi2013 游走 【概率DP】【高斯消元】*

    BZOJ3141 Hnoi2013 Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点 ...

  4. Spark学习体系整理(基础篇、中级篇、高级篇所涉及内容)

    新手刚开始学习比较迷茫,参考下面,然后找相关资料学习 1 Spark基础篇      1.1 Spark生态和安装部署          在安装过程中,理解其基本操作步骤.          安装部署 ...

  5. Mac根据端口号查询进程

    sudo lsof -i :8080

  6. k8s helm 私服chartmuseum minio s3 存储配置

    1. 安装minio 使用docker 安装 参考项目 https://github.com/rongfengliang/mino-thumbor-openresty 备注: 因为是一个集成项目可能会 ...

  7. worldpress自定义页面

    一:wordpress制作自定义页面的方法 有时候我们需要制作一些个性化的页面,而不是直接用wordpress的page页面模板.这时候我们就需要自已写一个页面出来.下面介绍一下制作流程: 第一步:制 ...

  8. I/O复用——select和poll

    概述 I/O多路复用(multiplexing)的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写 ...

  9. JAVA-Unit05: 视图、序列、索引 、 约束

    Unit05: 视图.序列.索引 . 约束 视图 数据库对象之一 视图在SQL语句中体现的角色与表相同, 但它并非一张真实存在的表,它对应的 是一个查询语句的结果集. 创建一个查看10号部门员工信息的 ...

  10. Linux和Windows的遍历目录下所有文件的方法对比

    首先两者读取所有文件的方法都是采用迭代的方式,首先用函数A的返回值判断目录下是否有文件,然后返回值合法则在循环中用函数B直到函数B的返回值不合法为止.最后用函数C释放资源. 1.打开目录 #inclu ...