Skip to content
Martin Ledvinka edited this page May 30, 2025 · 4 revisions

Descriptors are used in JOPA to parameterize entity operations. They form a simple hierarchy of descriptors shown in the following image.

descriptors

  • EntityDescriptor is the root describing an entity. If an entity references another entity (via object or annotation property), another EntityDescriptor can be used to describe the attribute
  • ObjectPropertyCollectionDescriptor is used to describe a plural object property attribute. It references an EntityDescriptor that is used to describe the elements
  • FieldDescriptor is used to describe a literal value attribute (datatype or annotation property)

Descriptors work recursively. So unless a setting is overridden in another descriptor, the value from the root descriptor applies. Also, a descriptor can be set as an attribute descriptor of itself. This can be handy for recursive structures like composites.

The following sections provide details of what can be configured with descriptors.

RDF Named Graphs (Contexts)

RDF named graphs (called contexts in JOPA API) allow to partition the storage into logical, uniquely identified collections of data.

Descriptors can specify the contexts (named graphs) of the relevant entity and (optionally) customize the contexts used for its attributes or referenced entities.

An EntityDescriptor can specify the context in which an entity is stored together with its attributes. Unless customized, all attributes of an entity are stored in the same context as the descriptor subject.

When configuring attributes, one has two options. It is possible to set just the attribute context via the addAttributeContext method. The descriptor will decide whether another EntityDescriptor or a FieldDescriptor will be used based on the attribute type. Object properties get a new EntityDescriptor, data and annotation properties get a FieldDescriptor. Another option is to call addAttributeDescriptor to specify the descriptor directly. In case of literal values, the property assertion is stored in the target context. In case of object properties, the property assertion is stored in the subject's context by default. This can be overridden by passing a false parameter to one of the EntityDescriptor constructors, which means that property assertions are stored in the target object's context.

For example, consider entities a and b.

a.hasB = b;
EntityDescriptor desc = new EntityDescriptor(URI.create("http://contextOne"));
desc.addAttributeDescriptor(hasBAttribute, new EntityDescriptor(URI.create("http://contextTwo"));
entityManager.persist(a, desc);

This will in effect lead to the following quads:

:a a :A http://contextOne .
:a :hasB :b http://contextOne .
:b a :B http://contextTwo .

In contrast, if we have the following code:

a.hasB = b;
EntityDescriptor desc = new EntityDescriptor(URI.create("http://contextOne"), false);
desc.addAttributeDescriptor(hasBField, new EntityDescriptor(URI.create("http://contextTwo"));
entityManager.persist(a, desc);

Then, the following quads will be the result:

:a a :A http://contextOne .
:a :hasB :b http://contextTwo .
:b a :B http://contextTwo .

Notice where the property assertion is stored this time - in the context of the target entity.

Plural Contexts

It is possible to specify multiple contexts for a descriptor or any of its attributes. However, this can be used only for retrieval operations, where a union of these contexts is used to get data. This can be useful when data are stored in a set of named graphs and the application does not know concrete context for a particular entity, but it cannot use the default context to prevent data conflicts. For write operations (persist, update), at most one context can be specified. If multiple contexts are encountered, an AmbiguousContextException is thrown.

Static Context Declaration

Since version 2.4.0, it is possible to use the @Context annotation to specify the repository context statically. The context can be specified on class or attribute level and can be configured to recursively propagate or apply only to the annotated item (class -> all instances, attribute -> referenced instances). If recursive propagation is disabled (default), any other reference/class is expected in the default context.

Note that static context declaration works only when descriptor is not passed to the EntityManager. If a descriptor is passed to the entity manager, regardless of the depth of its specification, @Context is ignored throughout the object graph reachable from the subject entity. This is because the implementation creates @Context-based descriptors for entity classes and uses them only for EntityManager calls without descriptor.

So, for example (assuming prefixes are declared):

@Context("ex:users")    // Contexts support prefixes
@OWLClass(iri = "foaf:Person")
public class Person {
    // Id, attributes, getters/setters
}

EntityManager em = // create entity manager
Person one = em.find(id, Person.class);    // Will use a descriptor created based on `@Context` in reachable entity classes
Person two = em.find(id, Person.class, new EntityDescriptor());    // Will use only what can be determined from the specified descriptor

Language

Descriptors can also be used to override the persistence unit-wide language configuration (cz.cvut.jopa.lang). This allows configuration of string-valued attributes loading and saving. Once again, setting the value on a root descriptor applies to all descriptors it references unless overridden.

Let's say we have the persistence unit configured to use English as the default string language (quite common setting). Suppose we have an entity class describing historical monuments and the following data about Wenceslas Square in Prague (in Turtle syntax):

<https://dbpedia.org/page/Wenceslas_Square> a <http://dbpedia.org/ontology/Place> ;
  rdfs:label "Wenceslas Square"@en , "Václavské náměstí"@cs .

Now, creating a descriptor and setting its language to Czech (or even just the label attribute) will ensure that JOPA will load the instance with the Czech label in this case

Descriptor d = new EntityDescriptor().setLanguage("cs");
Monument wSquare = em.find(Monument.class, URI.create("https://dbpedia.org/page/Wenceslas_Square"), d);
assert wSquare.getLabel() == "Václavské náměstí";

Inference

On more feature that can be configured via descriptors is inference. More specifically, if an attribute is marked as potentially containing inferred values (via the @Inferred annotation), a descriptor can be used to disable loading inferred values for the attribute.

Descriptor d = new EntityDescriptor().disableInference();
Monument wSquare = em.find(Monument.class, URI.create("https://dbpedia.org/page/Wenceslas_Square"), d);

The instance will be loaded with asserted data only.

Clone this wiki locally