Annotations

This section describes Topaz Java Annotations. See Configuring Application Metadata to see how annotated classes are registered with Topaz.

Alias An annotation for configuring a single alias for a uri.
Aliases An annotation for configuring a list of aliases.
Blob An annotation to mark a Blob field.
Embedded An annotation to mark an embedded class field.
Entity Annotation for classes to specify the necessary config for controlling persistence to an RDF triplestore.
GeneratedValue An annotation to support generated values.
Graph An annotation for configuring a single graph within the triple store
Graphs An annotation for configuring a list of graphs.
Id An annotation to mark an id field to be used as the subject of the rdf triples.
Predicate Annotation for properties to specify the necessary config for controlling persistence to an RDF triplestore.
PredicateMap An annotation to mark a Map<String,List<String>> field as a run-time set of predicate to value map.
Projection Annotation for fields in View's and SubView's.
Searchable Annotation for properties to specify that they should be inserted into lucene.
SubClassResolver Marks a static method as a SubClassResolver for the defining class.
SubView Annotation for sub-view classes.
UriPrefix An annotation for configuring the default uri-prefix for predicate uris that are auto-generated from the field name.
View Annotation for view classes.

Alias

Used to define a URI alias. Typical usage is as follows at the class or package level:

@Aliases({
  @Alias(alias = "dc",       value = Rdf.dc),
  @Alias(alias = "topaz",    value = Rdf.topaz)
})

An alternate to using the @Alias definition is to programatically set the alias as in:

   sessionFactory.addAlias("dc", Rdf.dc);
   sessionFactory.addAlias("topaz", Rdf.topaz);

Aliases

Groups multiple @Alias definitions as shown above. This grouping allows you to define more than one Alias annotation on a class or package level.

Blob

An annotation to mark a Blob property. Only one property in an Entity may be marked as a Blob field and it must be a scalar field. The @Id represents the id of the Blob and must be unique in the BlobStore where this Blob is persisted.

Note that the same annotation can be used for both streamable and copied blobs.

@Entity
class Image<T> {
...
...

  T getBlob() {...}
  void setBlob(T blob) {...}
}

@Entity
class SmallImage extends Image<byte[]> {
...
...

  byte[] getBlob() {...}
  @Blob
  void setBlob(byte[] blob) {...}
}

@Entity
class LargeImage extends Image<javax.activation.DataSource> {
...
...

  javax.activation.DataSource getBlob() {...}
  @Blob
  void setBlob(javax.activation.DataSource blob) {...}
}

An alternate way to configure the Blobs:

  // small blob
  ... 
  ... 
  sessionFactory.addDefinition(new BlobDefinition("SmallImage:blob", null, null));
  Property property = new Property(SmallImage.class, "blob");
  PropertyBinderFactory binder = new PropertyBinderFactory("SmallImage:blob", property);
  sessionFactory.getClassBinding("SmallImage").addBinderFactory(binder));

  ...
  ...
  // large blob
  ... 
  ... 
  sessionFactory.addDefinition(new BlobDefinition("LargeImage:blob", null, null));
  Property property = new Property(LargeImage.class, "blob");
  PropertyBinderFactory binder = new PropertyBinderFactory("LargeImage:blob", property);
  sessionFactory.getClassBinding("SmallImage").addBinderFactory(binder));

Embedded

This marks a property as a component of the containing Entity. All properties of the embedded component becomes properties of this entity. An example:

@Entity
class Employee {
  ...
  ...
  String getTitle() {...}
  @Predicate
  void setTitle(String s) {...}

  Address getAddress() {...}
  @Embedded
  void setAddress(Address a) {...}
}

@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) {...}
}  

This will cause the following graph for an instance of Employee:

An alternate to defining the @Embedded annotation is:

  sessionFactory.addDefinition(new EmbeddedDefinition("Employee:address", "Address"));

  Property property = new Property(Employee.class, "address");
  PropertyBinderFactory binder = new PropertyBinderFactory("Employee:address", property);
  sessionFactory.getClassBinding("Employee").addBinderFactory(binder));

Entity

