Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
Teneo/Hibernate/Dynamic EMF Tutorial
Contents
EMF allows you to dynamically change the in-memory ecore model by adding EPackages, EClasses and EStructuralFeatures. Teneo supports persisting dynamically created models. It is for example possible to read an ecore model from an ecore file (without generating code) and create the relational database schema. There are however some (practical) limitations, see the last section on this page.
The source code of this tutorial is available in the example project which you can download here.
This tutorial assumes some basic knowledge of EMF. If you are new to EMF then it can make sense to first try one of the core EMF tutorials which you can find here.
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 tutorial class described in the quick start tutorial to create a database for the Library example and some content.
- The initial setup of the quick start tutorial also applies here.
- When using a non-in-memory database: 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.
Running the tutorial
After downloading the examples project from cvs you can directly run the quick start program. Open the project and navigate to the Dynamic.java file and right click and select 'Run As > Java Application'. You will see several things getting printed to the console (one of them is the generated Hibernate mapping).
This run takes the following steps:
- reads the EPackage model from the generated EPackage file.
- generate a hibernate mapping for the EPackage
- create the database (on hsqldb)
- create EMF objects and persist them
- change the model
- re-initialize the datastore and update the relational database schema
- create and persist EMF objects adhering to the changed model
- query for these objects
- retrieve the EMF objects using an Hibernate EMF resource
Walk-through
Now let's walk through the code.
Quick start steps
The tutorial first calls the same code as described in the quick start tutorial, so it can make sense to first walk-through the quick start example. Further here only the additional steps are described.
Adapt the model
The following code extends the model with new types. It creates the SchoolBook EClass and sets the Book from the LibraryPackage as the supertype:
final EcoreFactory efactory = EcoreFactory.eINSTANCE; final EcorePackage epackage = EcorePackage.eINSTANCE; // 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 new EClass with a reference to the new SchoolBook EClass:
// 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);
Reconfigure Hibernate and the Relational Database Schema
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 new hibernate.hbm.xml System.err.println(hbds.getMappingXML());
At this point the database schema should have been updated.
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();
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.