Как я могу вкладывать объекты с помощью OpenRDF Alibaba с назначенными IRI ресурсов?

Я пробую OpenRDF Alibaba (связанный с Sesame) в качестве инструмента для сопоставления объектов Java с тройками RDF и обратно. В настоящее время я смотрю, как он обрабатывает графы объектов.

У меня есть два объекта, внутренний и внешний. Внешний имеет ссылку на Внутренний. Когда я сохраняю внешний экземпляр, кажется, что внутренний экземпляр всегда представлен как b-узел, даже если я ранее сохранял внутренний экземпляр с назначенным IRI.

Что мне нужно сделать, чтобы иметь возможность успешно назначить IRI внутреннего экземпляра самостоятельно, вместо того, чтобы создавать b-узлы?

Дополнительный кредитный вопрос: как я могу сделать IRI ресурса свойством объекта Java, вместо того, чтобы он был параллельным, но отключенным от объекта, который он идентифицирует?


Код:

Внутренний:

package alibabaeval.domain;

import org.openrdf.annotations.Iri;

@Iri("http://example.com/innerType")
public class Inner {

    @Iri("http://example.com/innerType/data")
    private String data;

    public Inner(String data) {
        this.data = data;
    }


    // if this is missing, an unhelpful ClassCastException will be thrown on retrieval
    public Inner() {
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

Внешний:

package alibabaeval.domain;

import org.openrdf.annotations.Iri;

@Iri("http://example.com/outerType")
public class Outer {

    @Iri("http://example.com/outerType/data")
    private String outerData;

    @Iri("http://example.com/outerType/innerObject")
    private Inner innerObject;

    public Outer(String outerData) {
        this.outerData = outerData;
    }

    // if this is missing, an unhelpful ClassCastException will be thrown on retrieval
    public Outer() {
    }

    public String getOuterData() {
        return outerData;
    }

    public void setOuterData(String outerData) {
        this.outerData = outerData;
    }

    public Inner getInnerObject() {
        return innerObject;
    }

    public void setInnerObject(Inner innerObject) {
        this.innerObject = innerObject;
    }
}

Программа испытаний:

package alibabaeval;

import org.junit.Test;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.query.QueryLanguage;
import org.openrdf.repository.Repository;
import org.openrdf.repository.object.ObjectConnection;
import org.openrdf.repository.object.ObjectRepository;
import org.openrdf.repository.object.config.ObjectRepositoryFactory;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFWriter;
import org.openrdf.rio.Rio;
import org.openrdf.sail.memory.MemoryStore;

import alibabaeval.domain.Inner;
import alibabaeval.domain.Outer;

public class AlibabaEval {

    public static void main(String[] args) throws Exception {
        Repository store = new SailRepository(new MemoryStore());
        store.initialize();

        // wrap in an object repository
        ObjectRepositoryFactory factory = new ObjectRepositoryFactory();
        ObjectRepository repository = factory.createRepository(store);

        // add a stuff to the repository
        ObjectConnection con = repository.getConnection();
        ValueFactory vf = con.getValueFactory();

        Inner inner = new Inner("some inner data");
        URI innerId = vf.createURI("http://example.com/inners/inner1");
        con.addObject(innerId, inner);

        URI outerId = vf.createURI("http://example.com/outers/outer1");
        Outer outer = new Outer("some outer data");
        outer.setInnerObject(inner);
        con.addObject(outerId, outer);

        // look at the triples that were created
        System.out.println("\n\n\nGenerated triples:");
        RDFWriter writer = Rio.createWriter(RDFFormat.NTRIPLES, System.out);
        con.prepareGraphQuery(QueryLanguage.SPARQL, "CONSTRUCT { ?s ?p ?o } WHERE {?s ?p ?o }").evaluate(writer);

        // close everything down
        con.close();
        repository.shutDown();
    }
}

Вывод:

Я создал только два экземпляра объекта и сохранил их по отдельности. Alibaba, похоже, проигнорировала это и создала вторую копию внутреннего экземпляра в качестве b-узла для ссылки из внешнего экземпляра.

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/me/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-jdk14/1.7.7/25d160723ea37a6cb84e87cd70773ff02997e857/slf4j-jdk14-1.7.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/me/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-log4j12/1.7.12/485f77901840cf4e8bf852f2abb9b723eb8ec29/slf4j-log4j12-1.7.12.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.JDK14LoggerFactory]
Jan 08, 2016 6:00:21 PM org.openrdf.repository.object.managers.helpers.Scanner scan
INFO: Scanning C:\workspace\AlibabaTest\bin for concepts
Jan 08, 2016 6:00:22 PM org.openrdf.repository.object.ObjectRepository compileSchema
INFO: Compiling schema
Jan 08, 2016 6:00:22 PM org.openrdf.repository.object.composition.ClassResolver setBaseClassRoles
WARNING: Concept will only be mergable: class alibabaeval.domain.Inner
Jan 08, 2016 6:00:22 PM org.openrdf.repository.object.composition.ClassResolver setBaseClassRoles
WARNING: Concept will only be mergable: class alibabaeval.domain.Outer



Generated triples:
<http://example.com/inners/inner1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/innerType> .
<http://example.com/inners/inner1> <http://example.com/innerType/data> "some inner data" .
<http://example.com/outers/outer1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/outerType> .
_:node1a8hqu4aqx1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/innerType> .
_:node1a8hqu4aqx1 <http://example.com/innerType/data> "some inner data" .
<http://example.com/outers/outer1> <http://example.com/outerType/innerObject> _:node1a8hqu4aqx1 .
<http://example.com/outers/outer1> <http://example.com/outerType/data> "some outer data" .

person Kaypro II    schedule 09.01.2016    source источник
comment
Это всего лишь предположение (я давно не пользовался Alibaba, поэтому я заржавел), но я думаю, что проблема в том, что идентификатор для Inner добавляется в магазин, но ваш фактический Inner POJO не обновляется. Вам может понадобиться добавить строку inner = con.getObject(Inner.class, innerId); после addObject.   -  person Jeen Broekstra    schedule 09.01.2016
comment
Итак, необходимо выполнить циклический обход каждого объекта через слой сохраняемости (чтобы присвоить ему идентификатор) и заменить вложенные экземпляры на месте (чтобы сохранить небазовый граф объектов)? Я попробую, когда вернусь к работе, хотя я надеюсь, что есть менее сложный способ сделать это.   -  person Kaypro II    schedule 09.01.2016
comment
Добавление inner = con.getObject(Inner.class, innerId); и добавление экземпляра этого к внешнему избавились от b-узлов. Спасибо!   -  person Kaypro II    schedule 11.01.2016
comment
Рад слышать, что это решило это. Я обновил свой комментарий до ответа. Также, чтобы справиться с вашим страхом перед производительностью: это не должно быть так уж плохо, как это делает Alibaba при кэшировании в памяти недавно использованных объектов.   -  person Jeen Broekstra    schedule 12.01.2016
comment
Производительность была лишь частью этого. Другая часть заключалась в сохранении неуправляемого графа объектов со всеми правильными идентификаторами (например, неупорядоченного из веб-сервиса в POJO). Я вижу по крайней мере один способ написать код, чтобы сделать это механически, хотя я надеялся, что Alibaba сделает это за меня. Есть ли способ установить ресурс для неуправляемого объекта, чтобы он был выбран с одним параметром con.addObject (экземпляр объекта)?   -  person Kaypro II    schedule 12.01.2016
comment
Я не уверена. Возможно, вам больше повезет, если вы спросите об этом в группе пользователей Sesame, там скрываются более опытные пользователи Alibaba (и ее главный разработчик).   -  person Jeen Broekstra    schedule 12.01.2016


Ответы (2)


Проблема в том, что идентификатор для Inner добавляется в хранилище, но ваш фактический Inner POJO не обновляется. Чтобы это исправить, просто добавьте строку inner = con.getObject(Inner.class, innerId); после вызова addObject.

FWIW дополнительный штраф за возврат туда и обратно для этого не особенно серьезен, поскольку Alibaba выполняет кэширование объектов последнего доступа, поэтому для этого поиска не нужно будет проходить весь путь до уровня сохраняемости.

Что касается того, как получить идентифицирующий ресурс из самого объекта: если вы убедитесь, что ваш POJO реализует интерфейс RDFObject, вы можете вызвать getResource для получения связанного идентификатора. Между прочим, ваша реализация метода getResource может просто вернуть null, поскольку Alibaba переопределит реализацию в сгенерированном объекте.

person Jeen Broekstra    schedule 11.01.2016

Теперь, когда я немного лучше знаком с Alibaba и еще раз прочитав документацию, я вижу лучший ответ.

Alibaba будет использовать значение, возвращаемое RDFObject.getResource() для отдельного объекта. Вот реализация inner, которая делает это:

package alibabaeval.domain;

import org.openrdf.annotations.Iri;
import org.openrdf.model.Resource;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.repository.object.ObjectConnection;
import org.openrdf.repository.object.RDFObject;

@Iri("http://example.com/#innerType")
public class Inner implements RDFObject {

