使用Java Jena API读取现有的Apache Jena TDB数据库:数据集为空
我使用 tdbload 创建了一个Apache Jena的 TDB数据库,数据库中包含的语句如下:
tdbquery --loc=/path/to/TDB.rdfs --query=query.sparql
(... OK output rows.... )
现在,我想用下面的代码,通过Java API查询(只读)这个现有的数据集。
但看起来我的数据集中似乎没有命名图(listNames未输出任何内容),数据集/模型看起来是空的。
dir exists ? true
tdb2 ?true
empty ? true
size:0
Done
我哪里做错了?
注:在执行完我的Java程序后返回到 tdbquery,看起来好像锁没有被释放?
org.apache.jena.tdb1.base.file.FileException: Unable to check TDB lock owner, the lock file contents appear to be for a TDB2 database. Please try loading this location as a TDB2 database. See https://jena.apache.org/documentation/tdb/faqs.html for more information.
at org.apache.jena.tdb1.base.file.LocationLock.getOwner(LocationLock.java:114)
at org.apache.jena.tdb1.base.file.LocationLock.canObtain(LocationLock.java:143)
at org.apache.jena.tdb1.sys.StoreConnection._makeAndCache(StoreConnection.java:263)
at org.apache.jena.tdb1.sys.StoreConnection.make(StoreConnection.java:227)
at org.apache.jena.tdb1.sys.StoreConnection.make(StoreConnection.java:241)
at org.apache.jena.tdb1.transaction.DatasetGraphTransaction.<init>(DatasetGraphTransaction.java:76)
at org.apache.jena.tdb1.sys.TDBMaker.createDirect(TDBMaker.java:116)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1713)
at org.apache.jena.tdb1.sys.TDBMaker._create(TDBMaker.java:102)
(...)
我的Java代码:
import java.util.Arrays;
import java.util.*;
import java.io.*;
import org.apache.jena.query.*;
import org.apache.jena.tdb2.*;
import org.apache.jena.rdf.model.*;
import org.apache.jena.riot.*;
public class Minikit
{
void instanceMainWithExit(final List<String> args) {
try {
String directory="/path/to/TDB.rdfs";
File f = new File(directory);
System.err.println("dir exists ?"+ f.exists());
Dataset dataset = TDB2Factory.connectDataset(directory) ;
System.err.println("tdb2 ?" + TDB2Factory.isTDB2(dataset));
dataset.begin(ReadWrite.READ);
System.err.println("empty ? "+dataset.isEmpty());
{
Iterator<String> it = dataset.listNames();
while(it.hasNext())
{
System.err.println("NAME="+it.next());
}
}
{
Iterator<org.apache.jena.rdf.model.Resource> it = dataset.listModelNames();
while(it.hasNext())
{
System.err.println("R="+it.next());
}
}
Model model = dataset.getDefaultModel();
System.err.println("size:"+model.size());
StmtIterator it = model.listStatements();
while(it.hasNext()) {
System.err.println(it.next());
}
it.close();
dataset.commit();
dataset.end();
model.close();
dataset.close();
System.err.println("Done");
}
catch(Throwable err) {
err.printStackTrace();
System.exit(-1);
}
}
public static void main(final String[] args) throws Throwable {
new Minikit().instanceMainWithExit(Arrays.asList(args));
}
}
编译时使用
javac -cp "/path/to/conda/apache-jena/bin/../lib/*" Minikit.java
以及
tdbquery --version
Apache Jena version 6.1.0
Apache Jena TDB1 version 6.1.0
$ java -version
openjdk version "23.0.1" 2024-10-15
OpenJDK Runtime Environment (build 23.0.1+11-39)
OpenJDK 64-Bit Server VM (build 23.0.1+11-39, mixed mode, sharing)
解决方案
关键线索在错误信息里:
the lock file contents appear to be for a TDB2 database
以及你版本输出中的信息:
Apache Jena TDB1 version 6.1.0
你正在混用 TDB1 和 TDB2。
发生了什么?
你的Java代码使用:
Dataset dataset = TDB2Factory.connectDataset(directory);
它把该位置以 TDB2 数据集打开。
但 tdbquery 显示自身为:
Apache Jena TDB1 version 6.1.0
而异常堆栈来自:
org.apache.jena.tdb1...
这意味着命令行工具正在尝试访问与 TDB1 相同的目录。
一个TDB1的工具不能读取TDB2数据库,反之亦然。
第一步:检查目录结构
一个TDB2数据库通常看起来像:
TDB.rdfs/
Data-0001/
Backups/
tdb.lock
一个TDB1数据库通常包含:
TDB.rdfs/
nodes.dat
prefixes.dat
SPO.dat
POS.dat
OSP.dat
...
你的目录里到底包含了什么?
为什么Java会显示空数据集?
如果数据库实际上是由以下方式创建的:
tdbloader
tdbquery
来自一个TDB1安装,那么用以下方式打开它:
TDB2Factory.connectDataset(...)
将无法正确工作,因为你使用了错误的存储引擎。
尝试:
import org.apache.jena.tdb1.TDB1Factory;
Dataset dataset =
TDB1Factory.createDataset(directory);
如果数据是TDB1。
或者如果数据是TDB2,请在所有地方都使用TDB2:
tdb2.tdbquery
tdb2.tdbloader
(取决于Jena版本和发行版)。
另一种可能:位置不同
请注意:
String directory="/path/to/TDB.rdfs";
请确认这是数据集目录,而不是你最初加载的RDF文件。
例如:
tdbloader --loc=mydb data.ttl
会创建
mydb/
并且你应该打开:
connectDataset("mydb")
而不是
connectDataset("data.ttl")
或其他路径。
默认模型为空并不奇怪
你正在检查:
Model model = dataset.getDefaultModel();
System.err.println("size:"+model.size());
但许多数据集会把所有内容加载到命名图或联合图中。
要更可靠地检查数据集:
dataset.begin(ReadWrite.READ);
System.out.println("Default size = "
+ dataset.getDefaultModel().size());
Iterator<String> names = dataset.listNames();
while(names.hasNext()) {
String g = names.next();
System.out.println(
g + " -> " +
dataset.getNamedModel(g).size());
}
dataset.end();
不过既然 dataset.isEmpty() 已经返回 true,更可能的问题是你用错了后端(TDB1 vs TDB2)或错了目录来打开数据库。
最可疑的细节
这两行一起:
Dataset dataset = TDB2Factory.connectDataset(directory);
System.err.println("tdb2 ?" + TDB2Factory.isTDB2(dataset));
产生
tdb2 ? true
empty ? true
而 tdbquery 成功返回行,强烈表明:
命令行工具和Java程序读取的不是同一种数据集格式。