Skip to content

Annotation Examples

clarkparsia edited this page Mar 22, 2011 · 2 revisions

Examples

The Basics

What your Java bean probably looks like now…

You might have the concept of a Person in your application, which might be represented like so:


public class Person {
	private String firstName;
	private String lastName;
	private String email;
	private URI homepage;
	private Image depiction;
	private int age;

	private Collection<Person> knows;

	// normal bean-style getters and setters...
}

If you’re using the class with something like Hibernate, it might look something like this:


@Entity
public class Person {

	private String firstName;
	private String lastName;
	private String email;
	private URI homepage;

	@OneToOne
	private Image depiction;

	@Transient
	private int age;

	@ManyToMany
	private Collection<Person> knows;

	// normal bean-style getters and setters...
}

This will get your Person instances into a single table with columns for each field, except age, maybe that’s being calculated during a pre/post persist phase. All of this probably looks very familiar.

Persistence with Empire

Now if we want to map this class to the concept foaf:Person and have it persisted by Empire into an RDF database, we just apply a few extra annotations:


@Entity
@Namespaces({"foaf", "http://xmlns.com/foaf/0.1/"})
@RdfsClass("foaf:Person")
public class Person implements SupportsRdfId {
	@RdfProperty("foaf:firstName")
	private String firstName;

	@RdfProperty("foaf:surname")
	private String lastName;

	@RdfId
	@RdfProperty("foaf:mbox")
	private String email;

	@RdfProperty("foaf:homepage")
	private URI homepage;

	@RdfProperty("foaf:depiction")
	private Image depiction;

	private int age;

	@RdfProperty("foaf:knows")
	private Collection<Person> knows;

	@RdfProperty("foaf:age")
	public int getAge() {
		// return the calculated age of the person
	}

	// normal bean-style getters and setters...
}

Note that I’ve left off some of the JPA annotations we don’t support, such as @ManyToMany and @OneToOne. You could leave these on the class and be able to use with a normal JPA solution and Empire at the same time, we just ignore the annotations that are not yet supported, and when we do support them, we’re not going to change the semantics, so there’s no harm in leaving them. But if you’re starting from scratch you’d only use the stuff supported by Empire.

Let’s go over a few of the significant annotations:

@Namespaces({"foaf", "http://xmlns.com/foaf/0.1/"})

This is an array of key-value pairs where the first value is the prefix of the namespace and the second is the URI. We provide this so you don’t have to put full URI’s into all the Empire tags. Since this is array, we encourage you to be careful and make sure the key’s and values are properly balanced or you may end up with namespace assertions you did not mean.

Alternatively, you can specify the prefix/namespace pairs in your empire configuration file (currently only supported in the Properties format):

# the value of 'ns_list' should be a comma separated list of keys, the values of which
# are the namespace URI's for those prefixes.
ns_list = foaf, empire
foaf = http://xmlns.com/foaf/0.1/
empire = http://empire.clarkparsia.com/

@RdfsClass("foaf:Person")

This simply says that all instances of this class are instances of foaf:Person; in the resulting RDF output for an instance of Person, you’d see a rdf:type triple whose object is foaf:Person.

This annotation is required in order for Empire to know what to turn your Java class into, and similarly, what Java class to create from a set of triples describing a resource. We also require @Entity like a normal JPA system, so remember to include both of these annotations on your classes.

implements SupportsRdfId

Empire does not support anonymous instances, so everything persisted by Empire must have an rdf:ID. The SupportsRdfId interface marks that a class can have an rdf:ID and provides a getter and setter to get and assign the rdf:ID of the object. We provide a default implementation of the interface SupportsRdfIdImpl which provides a basic write-once implementation that you can use in your code. You don’t have to assign the rdf:ID yourself, Empire will generate one and set the value of it on your class when it’s reading/writing data from the database.

@RdfId

@RdfId is a simple marker annotation which simply tells Empire what field to use for generating an rdf:ID for the class
if it does not already have one. @RdfId provides a single property, namespace(), which you can use to provide the
prefix for URI’s generated as the ID’s of individuals persisted by Empire. If you do not provide an @RdfId annotation
a random rdf:ID will be generated for your instances.

@RdfProperty(...)

This annotation tells Empire what property to use when saving, or reading, RDF data. In this example, the value of the field “lastName” will be persisted to a triple whose predicate is foaf:surname, and when the data is getting read back in, the object of the triple with the predicate of foaf:surname will get assigned to this field.

Empire enforces datatypes for literals, so fields using primitive types, such as long, short, boolean, etc. that correspond to xml datatypes will get serialized/deserialized using a datatype. You can disable this datatyping by setting EmpireOptions.STRONG_TYPING to false.

In the case where the value of the field is an Object, that Object must be persistable, i.e. it must be an @Entity, have an @RdfsClass anntotation, have an rdf:ID, etc.

When it’s a Collection, the collection must be of primitives which can be converted to literals, or Objects which are persistable.


	private int age;

	@RdfProperty("foaf:age")
	public int getAge() {
		// return the calculated age of the person
	}


Lastly, Empire works a little differently from other JPA implementations like Hibernate. Usually a field is persisted to the database no matter what, the presence of the @Basic annotation is implied. Empire is the opposite, the @Transient annotation is implied. So without the presence of the getter method with an annotation, age would not get persisted.

But this example serves to show that you can annotate a method and the value returned by that method will get persisted just as if the field itself was annotated. The field does not even need to exist in these cases. So you could calculate age on the fly from the current date and the person’s birthday and the value saved into their database would always be their current age.

The extras

@NamedGraph

If your database supports named graphs, this annotation gives you control over which named graph instances of the class are stored. There are two types of storage, NamedGraphType.Instance and NamedGraphType.Static. When the type is “Instance” each instance will be stored in it’s own named graph, when the value it static, you must provide a URI for the value field, and all instances of the class will be stored in the specified named graph.

Getting an Empire EntityManager.


PersistenceProvider aProvider = Empire.get().persistenceProvider();
EntityManager aManager = aProvider.createEntityManagerFactory("", new HashMap()).createEntityManager();

Queries

Currently, Empire supports both the SPARQL and SERQL query languages, just use whatever language is supported by the database you are using as the backend. The JPQL dialect is not supported, or any dialect of SQL, however, you can use the partial queries you are using to seeing in a normal JPA application. For example:


Query aQuery = mManager.createNativeQuery("from " +
                                          "{result} <" + SpaceVocab.ontology().agency + "> {??}," +
                                          "{result} <" + SpaceVocab.ontology().alternateName + "> {??altName}",
                                          Spacecraft.class);


This will return a set of Spacecraft instances which are bound to the “result” variable in the query. For now, you have to use the name “result” for what you want returned as the results of your query since we don’t yet support any of the mapping constructs.

You’ll also notice that parameters, both named and unnamed have a different syntax than in JPQL. Unamed parameters are marked by the token “??” while named parameters are marked by the same token followed by the name of the parameter; as the example shows “??altName”.

This is a departure from using ?1, ?2, ?3, etc for unnamed parameters and :name for named parameters in JPQL. This is because this clashes with the syntax for variables and qnames.

However, you still would bind the parameters to values in the same way:


        aQuery.setParameter(1, "U.S.S.R");
        aQuery.setParameter("altName", "00001");