Defines a persistent Entity class. The attributes are:

  • name - the unique name for the entity. Defaults to the class-name without the Package prefix.
  • graph - the graph where this is persisted. Defaults to the one inherited from the super-class. See Graph below for configuration information.
  • types - the list of rdf:type values that are specific to this class. It is not necessary to copy or repeat the rdf:type values of the super-classes or interfaces. They are automatically picked up. eg:
@Entity(types={"topaz:Animal"}, graph="animals")
class Animal {
  ...
  ...
}

@Entity(types={"topaz:Cat"})
class Cat extends Animal {
...
...
}  

This will cause the following graph for an instance of Cat:

Note: It is not mandatory that all Entity classes have the @Entity annotation. But it is strongly encouraged that you define one even when it is sufficient to use the default values for all attributes.

An alternate way to define an Entity definition is as follows:

   sessionFactory.addDefinition(new EntityDefinition("Animal", 
                       Collections.singletonSet(Rdf.topaz + "Animal"). "animals",
                       Collections.emptySet()));

   sessionFactory.addDefinition(new EntityDefinition("Cat", 
                       Collections.singletonSet(Rdf.topaz + "Cat"). null,
                       Collections.singletonSet("Animal")));


   sessionFactory.getClassBinding("Animal").bind(EntityMode.POJO, new ClassBinder(Animal.class));
   sessionFactory.getClassBinding("Cat").bind(EntityMode.POJO, new ClassBinder(Cat.class));

   // Add properties
   ...
   ...

GeneratedValue

This annotation tells Topaz that if an Entity's id field is not set, then Topaz should generate a value using the configured Id generator. The options are:

  • generatorClass - a user supplied Id generator class. The class must implement the org.topazproject.otm.id.IdentifierGenerator and must have a no-argument Constructor and must be loadable by the ThreadContextClassLoader. One instance of this class is created per declaration of this annotation and therefore the org.topazproject.otm.id.IdentifierGenerator#generator method must be multi-thread safe. The Default value is org.topazproject.otm.id.!GUIDGenerator which will generate a unique ID using http://java.sun.com/j2se/1.5.0/docs/api/java/util/UUID.html
  • uriPrefix - the prefix to prepend to convert the unique id generated by the generatorClass to a valid URI.

Example:

@Entity
class Employee {
 public String getId() {...}
 @Id
 @GeneratedValue(uriPrefix="topaz:Id/")
 public void setId() {...}

 ...
 ...
}

See Id for an example of how a Generator can be configured directly using SessionFactory API.

Graph

The Entity and Predicate annotations provide a way to define a 'graph' name. Additional information is needed in order for Topaz to translate from these graph names to a named-graph URI that the triple-stores can work with. Typical usage is as follows:

@Graphs({
  @Graph(id = "str", uri = "local:///topazproject#str",
         type = "http://topazproject.org/graphs#StringCompare"),
  @Graph(id = "xsd", uri = "local:///topazproject#xsd",
         type = Rdf.mulgara + "XMLSchemaModel"),
  @Graph(id = "prefix", uri = "local:///topazproject#prefix",
         type = Rdf.mulgara + "PrefixGraph"),
  @Graph(id = "photo", uri = "local:///topazproject#photo")
})

Note that it is possible to directly define these graph definitions using the SessionFactory API calls:

   sessionFactory.addGraph(new GraphConfig("str", URI.create("local:///topazproject#str"),
                                           URI.create("http://topazproject.org/graphs#StringCompare")));
   sessionFactory.addGraph(new GraphConfig("xsd", URI.create("local:///topazproject#xsd"),
                                           URI.create(Rdf.mulgara + "XMLSchemaModel")));
   sessionFactory.addGraph(new GraphConfig("photo", URI.create("local:///topazproject#photo")));

So all you need to use in an Entity definition is the graph name. Example:

@Entity(graph="photo") 
class Photo {
...
...
}

With the above configuration, instances of the Photo entity will be persisted in the local:///topazproject#photo named graph.

Graphs

This annotation allows grouping of Graph annotations as shown above. This annotation can be at the class level or at the package level.

Id

This annotation denotes this property as the unique identifier for instances of an Entity. In RDF, this translates to the resource identifier URI. This means, this becomes the subject-uri while persisting the instances of this Entity.

