Feature Tour 1: Object Persistency
Transparent persistency for object-oriented programming languages has been discussed for a few decades now. Anyhow, we are confident that the end of the story has not been reached yet. Please have a close look at OOMEGA's persistency concepts and see for yourself how much time could be saved due to automatic code generation for the persistency domain.
- Extended data modelling concepts
- Easy and comfortable Java Model Access API
- Model-based query language
- Nested transactions
- Event notification mechanism
- Cascading delete concept
- Reflection API
- Fast binary and XML serialisation
- Exchangeability and compatibility of persistency layers
- Support for Versant, Hibernate, db4objects and MemoryDB
Extended data modelling concepts
It's easy to model with OOMEGA. For example you can use our textual metamodelling language SDL.
@entity (cid = 1002) class Person
{
@attribute @notnull String firstname;
@attribute @notnull String lastname;
@attribute Address birthPlace;
@association (foreign="children", mult="0..2") Set<Person> parents;
@association (foreign="parents") Set<Person> children;
@association (foreign="spouse") Person spouse;
public String toString() {
return getName()!=null ? getName() : "<no name>";
}
}
Our metamodelling concepts are powerful. You can create
- user-defined attribute classes (cp. Address type of field birthplace)
- fine-granular multiplicities (cp. field parents),
- bidirectional associations (cp. fields parents and children),
- self-connected associations (cp. field spouse),
- and others like compositions, methods and class inheritance.
Please note that methods are implemented in Java. Anyhow, the implementation can rely on automatically generated getter and setter methods (cp. getName).
Easy and comfortable Java Model Access API
The generic code generator is already pre-configured for the persistency domain - thus it creates ready-made and deployable code for various persistency solutions.
In order to interact with any of these persistency layers, there's a standardised, easy and comfortable Java Model Access API available.
// initialise an in-memory database and obtain a session to it EOContainerSession session = MemoryEOC.createEOC(100).getSession(); // create an attribute object Address address = Address( "Munich", 80809, "Germany" ); // create an entity object Person person = session.insert( Person() ); person.setFirstname( "Christian" ); person.setLastname( "Merenda" ); person.setBirthPlace( address ); // commit the transaction session.commit(); // delete the previously created entity object and commit session.delete( person ); session.commit();
Certainly - the API possesses more sophisticated features like nested transactions and an event notification mechanism. Anyhow, the basic interaction is very simple, isn't it?
Model-based query language
A well designed database API asks for a comfortable and powerful query language. OOMEGA has not only designed a query language. It's more than that! It is a metamodel.
The model-based query language consists of
- a query metamodel (= abstract syntax) and
- an elegant integration into the Java world (= concrete syntax).
In contrast to other object-oriented query languages on the market, OOMEGA allows for navigation beyond collection-valued path expressions. Moreover OOMEGA does not only support predicates, i.e. the FROM and WHERE clauses, but also handles SELECT, GROUP BY, HAVING and ORDER BY statements. Please have a look at the following example query and notice the set comparison:
Query query = Query(
select( P(Person.P.firstname) ),
distinct( true ),
from( Person.CID ),
where(
eq(
nav( P(Person.P.children), P(Person.P.firstname) ),
VSet( "Christian", "Stefano" )
)
)
);
The shown statement can be send to the database with a simple method call.
session.execute( query );
Nested transactions
CREATE, READ, UPDATE and DELETE (CRUD) operations are always executed in the context of a transaction. Transaction execution is controlled with the following four API methods.
public TXContext begin(); public void prepare() throws ConstraintViolationException; public void commit() throws ConstraintViolationException; public void rollback();
Please notice, that the begin() method needn't be called explicitely at a transaction boundary (commit / rollback). Thus the begin() method solely serves the purpose of starting a nested transaction. Therefore you are able to set savepoints within a running transaction.
// initialise a file-based database and obtain a session to it
EOContainerSession session =
FileEOC.createEOC(new File("feature.sdf"), 100).getSession();
// create an entity object
Person person = session.insert( Person() );
// start a nested transaction
TXContext nestedTX = session.begin();
// make some change in the context of the nested transaction
person.setFirstname( "Christian" );
// commit the nested transaction
nestedTX.commit();
// rollback the main transaction
session.rollback();
Event notification mechanism
Another great feature is OOMEGA's incorporated event notification mechanism: you are able to observe every entity object of your choice.
// an observer must only implement the setterCallback method
public class DemoObserver implements EOObserver
{
public <T> void setterCallback(EntityObjectTX caller,
Property<T> field, Collection<T> oldValue)
{
System.out.println("Entity has been modified in field '" +
field.toString() + "'");
}
}
Now, you can attach the observer to entity objects as it is shown below.
// create an entity object Person person = session.insert( Person() ); // create and attach the observer EOObserver observer = new DemoObserver(); person.attachObserver(observer); // change the birthday of the observed entity person.setBirthDay( new java.util.Date() );
This will lead to the following console output.
Entity has been modified in field 'birthDay'
Cascading delete concept
An elegant cascading delete concept reduces the amount of explicit object deletions in your source code and helps to keep the database clean of dispensable objects.
We distinguish strong and weak entity classes:
- Strong entity classes are never subject of cascading deletes. As long as every entity class is a strong entity class, no object is deleted automatically by the container.
- Weak entity classes may be subject of cascading deletes. If a weak entity object is not part of exactly one composite object, it will be deleted automatically.
Reflection API
Apart from the standard Java Model Access API, OOMEGA offers dynamic access to objects whose structure is not known until runtime execution. Accordingly every object has dynamic getter and setter methods.
Dynamic access is best shown with a simple example. Let's query for the name of Christian's spouse.
// locate christian - the person object - first
Person christian = (Person) session.execute(
Query(
from( Person.CID ),
where(
like( P(Person.P.firstname), "Chris*" )
)
)
).iterator().next();
// create the path expression of interest
Edge edge = nav( P(Person.P.spouse), P(Person.P.firstname) );
// apply the path expression to the object 'christian'
System.out.println( christian.evaluate(edge) );
This will lead to the following console ouput.
[Mona-Maria]
Please note, that path expressions can be created dynamically, too.
Fast binary and XML serialisation
Compared to the standard Java serialisation OOMEGA's binary codec SDF (Structured Data Format) reduces the total amount of data to a third and the serialisation/deserialisation process is actually three times faster.
For both binary and XML serialisation corresponding sources and sinks for OOMEGA's streaming API are provided. They are called SDFDecoder and SDFCoder for the binary serialisation and SDMLDecoder and SDMLCoder for the XML serialisation.
To convert from the binary to the XML format, just connect the appropriate coder and decoder with an active pipe.
// create a decoder for the binary format SDF
in = new SDFDecoder(new FileInputStream("persons.sdf"),
session.getEOC().getClassDirectory());
// create a coder for the XML format
out = new SDMLCoder(new FileOutputStream("persons.xml"));
// connect the decoder and coder with an active pipe
new ActivePipe<EntityObject>(out,in).run();
// close the coder
out.close();
By the way - an entity object container can be used as input or output stream as well. Thus it is easy to stream the contents of a SDF or XML file into a database.
Exchangeability and compatibility of persistency layers
Every persistency layer is controlled with exactly the same metamodelling language and API. You can deploy applications to different database systems and seamlessly interconnect databases from different vendors.
Besides you will profit from stand-alone development with OOMEGA MemoryDB and the ability to switch to a heavy-weight database system in production environments.
Support for Versant, Hibernate, db4objects and MemoryDB
OOMEGA supports various persistency solutions out-of-the-box.
- Versant Object Database: The Versant binding is suitable for high performance object persistence in central data repositories.
- Hibernate: We do also support Hibernate to store objects in existing relational databases.
- db4objects: db4objects is supported to persist objects in an embedded database system.
- MemoryDB: Finally OOMEGA offers an in-memory/file-based object database which is called MemoryDB.