    @Iri("http://example.com/innerType/data")
    private String data;

    private Resource detachedId;

    public Inner() {

    }

    @Override
    public ObjectConnection getObjectConnection() {
        // don't really care about this one right now
        return null;
    }

    @Override
    public Resource getResource() {
        // only run on detached object, is hidden by a proxy on managed objects
        return detachedId;
    }

    public Resource getDetachedId() {
        return detachedId;
    }

    public void setDetachedId(Resource detachedId) {
        this.detachedId = detachedId;
    }

    public void setDetachedId(String detachedId) {
        this.detachedId = new URIImpl(detachedId);
    }

    public void setResource(Resource resource) {
        detachedId = resource;
    }

    public Inner(String data) {
        this.data = data;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

Это позволяет вам сделать это:

    Inner inner = new Inner("some inner data");
    inner.setDetachedId("http://example.com/inners/inner1");

    URI outerId = vf.createURI("http://example.com/outers/outer1");
    Outer outer = new Outer("some outer data");
    outer.setInnerObject(inner);
    con.addObject(outerId, outer);

И получить эти тройки:

<http://example.com/inners/inner1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/#innerType> .
<http://example.com/inners/inner1> <http://example.com/innerType/data> "some inner data" .
<http://example.com/outers/outer1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/#outerType> .
<http://example.com/outers/outer1> <http://example.com/outerType/data> "some outer data" .
<http://example.com/outers/outer1> <http://example.com/outerType/innerObject> <http://example.com/inners/inner1> .

Этот метод также работает с методом ObjectConnection.addObject(Object instance) с одним параметром.

Это также работает только для отдельных объектов. Экземпляры, управляемые Alibaba, будут проксировать метод RDFObject.getResource(), поэтому вы больше не сможете контролировать то, что он возвращает.

person Kaypro II    schedule 13.01.2016