Skip to content

Object ontological Mapping

Martin Ledvinka edited this page Oct 16, 2024 · 7 revisions

Object-ontological mapping (OOM) in JOPA is, much like in JPA, realized via annotations in Java.

The annotations tell JOPA which classes it should consider as entity classes, which of their attributes are persistent and to what ontological properties they are mapped.

The most important annotations in this regard are:

@OWLClass is placed on a Java class and specifies that this class is a part of the persistence unit metamodel. It has one attribute - iri - whose value maps the class to the corresponding RDFS/OWL class.

@Id marks field which contains the instance's identifier (IRI in ontological terms). Supported types are String, java.net.URI and java.net.URL.

The identifier can be marked as generated, so that when an instance with an empty identifier is persisted, JOPA will generate the identifier value. The generated identifier is generally based on the entity class IRI, with some random data appended to it.

@OWLObjectProperty marks Java attributes which reference other objects. In OWL, this corresponds to object properties, which link individuals to other individuals. RDF makes no such distinction.

However, for JOPA, the distinction between @OWLObjectProperty and other kinds of properties (see below) is important. They tell it whether it should expect a literal value (like an integer, String) or a resource's identifier. Therefore, when using RDF-based storage like RDF4J, @OWLObjectProperty should still be used, marking fields whose type is either another entity class or String/java.net.URI/java.net.URL. In case the type is String/URI/URL, JOPA will assume that the intended value is an individual's identifier (this is referred to as plain identifier object property in Javadoc and source code. No other types can be used for fields annotated with @OWLObjectProperty.

The iri attribute represents the property to which the annotated attribute is mapped.

fetch specifies whether the attribute value should be eagerly or lazily loaded (it is supposed to work the same as in JPA). Note that when using data access objects, it is usually better to have the fetch set to eager. Since JOPA currently does not support extended persistence contexts, once the loaded object becomes detached from the persistence context (usually when returned from DAO/service with transactional entity manager), lazily loaded attributes may be null without any way to load them. Defaults to FetchType.LAZY.

cascade whether entity manager operations should be cascaded over this attribute. This works the same way as in cascading in JPA.

@OWLDataProperty marks a literal-valued field. This corresponds to an OWL datatype property. Again, RDF does not care about this distinction, but JOPA will assume the value to be a literal and treat it as such. The annotated fields should not used Java primitive types but their wrapper equivalents, i.e. Integer for int etc.

The iri attribute represents the property to which the annotated attribute is mapped.

fetch works same as above. It defaults to FetchType.EAGER for data properties.

lexicalForm allows to load the property value into a String regardless of the type of the literal. This functionality, however, has a limitation - such an attribute would be read-only. This is because when the field is a String representing lexical form of the literal, JOPA has no way of knowing what the type of the literal should be when saving it (e.g., on persist, or even on update). Updating could thus lead to unintentional changes in the data. A safer way is thus to prevent modification of such values.

simpleLiteral configures the property to be stored/accessed as a RDF simple literal, i.e. a literal stored as xsd:string. Simple literals allow to store string literals without worrying about language tags. Note that language tagged strings can be loaded as simple literals. However, any changes to them will be stored as simple literals, so language information will be lost in that case.

@OWLAnnotationProperty correspond to OWL annotation properties. The notion is not known in RDF, so when accessing RDF-based storages, this annotation can be ignored and OWLDataProperty used instead. However, when using OWL-based storage (i.e. currently only OWL API), the distinctions between different kinds of properties are significant.

The iri attribute represents the property to which the annotated attribute is mapped.

fetch works same as above. It defaults to FetchType.EAGER for annotation properties.

lexicalForm works the same way as above. If the annotation value is an resource/individual, its identifier is loaded into the attribute. The read-only conditions, however, applies to it as well.

simpleLiteral works the same way as in data properties (see above). If the annotation value is an resource/individual, its identifier is loaded into the attribute.

Below is a simple example of a JOPA entity, which makes use of all the aforementioned kinds of properties:

@OWLClass(iri = Vocabulary.s_c_Occurrence)
public class Occurrence {

    @Id
    private URI uri;

    @OWLDataProperty(iri = Vocabulary.s_p_has_key)
    private String key;

    @OWLAnnotationProperty(iri = CommonVocabulary.RDFS_LABEL)
    private String name;

    OWLObjectProperty(iri = Vocabulary.s_p_has_part, fetch = FetchType.EAGER, cascade = {CascadeType.MERGE,
            CascadeType.REMOVE})
    private Set<Event> children;

    // Getters and setters follow
}

If a field should not persistent, one of the following mechanisms can be used:

  • Field is static
  • Field is transient
  • Field is annotated with @Transient

Collection Mapping

In RDF (and OWL), everything is a set - an RDF graph is a set of statements, multiple values of a property represent a set, i.e., there are not duplicates and there is no particular order. This is reflected by JOPA in that plural attributes are by default mapped to a java.util.Set. If the field type is java.util.Collection, the implementation used by JOPA will also by a set (typically a java.util.HashSet).

Lists, i.e., ordered sequences of statements, are supported by multiple additional features:

  • OWL sequences
  • RDF collections
  • RDF containers

OWL Sequences

OWL sequences represent an implementation of an ontology design pattern. There are two types of sequences:

  1. Simple
  2. Referenced

A simple list is basically a linked list of resources that each point to the next in the sequence. As such, they do not support literals. Referenced lists consist of list nodes that each point to a value (which can be a resource or a literal) and to the next node in the list. Reference list nodes are generated automatically by JOPA. The following snippet shows definition of attributes of both kinds:

@Sequence(type = SequenceType.referenced) // This is the default sequence type
@OWLObjectProperty(iri = "http://example.com/referencedList")
private List<Integer> marks;

@Sequence(type = SequenceType.simple)
@OWLObjectProperty(iri = "http://example.com/simpleList")
private List<URI> isolationLevels;

RDF Collections

RDF Collections, more specifically RDF lists work the same way as referenced sequences discussed above. The only difference is that RDF Lists use a controlled vocabulary and the list itself is terminated by a rdf:nil node (referenced sequence does not have a distinct terminal node). They can be used as follows:

@RDFCollection
@OWLObjectProperty(iri = "http://example.com/rdfCollection")
private List<URI> isolationLevels;

RDF Containers

RDF Containers define vocabulary and an informal semantics description (it is not enforced by the standard, though) for three types of containers - alternatives, bags, and sequences. The RDF Schema specification implies that an rdf:Alt is a set of distinct alternative values, a rdf:Bag is an unordered container, and a rdf:Seq is an ordered sequence of values. JOPA enforces some basic rules based on the informal description of the meaning of the various containers:

  • A rdf:Seq cannot be mapped to java.util.Set
  • A rdf:Alt cannot be mapped to java.util.List

RDF containers can store both individuals and literals.

The mapping can be used for example as follows:

@RDFContainer(type = RDFContainerType.SEQ)
@OWLObjectProperty(iri = "http://example.com/rdfContainer")
private List<URI> isolationLevels;