导入本体到Jena TDB数据库

      本体的存储方法或称本体持久化,大致分为基于内存的方式、基于文件的方式、基于数据库的方式和专门的管理工具方式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
     
  1  package cn.geodata.ont.tdb;
  2  
  3  import java.util.ArrayList;
  4  import java.util.Iterator;
  5  import java.util.List;
  6  
  7  import org.apache.commons.lang3.StringUtils;
  8  import org.apache.jena.riot.RDFDataMgr;
  9  import org.slf4j.Logger;
 10  import org.slf4j.LoggerFactory;
 11  
 12  import cn.geodata.ont.file.OntFile;
 13  
 14  import com.hp.hpl.jena.ontology.OntModel;
 15  import com.hp.hpl.jena.query.Dataset;
 16  import com.hp.hpl.jena.query.ReadWrite;
 17  import com.hp.hpl.jena.rdf.model.Model;
 18  import com.hp.hpl.jena.rdf.model.ModelFactory;
 19  import com.hp.hpl.jena.tdb.TDBFactory;
 20  import com.hp.hpl.jena.tdb.base.file.Location;
 21  
 22  /**
 23   * @TODO TDB CRUD操作,包含事务
 24   * @author Zhiwei HOU
 25   * @date 2015年12月2日
 26   */
 27  public class TDBPortal
 28  {
 29      final static Logger logger = LoggerFactory.getLogger(TDBPortal.class);
 30  
 31      // 必须close
 32      Dataset ds = null;
 33     
 34      /**
 35       * 连接TDB
 36       * 
 37       * @param tdbPath TDB目录或配置文件tdb-assembler.ttl路径. tdbPath可以通过配置文件进行设置
 38       * @param useAssemblerFile 是否使用配置文件连接
 39       */
 40      public TDBPortal(String tdbPath, boolean useAssemblerFile)
 41      {
 42          if (!useAssemblerFile)
 43          {
 44              Location location = Location.create(tdbPath);
 45              ds = TDBFactory.createDataset(location);
 46          }
 47          else
 48              ds = TDBFactory.assembleDataset(tdbPath);
 49      }
 50  
 51      public TDBPortal(String tdbPath)
 52      {
 53          Location location = Location.create(tdbPath);
 54          ds = TDBFactory.createDataset(location);
 55      }
 56  
 57      /**
 58      * 往模型中添加内容。不载入引用本体
 59      * 
 60      * @param modelUri 本体的uri
 61      * @param sourcePath  本体文件实际地址
 62      * @param override 是否覆盖
 63      * @return
 64      * @Houzw at 2016年4月1日下午11:36:13
 65      */
 66      public int loadModel(String modelUri, String sourcePath, Boolean isOverride)
 67      {
 68          Model model = null;
 69          ds.begin(ReadWrite.WRITE);
 70          try
 71          {
 72              if (ds.containsNamedModel(modelUri))
 73              {
 74                  if (isOverride)// 覆盖
 75                  {
 76                      removeModel(modelUri);//只是移除地址,实际数据不会移除
 77                      loadModel(modelUri, sourcePath, false);
 78                  }
 79              }
 80              else
 81              {
 82                  model = ds.getNamedModel(modelUri);// 没有则创建一个,model不会为null
 83                  model.begin();
 84                  RDFDataMgr.read(model, sourcePath);
 85                  model.commit();
 86              }
 87              // 已有,但是不覆盖,则直接返回
 88              ds.commit();
 89              logger.info("本体模型数据已经导入");
 90              return 1;
 91          }
 92          catch (Exception e)
 93          {
 94              return 0;
 95          }
 96          finally
 97          {
 98              if (model != null)
 99                  model.close();
100              ds.end();
101          }
102      }
103  
104      /**
105      * 导入本体。OntModel不支持事务。同时载入引用本体
106      * 
107      * @param modelUri 模型uri
108      * @param sourcePath 本体文件(集成文件)地址
109      * @param override 是否覆盖
110      * @return
111      * @Houzw at 2016年4月1日下午11:36:09
112      */
113      public int loadOntModel(String modelUri, String sourcePath, Boolean isOverride)
114      {
115          OntModel model = ModelFactory.createOntologyModel();// 不支持事务
116          ds.begin(ReadWrite.WRITE);
117          try
118          {
119              if (ds.containsNamedModel(modelUri))
120              {
121                  if (isOverride)// 覆盖
122                  {
123                      removeModel(modelUri);
124                      loadOntModel(modelUri, sourcePath, false);
125                  }
126              }
127              else
128              {
129                  model = OntFile.loadOntModelWithLocMapper(sourcePath);//导入本体文件
130                  ds.addNamedModel(modelUri, model);
131  
132              }
133              // 已有,但是不覆盖,则直接返回
134              ds.commit();
135              System.out.println(modelUri + " 已导入");
136              logger.info(modelUri + " 已导入");
137              return 1;
138          }
139          catch (Exception e)
140          {
141              System.out.println(e.getLocalizedMessage());
142              logger.error(e.getLocalizedMessage());
143              return 0;
144          }
145          finally
146          {
147              ds.end();
148          }
149      }
150  
151     
152      public Model getDefaultModel()
153      {
154          ds.begin(ReadWrite.READ);
155          Model model;
156          try
157          {
158              model = ds.getDefaultModel();
159              ds.commit();
160          }
161          finally
162          {
163              ds.end();
164          }
165          return model;
166      }
167  
168      /**
169       * 获取指定模型
170       */
171      public Model getModel(String modelUri)
172      {
173          Model model = null;
174          ds.begin(ReadWrite.READ);
175          try
176          {
177              model = ds.getNamedModel(modelUri);
178          }
179          finally
180          {
181              ds.end();
182          }
183          return model;
184      }
185  
186      
187      public void loadDefaultModel(String sourcePath)
188      {
189          Model model = null;
190          ds.begin(ReadWrite.WRITE);
191          try
192          {
193              model = ds.getDefaultModel();
194              model.begin();
195              if (!StringUtils.isBlank(sourcePath))
196                  RDFDataMgr.read(model, sourcePath);
197              model.commit();
198              ds.commit();
199          }
200          finally
201          {
202              if (model != null)
203                  model.close();
204              ds.end();
205          }
206      }
207  
208      
209      public void removeModel(String modelUri)
210      {
211          if (!ds.isInTransaction())
212              ds.begin(ReadWrite.WRITE);
213          try
214          {
215              ds.removeNamedModel(modelUri);
216              ds.commit();
217              System.out.println(modelUri + " 已被移除");
218              logger.info(modelUri + " 已被移除");
219          }
220          finally
221          {
222              ds.end();
223          }
224      }
225  
226      /**
227       * 列出所有模型的uri
228       */
229      public List<String> listModels()
230      {
231          ds.begin(ReadWrite.READ);
232          List<String> uriList = new ArrayList<>();
233          try
234          {
235              Iterator<String> names = ds.listNames();// DefaultModel没有name
236              String name = null;
237              while (names.hasNext())
238              {
239                  name = names.next();
240                  uriList.add(name);
241              }
242          }
243          finally
244          {
245              ds.end();
246          }
247          return uriList;
248      }
249  
250      /**
251       * 必须关闭TDB连接
252       */
253      
254      public void close()
255      {
256          ds.close();
257      }
258  }

 以上简单介绍了基于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/

posted @ 2016-05-25 10:24  yes_V_can  阅读(6718)  评论(4编辑  收藏  举报