本體的存儲方法或稱本體持久化,大致分為基於內存的方式、基於文件的方式、基於數據庫的方式和專門的管理工具方式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查詢服務。
下面介紹TDBPortal1 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/