To use the Document API include the following jars in your classpath:
orient-commons-*.jar
orientdb-core-*.jar
If you're using the Document Database interface connected to a remote server (not local/embedded mode) include also:
orientdb-client-*.jar
orientdb-enterprise-*.jar
The Orient Document DB is the base of higher-level implementation like Object-Database and Graph-Database. The Document Database API has the following features:
This is an example to store 2 linked documents in the database:
// OPEN THE DATABASE
ODatabaseDocumentTx db = new ODatabaseDocumentTx("remote:localhost/petshop").open("admin", "admin");
// CREATE A NEW DOCUMENT AND FILL IT
ODocument doc = new ODocument("Person");
doc.field( "name", "Luke" );
doc.field( "surname", "Skywalker" );
doc.field( "city", new ODocument("City").field("name","Rome").field("country", "Italy") );
// SAVE THE DOCUMENT
doc.save();
db.close();
This is the very first example. While the code is pretty clear and easy to understand please note that we haven't declared the type "Person" before now. When an ODocument instance is saved, the declared type "Person" will be created without constraints. To declare persistent classes look at the Schema management.
Before to execute any operation you need an opened database instance. You can open an existent database or create a new one. Databases instances aren't thread safe, so use one database per thread.
Before to open or create a database instance you need a valid URL. URL is where the database is available. URL says what kind of database will be used. For example memory: means in-memory only database, plocal: is for embedded ones and remote: to use a remote database hosted on an up & running DBServer OrientDB Server instance. For more information look at Database URL.
Database instances must be closed once finished to release precious resources. To assure it the most common usage is to enclose all the database operations inside a try/finally block:
ODatabaseDocumentTx db = new ODatabaseDocumentTx("plocal:/temp/test");
db.open("admin", "admin");
try {
// YOUR CODE
} finally {
db.close();
}
If you are using a remote storage (url starts with "remote:") assure the server is up & running and include the orientdb-client.jar file in your classpath.
The ODatabaseDocumentTx class is non thread-safe. For this reason use different ODatabaseDocumentTx instances by multiple threads. They will share the same Storage instance (with the same URL) and the same level-2 cache. For more information look at Multi-Threading with Java.
ODatabaseDocumentTx db = new ODatabaseDocumentTx ("plocal:/tmp/databases/petshop").create();
To create a database in a remote server you need the user/password of the remote OrientDB Server instance. By default the "root" user is created on first startup of the server. Check this in the file config/orientdb-server-config.xml, where you will also find the password.
To create a new document database called dbname on dbhost using filesystem storage (as opposed to in-memory storage):
new OServerAdmin("remote:dbhost")
.connect("root", "kjhsdjfsdh128438ejhj")
.createDatabase("dbname","document","local").close();
To create a graph database replace "document" with "graph".
To store the database in memory replace "local" with "memory".
ODatabaseDocumentTx db = new ODatabaseDocumentTx ("remote:localhost/petshop").open("admin", "admin");
The database instance will share the connection versus the storage. if it's a "local" storage, then all the database instances will be synchronized on it. If it's a "remote" storage then the network connection will be shared among all the database instances.
One of most common use cases is to reuse the database, avoiding to create it every time. It's also the typical scenario of the Web applications. Instead of creating a new ODatabaseDocumentTx instance all the times, get an available instance from the pool:
// OPEN THE DATABASE
ODatabaseDocumentTx db = ODatabaseDocumentPool.global().acquire("remote:localhost/petshop", "admin", "admin");
try {
// YOUR CODE
...
} finally {
db.close();
}
Remember to always close the database instance using the close()
database method like a classic non-pooled database. In this case the database will be not closed for real, but the instance will be released to the pool, ready to be reused by future requests. The best is to use a try/finally block to avoid cases where the database instance remains open, just like the example above.
By default OrientDB provide a global pool declared with maximum 20 instances. Use it with: ODatabaseDocumentPool.global()
.
To create your own pool build it and call the setup(min, max)
method to define minimum and maximum managed instances. Remember to close it when the pool is not more used. Example:
// CREATE A NEW POOL WITH 1-10 INSTANCES
ODatabaseDocumentPool pool = new ODatabaseDocumentPool();
pool.setup(1,10);
...
pool.close();
OrientDB can work in schema-full (like RDBMS), schema-less (like many NoSQL Document databases) and in schema-hybrid mode. For more information about the Schema look at the Schema page.
To use the schema with documents create the ODocument instance using the ODocument(String className)
constructor passing the class name. If the class hasn't been declared, it's created automatically with no fields. This can't work during transaction because schema changes can't be applied in transactional context.
Few NoSQL solutions supports security. OrientDB does it. To know more about it look at Security.
To manage the security get the Security Manager and use it to work with users and roles. Example:
OSecurity sm = db.getMetadata().getSecurity();
OUser user = sm.createUser("god", "god", new String[] { "admin" } );
To get the reference to the current user use:
OUser user = db.getUser();
ODocument instances can be saved by calling the save() method against the object itself. Note that the behaviour depends on the running transaction, if any. See Transactions.
ODocument animal = new ODocument("Animal");
animal.field( "name", "Gaudi" );
animal.field( "location", "Madrid" );
animal.save();
for (ODocument doc : database.browseCluster("CityCars")) {
System.out.println( doc.field("model") );
for (ODocument animal : database.browseClass("Animal")) {
System.out.println( animal.field( "name" ) );
long cars = database.countClass("Car");
v= Count records of a cluster ==
long cityCars = database.countCluster("CityCar");
Although OrientDB is part of the NoSQL database community, it supports a subset of SQL that allows it to process links to documents and graphs.
To know more about the SQL syntax supported go to: SQL-Query.
Example of a SQL query:
List<ODocument> result = db.query(
new OSQLSynchQuery<ODocument>("select * from Animal where ID = 10 and name like 'G%'"));
OrientDB supports asynchronous queries. The result is not collected and returned like synchronous ones (see above) but a callback is called every time a record satisfy the predicates:
database.command(
new OSQLAsynchQuery<ODocument>("select * from animal where name = 'Gipsy'",
new OCommandResultListener() {
resultCount = 0;
@Override
public boolean result(Object iRecord) {
resultCount++;
ODocument doc = (ODocument) iRecord;
// DO SOMETHING WITH THE DOCUMENT
return resultCount > 20 ? false : true;
}
@Override
public void end() {
}
})).execute();
Asynchronous queries are useful to manage big result sets because don't allocate memory to collect results.
Prepared query are quite similar to the Prepared Statement of JDBC. Prepared queries are pre-parsed so on multiple execution of the same query are faster than classic SQL queries. Furthermore the pre-parsing doesn't allow SQL Injection. Note: prepared queries (parameter substition) only works with select statements (but not select statements within other types of queries such as "create vertex").
Prepared query uses two kinds of markers to substitute parameters on execution:
? is positional parameter
:<par> is named parameter
Example of positional parameters:
OSQLSynchQuery<ODocument> query = new OSQLSynchQuery<ODocument>("select from Profile where name = ? and surname = ?");
List<ODocument> result = database.command(query).execute("Barack", "Obama");
Example of named parameters:
OSQLSynchQuery<ODocument> query = new OSQLSynchQuery<ODocument>("select from Profile where name = :name and
surname = :surname");
Map<String,Object> params = new HashMap<String,Object>();
params.put("name", "Barack");
params.put("surname", "Obama");
List<ODocument> result = database.command(query).execute(params);
OrientDB is a graph database. This means that traversing is very efficient. You can use this feature to optimize queries. A common technique is the Pivoting.
To execute SQL commands use the command()
method passing a OCommandSQL object:
int recordsUpdated = db.command(
new OCommandSQL("update Animal set sold = false")).execute();
See all the SQL Commands.
Traversing is the operation to cross documents by links (relationships). OrientDB is a graph database so this operation is much much more efficient than executing a JOIN in the relational databases. To kwow more about traversing look at the Java traverse API.
The example below traverses, for each movies, all the connected records up to the 5th depth level.
for (OIdentifiable id : new OTraverse()
.field("in").field("out")
.target( database.browseClass("Movie").iterator() )
.predicate(new OCommandPredicate() {
public boolean evaluate(ORecord<?> iRecord, OCommandContext iContext) {
return ((Integer) iContext.getVariable("depth")) <= 5;
}
})) {
System.out.println(id);
}
Any persistent document can be updated using the Java API then calling the db.save() method, or by calling save() method against the document to synchronize the changes to the repository. Behaviour depends on the transaction begun if any. See Transactions.
animal.field( "location", "Nairobi" );
animal.save();
OrientDB will update only the fields really changed.
Example of how to increase the price of all the animals by 5%:
for (ODocument animal : database.browseClass("Animal")) {
animal.field( "price", animal.field( "price" ) * 105 / 100 );
animal.save();
}
To delete a document call the delete() method against the document instance loaded. Behaviour depends by the transaction begun if any. See Transactions.
animal.delete();
Example of deletion of all the documents of class "Animal".
for (ODocument animal : database.browseClass("Animal"))
animal.delete();
Transactions are a practical way to group a set of operations all together. OrientDB supports ACID transactions so or all the operations succeed or no one. The database remains always consistent. For more information look at Transactions.
Transactions are managed at database level. No nested transactions are supported. A database instance can have only one transaction running. The database's methods to handle transactions are:
begin()
to start a new transaction. If a transaction was already running, it's rollbacked and a new one is beguncommit()
makes changes persistent. If an error occurs during commit the transaction is rollbacked and a OTransactionException exception is raisedrollback()
abort a transaction. All the changes will be lostCurrent release only supports the OPTIMISTIC transaction where no lock is kept and all is checked at commit time. This improves concurrency but could throw a OConcurrentModificationException
exception in case involved records were modified by concurrent clients/threads. In this case the client code can reload updated records and repeat the transaction.
Optimistic transaction keeps all the changes in memory at client-side level, so if you're a remote storage no messages are sent to the server until commit where all the changes will be transferred in block. This reduce network latency, speed-up the execution and increase the concurrency level. This is a big difference with the Relational DBMS where, during a transaction, changes are early sent to the server.
Transactions are committed only when the commit()
method is called and no errors occur at commit time. The most common usage of transactions is to enclose all the database operations inside a try/finally block. On database closing ("finally" block) if a pending transaction is running it will be rollbacked qutomatically. Look at this example:
ODatabaseDocumentTx db = new ODatabaseDocumentTx(url);
db.open("admin", "admin");
try {
db.begin();
// YOUR CODE
db.commit();
} finally {
db.close();
}
Even though you can use Indexes through SQL the best and most efficient way is using the Java API.
The main class to work against indexes is the IndexManager. To get the implementation of the IndexManager use:
OIndexManager idxManager = database.getMetadata().getIndexManager();
The Index Manager allows to manage the index life-cycle such as creation, deletion and retrieving of an index instance. The most common usage is against a single index. Get the reference to the index by using:
OIndex<?> idx = database.getMetadata().getIndexManager().getIndex("Profile.name");
Where "Profile.name" is the index name. Note that by default OrientDB assigns the name as <class>.<property>
for automatic indexes created against a class's property.
The OIndex interface it's similar to a Java Map and provides methods to get, put, remove and count items. Examples of retrieving records using a UNIQUE index against the name and a NOTUNIQUE index against the gender:
OIndex<?> nameIdx = database.getMetadata().getIndexManager().getIndex("Profile.name");
// THIS IS A UNIQUE INDEX, SO IT RETRIEVES A OIdentifiable
OIdentifiable luke = nameIdx.get( "Luke" );
if( luke != null )
printRecord( (ODocument) luke.getRecord() );
OIndex<?> genderIdx = database.getMetadata().getIndexManager().getIndex("Profile.gender");
// THIS IS A NOTUNIQUE INDEX, SO IT RETRIEVES A Set<OIdentifiable>
Set<OIdentifiable> males = genderIdx.get( "male" );
for( OIdentifiable male : males )
printRecord( (ODocument) male.getRecord() );
While automatic indexes are managed automatically by OrientDB hooks, the manual indexes can be used to store any value. To create a new entry use the put()
:
OIndex<?> addressbook = database.getMetadata().getIndexManager().getIndex("addressbook");
addressbook.put( "Luke", new ODocument("Contact").field( "name", "Luke" );