導入本體到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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM