HomeEMF HibernateEMF JDO/JPOXWeb App GenerationServices
 

Persisting Dynamic EMF Models

EMF allows you to dynamically change the in-memory ecore model by adding EPackages, EClasses and EStructuralFeatures. Teneo now supports persisting dynamically created models. There are however some (practical) limitations, see the bottom of this page.

Before starting this tutorial

Before starting this tutorial there are a number of things which you need to take into account:

  • It uses the QuickStart.java described in the quick start tutorial to create a database for the Library example and some content. So when running this tutorial you should also download the QuickStart.java file.
  • The prerequisites of the quick start tutorial also apply.
  • The tutorial assumes that there is an empty database with the name DynamicLibrary. When you rerun this tutorial you should make sure that this database is empty again (otherwise one of the smaller tests in the tutorial will fail).

In this tutorial the Library example is extended with two new types: SchoolBook (which inherits from Book) and Course which refers to a SchoolBook.

The source code of this tutorial can be downloaded here: Dynamic.java and QuickStart.java.

Step 1: Create the Dynamic Model

First call the relevant method in QuickStart.java to create a HbDataStore and some objects, next get some handles to the EMF base EPackage and EFactory.

// first do the quick start with the correct dbname
HbDataStore hbds = QuickStart.doQuickStart("DynamicLibrary");

final EcoreFactory efactory = EcoreFactory.eINSTANCE;
final EcorePackage epackage = EcorePackage.eINSTANCE;
			

Create the SchoolBook EClass and set the Book from the LibraryPackage as the supertype:

// create the SchoolBook EClass
EClass schoolBookEClass = efactory.createEClass();
schoolBookEClass.setName("SchoolBook");

// create a new attribute for this EClass
EAttribute level = efactory.createEAttribute();
level.setName("level");
level.setEType(epackage.getEInt());
schoolBookEClass.getEStructuralFeatures().add(level);

// Set the supertype of SchoolBook to the Book
schoolBookEClass.getESuperTypes().add(LibraryPackage.eINSTANCE.getBook());
			

Create a Course, a Course refers to a SchoolBook:

// create a course 
EClass courseEClass = efactory.createEClass();
courseEClass.setName("Course");

// give the Course a name 
EAttribute courseName = efactory.createEAttribute();
courseName.setName("courseName");
courseName.setEType(epackage.getEString());
courseEClass.getEStructuralFeatures().add(courseName);

// A course always uses one SchoolBook
EReference courseBook = efactory.createEReference();
courseBook.setName("courseBook");
courseBook.setEType(schoolBookEClass);
courseBook.setContainment(false);
courseEClass.getEStructuralFeatures().add(courseBook);
			

Create an EPackage and add the SchoolBook and Course EClass:

// Create a new EPackage and add the new EClasses 
EPackage schoolPackage = efactory.createEPackage();
schoolPackage.setName("elv");
schoolPackage.setNsPrefix("elv");
schoolPackage.setNsURI("http:///www.elver.org/School");
schoolPackage.getEClassifiers().add(courseEClass);
schoolPackage.getEClassifiers().add(schoolBookEClass);
EPackage.Registry.INSTANCE.put(schoolPackage.getNsURI(), schoolPackage);
			

Step 2: Reconfigure Hibernate and the Relational Database

The EPackage has been created now Hibernate should be reconfigured and the database schema should be updated.

// Now reset the epackages in the datastore, only required if you add an EPackage
hbds.setEPackages(new EPackage[]{LibraryPackage.eINSTANCE, schoolPackage});

// reinitialize hibernate and update the database schema
hbds.initialize();

// print the hibernate.hbm.xml
System.err.println(hbds.getMappingXML());
			

At this point the database schema should have been updated.

There is an option to prevent Teneo from updating the schema, this option should be disabled or not set, otherwise the database schema will not be updated (see here).

Step 3: Create and persist Dynamic EObject

The dynamic EMF objects can now be created. Because SchoolBook inherits from Book a real Book object is created:

// Now create an author, is used below 
Writer writer = LibraryFactory.eINSTANCE.createWriter();
writer.setName("Teacher");

// now create a schoolBook
// NOTE: because schoolBook inherits from Book, the create method will return a Book
Book bk = (Book)schoolPackage.getEFactoryInstance().create(schoolBookEClass);
bk.setAuthor(writer);
bk.setTitle("Biografie van Multatuli");
bk.setCategory(BookCategory.BIOGRAPHY);
bk.setPages(500);
bk.eSet(level, new Integer(1));

// and create a course
EObject course = schoolPackage.getEFactoryInstance().create(courseEClass);
course.eSet(courseName, "Dutch Literature Level 1");
course.eSet(courseBook, bk);

// now persist them all
Session session = hbds.getSession();
Transaction tx = session.getTransaction();
tx.begin();
session.save(writer);
session.save(course);
tx.commit();
			

Step 4: Query for dynamic EMF objects

The SchoolBook can be retrieved using a polymorphic query, for a Course the dynamic EAttribute can be used in the where clause.

// Now query for the books, one of them should be a SchoolBook
tx.begin();
Query qry = session.createQuery("from Book");
List list = qry.list();
Book schoolBook = null;
for (Iterator it = list.iterator(); it.hasNext();) {
	Book book = (Book)it.next();
	if (book.eClass() == schoolBookEClass) {
		if (schoolBook != null) {
			throw new Error("More than one schoolbook? Was the database not empty?");
		}
		schoolBook = book;
	}
}
if (schoolBook == null) {
	throw new Error("No schoolbook??");
}

// now query for all courses with the right name
qry = session.createQuery("from Course where courseName='Dutch Literature Level 1'");
list = qry.list();
EObject eobject = (EObject)list.get(0);
if (eobject.eClass() != courseEClass) {
	throw new Error("No Course?");
}

// the schoolBook should be the book of the course
Book courseBk = (Book)eobject.eGet(courseBook);
if (courseBk != schoolBook) {
	throw new Error("No schoolbook?");
} 
// and the dynamic feature level should be 1
if (((Integer)courseBk.eGet(level)).intValue() != 1) {
		throw new Error("Incorrect level?");
}
tx.commit();
session.close();
			

Limitations

  • It is not possible to create a containment relation between a dynamic model and a static model part. With static we mean the model for which java code has been generated.
  • When the model is dynamically changed it is required to update the database schema. To change the database schema database servers can lock the whole database. This is probably not practical in a live production environment.
  • A dynamic EClass can not have a composite-id.