In addition @Id annotation denotes the identifier field for Blobs and Views.

See also Entity, Predicate, Blob, GeneratedValue, View

Example usage:

@Entity   
class Product {
  ...
  ...
  String getId() {....}
  @Id
  @GeneratedValue(uriPrefix="topaz:Id/")
  void setId(String id) {...}
}

An alternate way to configure the @Id property is as follows:

   GUIDGenerator generator = new GUIDGenerator();
   generator.setUriPrefix(Rdf.topaz + "Id/");

   sessionFactory.addDefinition(new IdDefinition("Product:id", generator));

   Propert property = new Property(Product.class, "id");
   PropertyBinderFactory binder = new PropertyBinderFactory("Product:id", property);
   sessionFactory.getClassBinding("Product").addBinderFactory(binder);

Predicate

This annotation controls how a specific property in an Entity is persisted. (See Property Mapping) Most common usage is in defining a predicate-uri that represents this property. The other usage is by referencing configuration information from other such definitions.

It is also possible to apply this annotation on an overridden get or set method in a sub-class. See Property Overrides for details.

The attributes are (from the javadoc):

  • ref References another property configuration defined elsewhere by its name. For example an external configuration file can be used to define the predicate-uri and other attributes so that they need not be defined here. If this attribute is defined, then rest of the attributes in this annotation is treated as an override to the values defined in the reference.
  • uri Predicate uri. Defaults to the value from reference} or if reference is undefined, a URI is constructed by concatenating the value of @UriPrefix and the name of this property.
  • type The property type of this predicate uri. Defaults to the value specified in the reference. If no reference is supplied, it defaults to OBJECT for associations and URI or URL fields when the dataType() value is unspecified. Otherwise it defaults to DATA. Normally there is no need to configure this. The only place this is needed is to specify an OBJECT type for a field that is serializable. (See Mapping values)
  • dataType Data type for literals. Defaults to the value specified in the reference. If no reference is supplied, a default value is guessed based on this property's data type. Use UNTYPED for explicitly defining untyped literals. (See Mapping values)
  • graph The graph where this predicate is stored. Defaults to value defined in the containing Entity.
  • inverse Marks an inverse association. Instead of s p o, load/save as o p s where s is the Id for the containing Entity and p is the uri for this predicate and o the value of this field. Defaults to 'false' if no reference attribute is configured. (See Types of Mapping)
  • notOwned Marks the backing triples for this field as not owned by this entity and is therefore used only for load. Updates of the entity will skip the rdf statements corresponding to this property. By default all triples for a property are owned by the entity if no reference attribute is configured.
  • collectionType Collection Type of this property. Applicable only for arrays and java.util.Collection properties. Default is CollectionType.PREDICATE for collections when there is no reference configured. (See Handling cardinality and Rdf Collections and Containers)
  • cascade Cascading preferences for this field. Applicable only for associations. Default is CascadeType.peer when there is no reference configured. (See Transitive persistence)
  • fetch Fetch preferences for this field. Default is FetchType.lazy. Possible values are FetchType.lazy and FetchType.eager. It is only useful for associations and Rdf Collections and Containers. For all others the value is ignored (or rather, the effect is FetchType.eager). See also the Fetch options discussion for the advantages/disadvantages of lazy loading.

Example usage:

@Entity(...)
class Product {
  ...
  ...
  Float getPrice() {...}
  @Predicate(uri="topaz:price")
  void setPrice(Float price) {...}
}

An alternate way to configure the @Predicate property is as follows:

   RdfDefinition def;
   def = new RdfDefinition("Product:price", null, null, Rdf.topaz + "price",
                            Rdf.xsd + "float", null, null, null, null, null,
                            null, null, null, null);
                                    
   sessionFactory.addDefinition(def);

   Propert property = new Property(Product.class, "price");
   PropertyBinderFactory binder = new PropertyBinderFactory("Product:price", property);
   sessionFactory.getClassBinding("Product").addBinderFactory(binder);

Notice here that the name of the RdfDefinition object is Product:price. It is a concatenation of the entity name and property name with ':' as a separator. This is how Topaz generates names for properties mapped using this annotation. An application can rely on this naming scheme when referencing properties defined elsewhere. For example:

