Grouping Concepts
So far in the previous chapter we have been looking at Mapping from a single property perspective. However it is not possible to directly manipulate a single property in the current version of Topaz. For this you need to define something called an Entity.
Entity
An entity is the smallest unit that an application can manipulate using Topaz. It can be thought of as a single node in an RDF graph. (Except Blank Nodes - See note on Blank Nodes below.)

In RDF terms, instances of an Entity represents Nodes in the RDF graph. Additional properties defined on the entities are the rdf:type and the named graph in which instances of the entity can be found. Entities that have an rdf:type specified also has equivalence in the RDFS and OWL domains. See [TBD] for a full comparison of how these are related.
See @Entity for configuring an Entity. It should be noted that in java POJO mapping case, any java class passed to the preload() method in SessionFactory becomes registered as an Entity in the SessionFactory?. See Configuring Application Metadata for more details.
Blank Nodes
Topaz cannot map properties grouped under a blank node into an object. This is because an update semantics is unclear for Blank nodes. (Note: Topaz currently does updates by deleting old statements and inserting new statements.)
[TBD - is this right?]
However the functionality from a higher level application perspective is easily achieved by identifier generators. See GeneratedValue
rdf:type handling
Topaz does special handling of rdf:type properties that are configured on the Entity. All instances of an entity will have the same rdf:type values and therefore Topaz does not map these value into an instance property. Instead this is pre-mapped/configured in the Entity metadata by the application.
rdf:type has special handling in 3 places in Topaz:
- when a new instance is persisted, Topaz will write out the predefined rdf:type values also
- when a load of an entity is requested, the rdf:type values are checked to ensure that the node that was loaded represents this entity
- rdf:type is used in sub-class determination as explained below
Named Graph Handling
Most triple-stores are really quad stores and applications have come to rely on named graph as a partition for grouping rdf statements. Therefore it is entirely possible to even apply semantic meaning for graphs by an application. An example is the permission service in Ambra.
Briefly, the permission service relies on the named graph 'grants' for permission grants and 'revokes' for permission revokes. The only difference between them is really the named graph in which they are defined.
Using Topaz this can be modeled as:
@Entity class Permission { @Id String getResource() {...} void setResource(String resource) {...} @PredicateMap Map<String, List<String>> getPermissions() {...} void setPermissions(Map<String, List<String>> setPermissions() {...} } @Entity(graph="grants") class Grants extends Permission { } @Entity(graph="revokes") class Revokes extends Permission { }
The partitioning by named graphs works in Topaz as long as the graphs are static and small in number. This is because as you saw above, the graphs are part of the metadata of an Entity.
Dynamic named graphs
If your application relies heavily on dynamic run-time named graph usage, currently the only way to support that is by adding/manipulating the metadata stored in the SessionFactory. See the sections on @Entity and @Graph for help with dynamic runtime configurations. Essentially what you would have to do is to bind the same Java class to more than one entity:
@Entity class Permission { @Id String getResource() {...} void setResource(String resource) {...} @PredicateMap Map<String, List<String>> getPermissions() {...} void setPermissions(Map<String, List<String>> setPermissions() {...} } // Let the annotation parser set up everything for Permission.class sessionFactory.preload(Permission.class); // Configure the graphs: sessionFactory.addGraph(new GraphConfig("grants", URI.create("local:///topazproject#grants"))); sessionFactory.addGraph(new GraphConfig("revokes", URI.create("local:///topazproject#revokes"))); // Now define 'Grants' entity with graph = 'grants' and as a sub-class of 'Permissions' sessionFactory.addDefinition(new EntityDefinition("Grants", Collections.emptySet(), "grants", Collections.singletonSet("Permission"))); // Bind it to the same Permission.class. Note that 'suppressAliases' is true here. sessionFactory.getClassBinding("Grants").bind(EntityMode.POJO, new ClassBinder(Permission.class, true)); // Similarly for 'Revokes' ... ... // Validate and build metadata sessionFactory.validate(); // Now the application code can look up grants and revokes for a resource: Permission grants = session.get("Grants", "my:resource:42"); Permission revokes = session.get("Revokes", "my:resource:42");
Loading and Saving Entities
Applications work with Topaz sessions to load and save instances of the entity (or collectively the group of statements with the same subject-uri.
For example:
Article a = session.get("Article", "article:23"); Article b = session.get(Article.class, "article:23");
Both the above statements will load all properties that are grouped under the "Article" entity for the subject-uri "article:23". Additionally the load will be from the graph configured for this Entity.
Similarly the following example:
Article a = new Article(); a.setId("article:42"); a.setProperty1(value1); ... ... ... session.saveOrUpdate(a);
This will save all of the properties that are grouped under the "Article" entity for the subject-uri "article:42" and that are set on the Artcile.class instance 'a'. Additionally the save will be to the graph configured for this Entity.
Entity in Queries
In OQL, Entity also identifies a graph/group from which a selection is made:
For example:
select a from Article a where ....;
The 'from' clause identifies the Entity or group from which to load.
Inheritance
Entity inheritance works much the same way as Java class inheritance. An entity can have more than one super-entity. In Java terms this is equivalent to multiple interfaces being implemented by a class.
Sub-classes inherit all of the properties of the super-classes. Additionally sub-classes inherit the graph definition and the rdf:type definitions from the super-classes.
rdf:type inheritance
The set of effective rdf:type values of a sub-class is the union of the declared rdf:type values and the effective rdf:type values of all of it's declared super-classes. Both the declared set and the effective set are maintained by Topaz on the metadata for this Entity. (See getTypes() and getAllTypes() in the org.topazproject.otm.ClassMetadata?)
For example:
@Entity(graph="animals", types = {animals:Animal}) class Animal { ... } @Entity(types = {animals:Cat}) class Cat extends Animal { ... }
'Animal' has {'animals:Animal'} as both the declared and effewctive rdf:type values set. Where as 'Cat' has {'animals:Cat'} as the declared set and {'animals:Animal', 'animals:Cat'} as the effective set.
Topaz writes out the effective set of rdf:types always when an instance is persisted. Whereas queries will only test for the declared sets to check the existence of Entities.
eg:
select a from Animal a ...; select c from Cat c ...;
The first query tests for {'animals:Animal'} and the second {'animals:Cat'}.
That is there is no inferencing done in the query for Animal to test for 'animals:Cat' and include those in the result set.
There is no need for inferencing when working with data that is persisted by Topaz. eg. When Cat instances are persisted with Topaz the effective set {'animals:Animal', 'animals:Cat'} is what gets written oput.
However when working with existing data, the application must run this inferencing and insert the effective set before Topaz can work with it. In TQL this can be achieved by executing the following:
insert select $s <rdf:type> <animals:Animal> from <...> where $s <rdf:type> <animals:Cat> into <...>;
Graph inheritance
Graph values are handled differently. There can only be one graph that an Entity is associated with in Topaz. So the declared graph value will always override the values declared in the super-classes.
When a graph is not configured on an entity, Topaz expects the union of the graphs from the super-classes to be a singleton set or an empty set. ie. Conflicting graph definitions will result in an error being flagged during the configuration validation step.
For example, given these definitions:
@Entity(graph="animals", types = {animals:Animal}) class Animal { ... } @Entity(types = {animals:Cat}) class Cat extends Animal { ... }
The named graph 'animals' is inherited by Cat.
Property Overrides
Normally during inheritance, all of the properties of the super-classes are added to the set of properties declared on this Entity to form an effective property set.
However there is also the case of superseded property definitions that Topaz supports. Most notable use case is the support for java generics in POJO mappings. (see below)
But in its most general form any attribute of a property may be superseded in a sub-class. This more or less translates to any attribute in @Predicate being supersedable. superseded property then becomes a 'referenced' property in this property definition in the absence of any declared 'referenced' properties. (See ref attribute in @Predicate)
When Topaz builds the effective set of properties for an entity, the superseded properties are discarded and are replaced with the superseding properties declared in this entity.
Java Generics support
Generics support in Topaz is a special case of superseded property definitions. As you have seen from above sections, the declared data-type of a property is used by the annotation parser to make numerous decisions about the property:
- literal vs resource reference
- data-type in case of literals and associated entity in case of resource reference
These determinations then supersede the ones from the genric base class. In addition an application may explicitly control the superseded properties by having an annotated superseded getter/setter method declaration in the sub-class.
Sub-Class Resolving
During load from the TripleStore, Topaz creates an instance of an entity based on the properties it finds in the !Triplestore. Just as in Java, a sub-class entity instance is assignment compatible to any of its super entities.
For example, given these definitions:
@Entity(graph="animals", types = {animals:Animal}) class Animal { ... } @Entity(types = {animals:Cat}) class Cat extends Animal { ... }
And given the following graph:

Then the following assertions would hold:
Animal a = session.get(Animal.class, "cat:1"); assert a instanceof Cat;
Application Defined SubClass Resolver
In the above exanple. Topaz makes the subclass resolving decision purely based on the rdf:type values. For cases where that is not sufficient, an application needs to define a subclass resolver. (See @SubClassResolver)
SubClass Resolvers get to see the entire forward and reverse mapped properties and their values and therefore can decide on a sub-class based on any criteria.
For example, given the additional class definition:
@Entity class BlackCat extends Cat { ... }
And the following graph:

A SubClass Resolver can then make use of the <cat:1> <animal:color> 'black' statement and determine that the required instance is that of a 'BlackCat?'.
See @SubClassResolver for an example on how to define a sub-class resolver.
Containment
We saw in the inheritance section how sub-classes inherit the properties of the super-class. However inheritance is not the only way in which a group of properties can be re-used. An example is the usage of a group of properties that define a postal-address:
@UriPrefix("address:") class Address { String getStreet() {...} @Predicate void setStreet(String s) {...} String getCity() {...} @Predicate void setCity(String s) {...} String getState() {...} @Predicate void setState(String s) {...} String getZip() {...} @Predicate void setZip(String s) {...} String getCountry() {...} @Predicate void setCountry(String s) {...} } @Entity class Office { ... ... @Embedded Address getAddress() {...} void setAddress(Address address) {...} } @Entity class Home { ... ... @Embedded Address getAddress() {...} void setAddress(Address address) {...} } @Entity class Applicant { @Embedded Address getAddress() {...} void setAddress(Address address) {...} }
rdf:type from contained entity
Currently Topaz ignores any rdf:type values defined in the contained entity. Therefore it is better to leave out any rdf:type definitions for the containing entity to define. However this may change in a future version.
Named graph from contained entity
Since Topaz allows graph definitions at a property level, any graph definitions on the contained entity is only applied to properties within that entity.
For example, if the 'Address' entity defined a graph named 'address' for its properties, then all usages of 'Address' in any containing class, will continue to load and save the properties of Address from the named graph 'address'.
ie. the graph definitions of the contained graph cannot currently be overridden. However this may change in a future version.
Superseding properties from a contained entity
Similar to graph definition, the property definitions in a contained property cannot be superseded in the containing entity. This behavior also may change in a future version.
Association
Association is strictly not a property grouping concept in the same lines as inheritance and containment. The difference is now we are traversing to a different graph node.
For example:

can be expressed in POJO mapping as:
@Entity class Book { ... ... @Predicate(uri="dc:title") String getTitle() {...} void setTitle(String title) { ... } @Predicate(uri="dc:creator") Set<Author> getAuthors() {...} void setAuthors(Set<Author> authors) { ... } } @Entity class Author { ... ... @Predicate(uri="foaf:name") String getName() {...} void setName(String name) { ... } }
Alternatively from a graph representation point of view we could define the authors property for Book as:
@Entity class Book { ... ... @Predicate(uri="dc:creator", type=Predicate.PropType.OBJECT) Set<String> getAuthors() {...} void setAuthors(Set<String> authors) { ... } }
The differences are:
- the first form is called an Association where as the second is merely an Object property definition with no clear rdfs:range specified.
- the second form therefore is more dynamic in the sense it is not limited to instances of Author objects. In this case, the application needs to make an explicit call to session.get(Author.class, id) to load instances of Author objects.
- the first form also is considered an implicit filter for nodes that match instances of Author objects. WARN: The filtering is done at load time and the nodes that are filtered out are discarded. This means any change that causes the list of associations to be modified (eg. adding/deleting Authors) will cause the new list to replace the existing list from the triple-store. This means any statements that were filtered out by the implicit filter will also be removed. Most often that is the desired behavior, but there could be cases where the original filtered out statements need to be preserved. This option is slated for a future version of Topaz.
Fetch options
The associated object is loaded by Topaz on an as needed basis (called lazy loading). It is possible to change the loading option to eager by setting the fetch option in @Predicate annotation (or manually by setting the FetchType value in the RdfDefinition configured for this property).
WARN: For Lazy loading to succeed, the Topaz Session must be open and a Transaction must be active. The load is performed on the first access to a property of the object. This means a get or set of a property is the one that is triggering the load. This is something to watch out for when passing lazy loaded POJO objects to a View component in an MVC framework, for example. Accessing a lazy loaded property that is yet to be loaded outside of a Transaction scope will result in an OtmException being thrown. To mitigate this an application has three options:
- define associations as eager fetched
- manually touch properties needed by view components
- use an Open Session in View paradigm where a Session and Transaction is kept alive even when the view is being built
Cascade options
The FetchType option controls how associations are loaded when Topaz is asked to load an object. Similarly updates on an object can cascade down to the Associations. This is explained in the Transitive Persistence section.
