Domain Mapping - Wolfgang-Schuetzelhofer/jcypher GitHub Wiki
Previous | Next | Table of Contents |
JCypher allows to map complex business domains to graph databases. You can take an arbitrarily complex graph of domain objects (pojos (plain old java objects)) and store it in a straight forward way into a graph database for later retrieval. You do not need to modify your domain object classes in any way. You even do not add annotations.
Moreover JCypher provides a default mapping so you don't have to write a single line of mapping code or of mapping configuration.
The only requirement (and that may be loosened in the future) is, that domain objects must have a default constructor (i.e. a public no-argument constructor).
When accessing a domain you work against a slim interface: IDomainAccess
. Most importantly IDomainAccess
provides methods to store and to retrieve domain objects or graphs of domain objects respectively. You create instances of IDomainAccess
by means of the factory class DomainAccessFactory
. The root package for domain access related classes is iot.jcypher.domain
.
IDBAccess dbAccess = // create an IDBAccess
// a domain name is required and must be unique within a database
String domainName = "PEOPLE-DOMAIN";
// create an IDomainAccess
IDomainAccess domainAccess =
DomainAccessFactory.createDomainAccess(dbAccess, domainName);
When you store a domain object in a graph database it is mapped as follows:
- The domain object is mapped to a node of the graph (call this node n_0).
- The node-label is derived from the domain object's type.
- Every attribute of the domain object which is of simple type (String, Number, Boolean, Date) is mapped to a property of the node.
- Every Attribute of the domain object which is of complex type is considered a domain object in turn and is appropriately mapped to a node (call this node n_1).
- Node n_0 is then connected to node n_1 via a relation.
In that way, starting with the first domain object, the entire object graph is navigated and appropriately stored to the graph database.
Navigation stops at leafs (i.e. objects which have at most attributes of simple type) or when a loop (cycle) in the object graph is detected.
You can store multiple domain models in a single database. When you do that, all but the first domain are mapped so that every domain object node has an additional label derived from the domain name. That is the default behaviour. If you want to have object nodes of the first domain also being stored with additional domain name labels, you have to use another factory method to create the IDomainAccess
.
// create an IDomainAccess
IDomainAccess domainAccess =
DomainAccessFactory.createDomainAccess(dbAccess, domainName,
DomainLabelUse.ALWAYS);
You can store a single domain object or the object graph rooted by this object respectively in a straight forward way.
Person person = // create a domain object
// Imagine the person has addresses, friends, parents,
// is related to a company...
// Starting with person you have an entire object graph
IDomainAccess domainAccess = // create an IDomainAccess
// Store the domain object,
// the entire object graph rooted by the domain object
// is mapped to the graph database.
List<JcError> errors = domainAccess.store(person);
if (errors.size() > 0)
// handle errors
You can also store lists of domain objects.
List<Person> persons = // create a list of domain objects.
// Store the list of domain objects,
List<JcError> errors = domainAccess.store(persons);
if (errors.size() > 0)
// handle errors
For every domain object stored in the graph database you can retrieve a SyncInfo
object, which you can ask for the object's id (i.e. the id of the node in the graph to which the object was mapped). Again you can retrieve the SyncInfo
for a single domain object or for a list of domain objects.
// for a single domain object
SyncInfo syncInfo = domainAccess.getSyncInfo(person);
long personId = syncInfo.getId();
// for a list of domain objects
List<SyncInfo> syncInfos = domainAccess.getSyncInfos(persons);
When you want to retrieve domain objects from a domain stored in a graph database you currently have two ways to do it. You can retrieve domain objects by their ids (in case you know the ids), or you can retrieve domain objects by type.
long[] ids = // an array of domain object ids
// Load a list of domain objects from the graph database.
// By means of the second parameter you can specify
// the resolution depth.
// 0 would mean you only load simple attributes into the objects
// (i.e. attributes which have been stored
// to a node property; strings, numbers, booleans, or dates).
// Complex attributes are stored in separate nodes
// accessed via relations.
// So with the second parameter you can specify how deep exactly
// the graph should be navigated when loading objects.
// -1 means resolution as deep as possible (until leafs of the graph,
// i.e. objects which at most have simple attributes, are reached
// or until a cycle (loop) is detected).
List<Object> domainObjects =
domainAccess.loadByIds(Object.class, -1, ids);
// or you can do it for a single id
Person person =
domainAccess.loadById(Person.class, -1, personId);
Note: When you know the type of the domain object(s) to load, you specify it as the first parameter in the method call (see second call above). If you don't know the type, you can at any time use Object.class
.
Load by type:
// All objects of the specified type or any subtype of it
// will be loaded from the domain graph and returned in a list.
// You can even use 'Object.class' in which case
// all domain objects in the graph will be returned.
// Again you can specify the resolution depth.
// Additionally, you can specify an offset (parameter 3)
// and a count (parameter 4) of objects to be returned
// with respect to a list containing the total number of
// objects of the specified type.
// This feature provides the possibility to do pagination.
// Offset 0 and count -1 will return a list of all objeccts
// of the specified type.
// To really make use of the pagination feature you need
// to know the total number of objects of a certain type.
// There is an interface method which
// provides this information. We will see that soon.
List<AbstractPerson> domainObjects =
domainAccess.loadByType(AbstractPerson.class, -1, 0, -1);
Retrieving the number of instances for a given type:
// You can query the total number of instances
// of a certain type stored in the domain graph.
// The number of stored instances of the specified type
// and of all its subtypes is returned.
// Note: Specifying 'Object.class' will therefore return the
// total number of domain objects stored in the domain graph
long num = domainAccess.numberOfInstancesOf(AbstractPerson.class);
By means of the class DomainInformation
you can retrieve general informations about one or more domains.
// the static method answers a list of names
// of domains which have been stored to the
// graph database.
// the graph database is identified by the dbAccess parameter
List<String> available =
DomainInformation.availableDomains(dbAccess);
// create a DomainInformation object
// for a distinct domain
String domainName = "PEOPLE-DOMAIN";
di = DomainInformation.forDomain(dbAccess, domainName);
// retrieve a list of DomainObjectType(s)
// of all domain objects that have been stored
// in the domain
List<DomainObjectType> types = di.getDomainObjectTypes();
// alternatively you can retrieve a list
// of names of DomainObjectTypes
List<String> typeNames = di.getDomainObjectTypeNames();
DomainObjectType type = types.get(0);
// You can ask a DomainObjectType for its type name
// i.e. the fully qualified name of the java type
String typeName = type.getTypeName();
// You can ask a DomainObjectType for the
// label of nodes to which domain objects
// of that type are mapped
String label = type.getNodeLabel();
// You can retrieve the java type (Class)
// from a DomainObjectType.
// Note: this may raise a ClassNotFoundException
Class<?> jType = type.getType();
Previous | Next | Table of Contents |