使用Java Jena API读取现有的Apache Jena TDB数据库:数据集为空

后端开发 2026-07-09

我使用 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

你正在混用 TDB1TDB2

发生了什么?

你的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程序读取的不是同一种数据集格式

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章