@Entity(...)
class PriceListEntry {
  ...
  ...
  Float getPrice() {...}
  @Predicate(ref="Product:price")
  void setPrice(Float price) {...}
}

will inherit all attributes for price property from the @Predicate annotation definition for price property in Product. Forward declarations of referenced properties are allowed and therefore it doesn't matter in which order the Product.class and PriceListEntry.class are scanned for Topaz annotations.

Default Literal Data Types

The following table lists the data type value guessed by Topaz Annotation parser while parsing the @Predicate annotation:

String.class
Boolean.class xsd:boolean
Boolean.TYPE xsd:boolean
Integer.class xsd:int
Integer.TYPE xsd:int
Long.class xsd:long
Long.TYPE xsd:long
Short.class xsd:short
Short.TYPE xsd:short
Float.class xsd:float
Float.TYPE xsd:float
Double.class xsd:double
Double.TYPE xsd:double
Byte.class xsd:byte
Byte.TYPE xsd:byte
URI.class xsd:anyURI**
URL.class xsd:anyURI**
Date.class xsd:dateTime
Calendar.class xsd:dateTime

** - These values are guessed only if the type value is set to 'DATA'. Otherwise by default the property type is guessed as OBJECT (in which case the data type value is unused.)

UriPrefix

This annotation allows a common URI prefix to be applied to uri attributes for @Predicate annotations in a class or subclassehs. This prefix does not get configured in the SessionFactory? - but only affects how a java class is processed.

Example usage:

@UriPrefix("http://example.org/RDF/")
@Entity
class Product {
....
...
  Float getPrice() {...}
  @Predicate
  void setPrice(Float price) {...}
}

This is equivalent of @Predicate(uri = "http://example.org/RDF/price") being applied to the setPrice() method.

Searchable

This annotation marks properties that are to be indexed for free-text searching.

Attributes are (from javadoc):

  • index The search index to use. In the case where the search indexes are stored in a graph, the is the name of the graph.
  • ref References another searchable configuration defined elsewhere. Similar to the ref attribute for @Predicate
  • uri Predicate uri. This is only used when using a store that stores the search indexes in a graph (e.g. using ItqlStore with Mulgara and the LuceneResolver). It defaults to the uri of the Predicate definition for the property this annotation is defined for, if there is one.
  • tokenize Whether the value should be tokenized or not. By default all values are tokenized.
  • analyzer The analyzer to use (only valid if tokenize() is true)
  • boost The boost to assign to this field.
  • preProcessor The pre-processor to use; by default the value is indexed as is.

Example usage:

@Entity
class Article {
  ...
  ...
  public String getTitle() {...}
  @Predicate(uri="dc:title")
  @Searchable(index="lucene")
  public void setTitle(String title) {...}

  ...
  ...

  public String getBody() {...}
  @Blob
  @Searchable(index="lucene", uri="topaz:body", preProcessor=org.topazproject.otm.search.XmlTagStripper.class)
  public void setBody(String body) {...}
}

Notice that @Searchable annotation can be applied to both @Predicate and @Blob fields. For @Blob fields, a URI must be configured when using the lucene resolver in Mulgara. Also notice the use of the XmlTagStripper? to remove xml tags before indexing. org.topazproject.otm.search contains other such default preProcessoers. You can also define your own by implementing the org.topazproject.otm.search.PreProcessor?' interface.

An alternate to defining a @Searchable annotation is to:

  SearchableDefinition def;
  def = new SearchableDefinition("Article:title", null, null, null, "lucene", null, null, null, null);

  sessionFactory.addDefinition(def);

  def = new SearchableDefinition("Article:body", null, null, Rdf.topaz + "body", "lucene", null, null, 
                                  null, XmlTagStripper.class);

  sessionFactory.addDefinition(def);

SubClassResolver

This annotation makes it convenient to register a sub-class resolver. Sub-class resolvers are necessary when the rdf:type alone is not sufficient to determine the right sub-class that Topaz should use when instantiating entity instances based on data loaded from the store.

Unlike the other annotations above, this annotation must be placed on a static method.

Example usage:

@Entity
class TextRepresentation extends Representation {

  ...
  ...


