Object API allows to work with POJOs that bind OrientDB documents. This API is not able work on top of Graph-API. If you are interested on using a Object-Graph mapping framework, look at the available ones that work on top of Graph-API layer: Object-Graph Mapping. |
---|
To use the Object APi include the following jars in your classpath:
orientdb-core-*.jar
orientdb-object-*.jar
If you're using the Object Database interface connected to a remote server (not local/embedded mode) include also:
orientdb-client-*.jar
orientdb-enterprise-*.jar
The OrientDB Object Interface works on top of the Document-Database and works like an Object Database: manages Java objects directly. It uses the Java Reflection to register the classes and Javassist tool to manage the Object-to-Document conversion. Please consider that the Java Reflection in modern Java Virtual Machines is really fast and the discovering of Java meta data is made only at first time.
Future implementation could use also the byte-code enhancement techniques in addition.
The proxied objects have a ODocument bounded to them and transparently replicate object modifications. It also allows lazy loading of the fields: they won't be loaded from the document until the first access. To do so the object MUST implement getters and setters since the Javassist Proxy is bounded to them. In case of object load, edit an update all non loaded fields won't be lost.
The database instance has an API to generate new objects already proxied, in case a non-proxied instance is passed it will be serialized, wrapped around a proxied instance and returned.
Read more about the Binding between Java Objects and Records.
Quick example of usage:
// OPEN THE DATABASE
OObjectDatabaseTx db = new OObjectDatabaseTx ("remote:localhost/petshop").open("admin", "admin");
// REGISTER THE CLASS ONLY ONCE AFTER THE DB IS OPEN/CREATED
db.getEntityManager().registerEntityClasses("foo.domain");
// CREATE A NEW PROXIED OBJECT AND FILL IT
Account account = db.newInstance(Account.class);
account.setName( "Luke" );
account.setSurname( "Skywalker" );
City rome = db.newInstance(City.class,"Rome", db.newInstance(Country.class,"Italy"));
account.getAddresses().add(new Address("Residence", rome, "Piazza Navona, 1"));
db.save( account );
// CREATE A NEW OBJECT AND FILL IT
Account account = new Account();
account.setName( "Luke" );
account.setSurname( "Skywalker" );
City rome = new City("Rome", new Country("Italy"));
account.getAddresses().add(new Address("Residence", rome, "Piazza Navona, 1"));
// SAVE THE ACCOUNT: THE DATABASE WILL SERIALIZE THE OBJECT AND GIVE THE PROXIED INSTANCE
account = db.save( account );
One of most common use case is to reuse the database avoiding to create it every time. It's also the typical scenario of the Web applications.
// OPEN THE DATABASE
OObjectDatabaseTx db= OObjectDatabasePool.global().acquire("remote:localhost/petshop", "admin", "admin");
// REGISTER THE CLASS ONLY ONCE AFTER THE DB IS OPEN/CREATED
db.getEntityManager().registerEntityClass("org.petshop.domain");
try {
...
} finally {
db.close();
}
The close() method doesn't close the database but release it to the owner pool. It could be reused in the future.
In the example above a database of type Database Object Transactional has been created using the storage: remote:localhost/petshop. This address is a URL. To know more about database and storage types go to Database URL.
In this case the storage resides in the same computer of the client, but we're using the remote storage type. For this reason we need a OrientDB Server instance up and running. If we would open the database directly bypassing the server we had to use the local storage type such as "plocal:/usr/local/database/petshop/petshop" where, in this case, the storage was located in the /usr/local/database/petshop folder on the local file system.
The OObjectDatabaseTx class is non thread-safe. For this reason use different OObjectDatabaseTx instances by multiple threads. They will share local cache once transactions are committed.
Starting from the release 0.9.19 OrientDB supports the Inheritance. Using the ObjectDatabase the inheritance of Documents fully matches the Java inheritance.
When registering a new class Orient will also generate the correct inheritance schema if not already generated.
Example:
public class Account {
private String name;
// getters and setters
}
public class Company extends Account {
private int employees;
// getters and setters
}
When you save a Company object, OrientDB will save the object as unique Document in the cluster specified for Company class. When you search between all the Account instances with:
SELECT FROM account
The search will find all the Account and Company documents that satisfy the query.
Before to use a database you need to open or create it:
// CREATE AN IN MEMORY DATABASE
OObjectDatabaseTx db1 = new OObjectDatabaseTx("memory:petshop").create();
// OPEN A REMOTE DATABASE
OObjectDatabaseTx db2 = new OObjectDatabaseTx("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.
To get the reference to the current user use:
OUser user = db.getUser();
Once finished remember to close the database to free precious resources.
db.close();
Please read the POJO binding guide containing all the information about the management of POJO.
The Object Database can be used totally in schema-less mode as long as the POJO binding guide requirements are followed. Schema less means that the class must be created but even without properties. Take a look to this example:
OObjectDatabaseTx db = new OObjectDatabaseTx("remote:localhost/petshop").open("admin", "admin");
db.getEntityManager().registerEntityClass(Person.class);
Person p = db.newInstance(Person.class);
p.setName( "Luca" );
p.setSurname( "Garulli" );
p.setCity( new City( "Rome", "Italy" ) );
db.save( p );
db.close();
This is the very first example. While the code it's pretty clear and easy to understand please note that we didn't declared "Person" structure before now. However Orient has been able to recognize the new object and save it in persistent way.
In the schema-full mode you need to declare the classes you're using. Each class contains one or multiple properties. This mode is similar to the classic Relational DBMS approach where you need to create tables before storing records. To work in schema-full mode take a look at the Schema APIs page.
The best practice to create a Java object is to use the OObjectDatabaseTx.newInstance() API:
public class Person {
private String name;
private String surname;
public Person(){
}
public Person(String name){
this.name = name;
}
public Person(String name, String surname){
this.name = name;
this.surname = surname;
}
// getters and setters
}
OObjectDatabaseTx db = new OObjectDatabaseTx("remote:localhost/petshop").open("admin", "admin");
db.getEntityManager().registerEntityClass(Person.class);
// CREATES A NEW PERSON FROM THE EMPTY CONSTRUCTOR
Person person = db.newInstance(Person.class);
person.setName( "Antoni" );
person.setSurname( "Gaudi" );
db.save( person );
// CREATES A NEW PERSON FROM A PARAMETRIZED CONSTRUCTOR
Person person = db.newInstance(Person.class, "Antoni");
person.setSurname( "Gaudi" );
db.save( person );
// CREATES A NEW PERSON FROM A PARAMETRIZED CONSTRUCTOR
Person person = db.newInstance(Person.class,"Antoni","Gaudi");
db.save( person );
However any Java object can be saved by calling the db.save() method, if not created with the database API will be serialized and saved. In this case the user have to assign the result of the db.save() method in order to get the proxied instance, if not the database will always treat the object as a new one. Example:
// REGISTER THE CLASS ONLY ONCE AFTER THE DB IS OPEN/CREATED
db.getEntityManager().registerEntityClass(Animal.class);
Animal animal = new Animal();
animal.setName( "Gaudi" );
animal.setLocation( "Madrid" );
animal = db.save( animal );
Note that the behaviour depends by the transaction begun if any. See Transactions.
for (Object o : database.browseCluster("CityCars")) {
System.out.println( ((Car) o).getModel() );
for (Animal animal : database.browseClass(Animal.class)) {
System.out.println( animal.getName() );
long cars = database.countClass("Car");
long cityCars = database.countCluster("CityCar");
Any proxied object can be updated using the Java language and then calling the db.save() method to synchronize the changes to the repository. Behaviour depends by the transaction begun if any. See Transactions.
animal.setLocation( "Nairobi" );
db.save( animal );
Orient will update only the fields really changed.
Example of how to update the price of all the animals by 5% more:
for (Animal animal : database.browseClass(Animal.class)) {
animal.setPrice(animal.getPrice() * 105 / 100);
database.save(animal);
}
If the db.save() method is called with a non-proxied object the database will create a new document, even if said object were already saved
To delete an object call the db.delete() method on a proxied object. If called on a non-proxied object the database won't do anything. Behaviour also depends by the transaction begun if any. See Transactions.
db.delete( animal );
Example of deletion of all the objects of class "Animal".
for (Animal animal : database.browseClass(Animal.class))
database.delete(animal);
Object Database uses JPA annotations to manage cascade deleting. It can be done expliciting (orphanRemoval = true) or using the CascadeType. The first mode works only with @OneToOne and @OneToMany annotations, the CascadeType works also with @ManyToMany annotation.
Example:
public class JavaCascadeDeleteTestClass {
...
@OneToOne(orphanRemoval = true)
private JavaSimpleTestClass simpleClass;
@ManyToMany(cascade = { CascadeType.REMOVE })
private Map<String, Child> children = new HashMap<String, Child>();
@OneToMany(orphanRemoval = true)
private List<Child> list = new ArrayList<Child>();
@OneToMany(orphanRemoval = true)
private Set<Child> set = new HashSet<Child>();
...
// GETTERS AND SETTERS
}
so calling
database.delete(testClass);
or
for (JavaCascadeDeleteTestClass testClass : database.browseClass(JavaCascadeDeleteTestClass.class))
database.delete(testClass);
will also delete JavaSimpleTestClass instances contained in "simpleClass" field and all the other documents contained in "children","list" and "test"
Since version 1.1.0 the Object Database provides attach(Object) and detach(Object) methods to manually manage object to document data transfer.
With the attach method all data contained in the object will be copied in the associated document, overwriting all existing informations.
Animal animal = database.newInstance(Animal.class);
animal.name = "Gaudi" ;
animal.location = "Madrid";
database.attach(animal);
database.save(animal);
in this way all changes done within the object without using setters will be copied to the document.
There's also an attachAndSave(Object) methods that after attaching data saves the object.
Animal animal = database.newInstance(Animal.class);
animal.name = "Gaudi" ;
animal.location = "Madrid";
database.attachAndSave(animal);
This will do the same as the example before
With the detach method all data contained in the document will be copied in the associated object, overwriting all existing informations. The detach(Object) method returns a proxied object, if there's a need to get a non proxied detached instance the detach(Object,boolean) can be used.
Animal animal = database.load(rid);
database.detach(animal);
this will copy all the loaded document information in the object, without needing to call all getters. This methods returns a proxied instance
Animal animal = database.load(rid);
animal = database.detach(animal,true);
this example does the same as before but in this case the detach will return a non proxied instance.
Since version 1.2 there's also the detachAll(Object, boolean) method that detaches recursively the entire object tree. This may throw a StackOverflowError with big trees. To avoid it increase the stack size with -Xss java option. The boolean parameter works the same as with the detach() method.
Animal animal = database.load(rid);
animal = database.detachAll(animal,true);
(Since 2.2)
When calling detachAll(object,true) on a large object tree, the call may become slow, especially when working with remote connections. It will recurse through every link in the tree and load all dependencies.
To only load parts of the object tree, you can add the @OneToOne(fetch=FetchType.LAZY) annotation like so:
public class LazyParent {
@Id
private String id;
@OneToOne(fetch = FetchType.LAZY)
private LazyChild child;
...
public class LazyChild {
@Id
private ORID id;
private String name;
public ORID getId() {
return id;
}
public void setId(ORID id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
In the above example, when calling detachAll(lazyParent,true), the child variable (if a link is available) will contain a normal LazyChild object, but only with the id loaded. So the name property will be null, as will any other property that is added to the class. The id object can be used to load the LazyChild object in a later stage.
Although OrientDB is part of NoSQL databases, supports the SQL engine, or at least a subset of it with such extensions to work with objects and graphs.
To know more about the SQL syntax supported go to: SQL-Query.
Example:
List<Animal> result = db.query(
new OSQLSynchQuery<Animal>("select * from Animal where ID = 10 and name like 'G%'"));
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.
The OObjectDatabaseTx implementation has APIs to get a document from its referencing object:
ODocument doc = db.getRecordByUserObject( animal );
In case of non-proxied objects the document will be a new generated one with all object field serialized in it.
The Object Database can also create an Object from a record.
Object pojo = db.getUserObjectByRecord(record);
Since version 1.5 the Object Database manages automatic Schema generation based on registered entities. This operation can be
- manual
- automatic
The ObjectDatabase will generate class properties based on fields declaration if not created yet.
Changes in class fields (as for type changing or renaming) types won't be updated, this operation has to be done manually
Schema can be generated manually for single classes or entire packages:
Version 1.6
db.getMetadata().getSchema().generateSchema(Foo.class); // Generates the schema for Foo class
db.getMetadata().getSchema().generateSchema("com.mycompany.myapp.mydomainpackage"); // Generates the schema for all classes contained in the given package
Version 1.5
db.generateSchema(Foo.class); // Generates the schema for Foo class
db.generateSchema("com.mycompany.myapp.mydomainpackage"); // Generates the schema for all classes contained in the given package
By setting the "automaticSchemaGeneration" property to true the schema will be generated automatically on every class declaration.
db.setAutomaticSchemaGeneration(true);
db.getEntityManager().registerClass(Foo.class); // Generates the schema for Foo class after registering.
db.getEntityManager().registerEntityClasses("com.mycompany.myapp.mydomainpackage"); // Generates the schema for all classes contained in the given package after registering.
class Foo could look like, generating one field with an Integer and ignoring the String field.
public class Foo {
private transient String field1; // ignore this field
private Integer field2; // create a Integer
}
Having the Foo class defined as following
public class Foo{
private String text;
private Child reference;
private int number;
//getters and setters
}
schema generation will create "text", "reference" and "number" properties as respectively STRING, LINK and INTEGER types.
The default schema management API equivalent would be
OClass foo = db.getMetadata().getSchema().getClass(Foo.class);
OClass child = db.getMetadata().getSchema().getClass(Child.class)
foo.createProperty("text",OType.STRING);
foo.createProperty("number",OType.INTEGER);
foo.createProperty("text",OType.LINK, child);
db.getMetadata().getSchema().save();
Since version 1.6 there's an API to synchronize schema of all registered entities.
db.getMetadata().getSchema().synchronizeSchema();
By calling this API the ObjectDatabase will check all registered entities and generate the schema if not generated yet. This management is useful on multi-database enviroments
Until the release 1.0rc9 the Object Database was implemented as the class com.orientechnologies.orient.db.object.ODatabaseObjectTx
. This class is deprecated, but if you want to continue to use it change the package to: com.orientechnologies.orient.object.db
.
This implementation and documentation refers to all ODatabaseObjectXXX deprecated classes.
The Orient Object DB works on top of the Document-Database and it's able to treat Java objects without the use of pre-processor, byte enhancer or Proxy classes. It uses the simpler way: the Java Reflection. Please consider that the Java reflection in modern Java Virtual Machines is really fast and the discovering of Java meta data is made at first time. Future implementation could use the byte-code enhancement techniques in addition.
Read more about the Binding between Java Objects and Records.
Quick example of usage:
// OPEN THE DATABASE
ODatabaseObjectTx db = new ODatabaseObjectTx ("remote:localhost/petshop").open("admin", "admin");
db.getEntityManager().registerEntityClasses("foo.domain");
// CREATE A NEW ACCOUNT OBJECT AND FILL IT
Account account = new Account()
account.setName( "Luke" );
account.setSurname( "Skywalker" );
City rome = new City("Rome", new Country("Italy"));
account.getAddresses().add(new Address("Residence", rome, "Piazza Navona, 1"));
db.save( account );
One of most common use case is to reuse the database avoiding to create it every time. It's also the typical scenario of the Web applications.
// OPEN THE DATABASE
ODatabaseObjectTx db= ODatabaseObjectPool.global().acquire("remote:localhost/petshop", "admin", "admin");
...
db.close();
The close() method doesn't close the database but release it to the owner pool. It could be reused in the future.
Starting from the release 0.9.19 OrientDB supports the Inheritance. Using the ObjectDatabase the inheritance of Documents fully matches the Java inheritance.
Example:
public class Account {
private String name;
}
public class Company extends Account {
private int employees;
}
When you save a Company object, OrientDB will save the object as unique Document in the cluster specified for Company class. When you search between all the Account instances with:
SELECT FROM account
The search will find all the Account and Company documents that satisfy the query.