EMap – Map your EMF to an SQL-Database


There are various technologies available for mapping EMF-objects to SQL databases, like the probably most established ones in the Eclipse ecosystem (both developed by Martin Taal)

  • Teneo
  • Texo

We more than contentendly used these two in quite some projects until we had – at the end of 2013 – to rethink our RCP architecture due to various (most of all performance) challenges with teneo (or more precisely hibernate).

I want to point out that these technical issues we faced were not caused by design problems of hibernate, but origin from the different lifespans of sessions to be handled – application servers with short living sessions on the one hand and RCP with typically fairly long running sessions.

Anyways this blog post is not about discussing why we left Teneo & Hibernate behind and developed our own EMF to SQL mapper named EMap but introducing this new mapping choice.

What is EMap

On first sight EMap is a DSL allowing you to:

  • define how an EMF entity is mapped to an SQL table
  • define how to retrieve EMF-Entities from your SQL-Database

But going deeper into it … EMap is a lot more, as the following example illustrates.

Let’s assume we have an EMF entity like the following:

interface TreeItem {
  public long getId();
  public void setId(long id);
  public String getName();
  public String setName(String name);
  public List<TreeItem> getChildren(); // containment, opposite of parent
  public TreeItem getParent(); // opposite of children
}

This would be mapped into a SQL database like this:

====================================
| T_ID | T_NAME     | T_REF_PARENT |
====================================
|  1   | Root 1     | NULL         |
------------------------------------
|  2   | Root 2     | NULL         |
------------------------------------
|  3   | Child 1.1  | 1            |
------------------------------------
|  4   | Child 1.2  | 1            |
------------------------------------
|  5   | Child 2.1  | 2            |
------------------------------------
| ....

The EMaping for that would look like this:

package sample;

entity TreeItem
{
  etype "http://www.bestsolution.at/emap/sample"#TreeItem
  attributes {
    primarykey id => T_ID 
      generatedby { 
        "Oracle" seqnext "SEQ_ID_TREEITEM", 
        "h2" autokey 
      }
    name          => T_NAME
    children      => 
      resolve TreeItem.selectChildren(T_ID)
    parent        => 
      resolve TreeItem.selectById(T_REF_PARENT)
  }
  queries {
    single selectById(long id) {
      default TreeItem FROM 
        '"TREE_ITEM"' WHERE '"T_ID" = ${id}'
    },
    selectAllItems() {
      default TreeItem FROM '"TREE_ITEM"'
    }
    selectChildren(long parentId) {
      default TreeItem FROM 
        '"TREE_ITEM"' WHERE '"T_REF_PARENT" = ${parentId}'
    },
    selectRootItems() {
      default TreeItem FROM 
        '"TREE_ITEM"' WHERE '"T_REF_PARENT" IS NULL'
    }
  }
}

And having this in EMap brings along the following stuff – automatically generated:

  • DDL file to create the SQL tables
  • a set of SQL files holding the named SQL queries
  • a set of Java interfaces & classes allowing you to
    retrieve, insert, update and delete entities

Most important here are the Java interfaces which look as follows for our TreeItem:

interface TreeItemMapper 
  extends at.bestsolution.persistence.ObjectMapper<TreeItem> {
   TreeItem selectById(long id);
   List<TreeItem> selectChildren(long parentId);
   List<Treeitem> selectRootItems();

   List<Treeitem> selectAllItems();
   MappedQuery<TreeItem> selectAllItemsMappedQuery();

   public static class Expression {
     public static final 
      TreeItemMapper.ExpressionStringExpressionFactory<TreeItem> name();
     public static final 
      LongExpressionFactory<TreeItem> id();
     public static final 
      LongExpressionFactory<TreeItem> parent_fk();

     public static final TreeItemMapper.Join<TreeItem> parent();
     public static final TreeItemMapper.Join<TreeItem> children();
   }

   static class Join<O> {
     StringExpressionFactory<O> name();
     LongExpressionFactory<O> id();
     LongExpressionFactory<O> parent_fk();
   }

   static class Order {
     OrderColumnFactory<TreeItem> id();
     OrderColumnFactory<TreeItem> name();
   }
}

// at.bestsolution.persistence.ObjectMapper
interface ObjectMapper<O> {
  void update(O object);
  void insert(O object);
  void delete(O object);
  // ....
}

who have the API you’ll use in your code to interact with the backend store.

Access data in your application

At runtime only the generated artifacts are needed so you don’t get extra dependencies e.g. on Xtext, … beside naturally some emap components who do the mapping.

EMap is fully integrated into OSGi and makes use of the OSGi-Service registry to e.g. retrieve the configuration to access datasources but it publishes itself as well into the OSGi-Service-Registry.

Let’s assume you would have an e4 component with which you would like to present the TreeItem structure. What would you have to do?

import static ...TreeItemMapper.Expression

public class MyView {
   private Session session;

   @Inject
   public MyView(SessionFactory factory) {
     session = factory.createSession();
   }

   private void printTree2Level() {
      TreeItemMapper m = 
        session.createMapper(TreeItemMapper.class);

      for( TreeItem r : m.selectRootItems() ) {
        System.out.println("+ " + r.getName());
        for( TreeItem c : r.getChildren() ) {
          System.out.println("  + " + c.getName());
        }
      }
   }

   private void findAllItemsNameStartWith(String namePrefix) {
     TreeItemMapper m = 
       session.createMapper(TreeItemMapper.class);

     MappedQuery<TreeItem> q = m.selectAllItemsMappedQuery();
     Expression<TreeItem> filter = 
       name().like(namePrefix+"%");

     q.where(filter).list().map( (i) -> i.getName() );
   }

   private void renameItem(TreeItem item) {
     String name = ...;
     TreeItemMapper m = 
       session.createMapper(TreeItemMapper.class);
     item.setName(newName);
     session.runInTransaction( (s) -> { 
       m.update(item); return true; 
     } );
   }

   @PreDestroy
   public void destroy() {
     session.close();
   }
}

Cool isn’t it!

And all this is typesafe!

And EMap brings along much more like this (for example FETCH JOINS similar to Hibernate, …), but going into these would go beyond the scope of this blog post.

Supported platforms, databases and the sourcecode

The sources are all available under EPL from our github repository at https://github.com/BestSolution-at/emap.

In current version EMap offers production ready support for

  • Firebird 2.5
  • Oracle 11

and runs on Java6 and above.

What’s next?
At the moment, we are looking for further funding on EMap allowing us to
add additional databases and further features. Just drop us a note for
sponsored development possibilities.

Advertisement
This entry was posted in Eclipse. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.