  /**
   * A {@link org.topazproject.otm.SubClassResolver} which detects Representation's with a
   * content-type of 'text/...' and selects this class instead.
   * @param superEntity       the root of the entity hierarchy that the resolved class
   *                          must be a sub-class-of
   * @param instantiatableIn  the entity mode that the resolved sub-class must be instantiatable in
   * @param sf                the session factory
   * @param typeUris          the rdf:type values found in the data
   * @param statements        the data
   * @return                  the resolved class or null
   */
  @SubClassResolver
  public static ClassMetadata resolve(ClassMetadata superEntity, EntityMode instantiatableIn,
                                      SessionFactory sf, Collection<String> typeUris,
                                      TripleStore.Result statements) {
    if (instantiatableIn != EntityMode.POJO ||
        !superEntity.isAssignableFrom(sf.getClassMetadata(Representation.class), EntityMode.POJO))
      return null;

    String p = ((RdfDefinition) sf.getDefinition("Representation:contentType")).getUri();

    if (statements.getFValues().get(p).get(0).startsWith("text/"))
      return sf.getClassMetadata(TextRepresentation.class);
    else
      return sf.getClassMetadata(Representation.class);
  }
}

An alternate to using this annotation:

  sessionFactory.addSubClassResolver("TextRepresentation", new SubClassResolver() {
      public ClassMetadata resolve(ClassMetadata superEntity, EntityMode instantiatableIn,
                                   SessionFactory sf, Collection<String> typeUris,
                                   TripleStore.Result statements) {
          return TextRepresentation.resolve(superEntity, instantiatableIn, sf, typeUris, statements)l
      }
    });

PredicateMap

Note: The definition and usage of this annotation is likely to change.

Projection

Note: The definition and usage of this annotation is likely to change.

Projection annotation maps a single projection variable on a #View or #SubView OQL query onto this POJO property. Attributes are:

  • value Specifies the projection variable name to map to this POJO property. By default the POJO property name is used as the projection variable name.
  • fetch Specifies the FetchType for this variable for entity fields. Default is eager.

SubView

Note: The definition and usage of this annotation is likely to change.

Annotation for sub-view classes. SubView's are classes which hold the results of subqueries in a #View's query. The fields of the annotated class must be marked using the @Projection annotation.

View

Note: The definition and usage of this annotation is likely to change.

Annotation for view classes. View's are expressed using OQL queries, with every field corresponding to an element in the query's projection list; the fields must be marked using the @Projection annotation. The OQL query must take exactly one parameter, and it must be named id; the result of running the query must be 0 or 1 rows. Furthermore, as with Entity one field must be marked @Id (in addition to the @Projection annotation). This makes a View behave similar to an Entity in that each instance has an id by which it can be retrieved (using Session.get()) and under which it is cached. This also allows View's to be used as fields in other View's and to be used as result objects in OQL and Criteria queries (they may not be dereferenced, though). View's may not be saved or deleted, however.

Subqueries may be used to fill in fields with type collection or array. If the subquery has only a single projection element then nothing further is needed and the projection element is directly converted to the array/collection's component type. If the subquery has more than one element in the projection list then the field's component type must be a class that has the @SubView annotation; @Projection annotations on the fields of that class are used as normal to tie the subquery's projection elements to that class' fields.

Example:

   @View(query = "select a.uri id, (select oi.uri pid, (select oi.representations from ObjectInfo oi2) reps from ObjectInfo oi where oi = a.parts order by pid) parts from Article a where a.uri = :id order by id;")
   class MyView {
     @Id @Projection("id")
     String getId() {...}
     void setId(String id) {...}

     @Projection("parts")
     List<MyPart> getParts() {...}
     void setParts(List<MyPart> parts) {...}
   }

   @SubView
   class MyPart {
     @Projection("pid")
     String getId() {...}
     void setId(String id) {...}

     @Projection("reps")
     Set<String> getRepresentations() {...}
     void setRepresentations(Set<String> reps) {...}
   }

   // You can register this view instance by:
   sessionFactory.preload(MyView.class);
   sessionFactory.preload(MyPart.class);
   sessionFactory.validate();

   // You can load an instance of the view by:
   MyView instance = session.get(MyView.class, id);
   ...