keycloak-aplcache

Changes

model/mongo/src/main/java/org/keycloak/models/mongo/api/AbstractMongoEntity.java 12(+0 -12)

model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoId.java 18(+0 -18)

Details

diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/AbstractMongoIdentifiableEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/AbstractMongoIdentifiableEntity.java
new file mode 100644
index 0000000..d6509e0
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/AbstractMongoIdentifiableEntity.java
@@ -0,0 +1,50 @@
+package org.keycloak.models.mongo.api;
+
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AbstractMongoIdentifiableEntity implements MongoIdentifiableEntity {
+
+    private String id;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
+        // Empty by default
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+
+        if (this.id == null) return false;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AbstractMongoIdentifiableEntity that = (AbstractMongoIdentifiableEntity) o;
+
+        if (!getId().equals(that.getId())) return false;
+
+        return true;
+
+    }
+
+    @Override
+    public int hashCode() {
+        return id!=null ? id.hashCode() : super.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s [ id=%s ]", getClass().getSimpleName(), getId());
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoStoreInvocationContext.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoStoreInvocationContext.java
new file mode 100644
index 0000000..b6911e9
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoStoreInvocationContext.java
@@ -0,0 +1,25 @@
+package org.keycloak.models.mongo.api.context;
+
+import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface MongoStoreInvocationContext {
+
+    void addLoadedObject(MongoIdentifiableEntity entity);
+
+    <T extends MongoIdentifiableEntity> T getLoadedObject(Class<T> type, String id);
+
+    void addUpdateTask(MongoIdentifiableEntity entityToUpdate, MongoTask task);
+
+    void addRemovedObject(MongoIdentifiableEntity entityToRemove);
+
+    void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType);
+
+    void begin();
+
+    void commit();
+
+    void rollback();
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoTask.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoTask.java
new file mode 100644
index 0000000..0c1d500
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoTask.java
@@ -0,0 +1,13 @@
+package org.keycloak.models.mongo.api.context;
+
+import org.keycloak.models.mongo.api.MongoStore;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface MongoTask {
+
+    void execute();
+
+    boolean isFullUpdate();
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoEntity.java
index 6af6bbc..8b91583 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoEntity.java
@@ -1,16 +1,11 @@
 package org.keycloak.models.mongo.api;
 
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+
 /**
  * Base interface for object, which is persisted in Mongo
  *
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 public interface MongoEntity {
-
-    /**
-     * Lifecycle callback, which is called after removal of this object from Mongo.
-     * It may be useful for triggering removal of wired objects.
-     */
-    void afterRemove(MongoStore mongoStore);
-
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoIdentifiableEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoIdentifiableEntity.java
new file mode 100644
index 0000000..45ce126
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoIdentifiableEntity.java
@@ -0,0 +1,21 @@
+package org.keycloak.models.mongo.api;
+
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+
+/**
+ * Entity with Id
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface MongoIdentifiableEntity extends MongoEntity {
+
+    public String getId();
+
+    public void setId(String id);
+
+    /**
+     * Lifecycle callback, which is called after removal of this object from Mongo.
+     * It may be useful for triggering removal of wired objects.
+     */
+    void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invocationContext);
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java
index 89e4bb7..1741149 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java
@@ -1,6 +1,7 @@
 package org.keycloak.models.mongo.api;
 
 import com.mongodb.DBObject;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 
 import java.util.List;
 
@@ -14,30 +15,29 @@ public interface MongoStore {
      *
      * @param object to update
      */
-    void insertObject(MongoEntity object);
+    void insertObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context);
 
     /**
      * Update existing object
      *
      * @param object to update
      */
-    void updateObject(MongoEntity object);
+    void updateObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context);
 
 
-    <T extends MongoEntity> T loadObject(Class<T> type, String oid);
+    <T extends MongoIdentifiableEntity> T loadObject(Class<T> type, String oid, MongoStoreInvocationContext context);
 
-    <T extends MongoEntity> T loadSingleObject(Class<T> type, DBObject query);
+    <T extends MongoIdentifiableEntity> T loadSingleObject(Class<T> type, DBObject query, MongoStoreInvocationContext context);
 
-    <T extends MongoEntity> List<T> loadObjects(Class<T> type, DBObject query);
+    <T extends MongoIdentifiableEntity> List<T> loadObjects(Class<T> type, DBObject query, MongoStoreInvocationContext context);
 
-    // Object must have filled oid
-    boolean removeObject(MongoEntity object);
+    boolean removeObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context);
 
-    boolean removeObject(Class<? extends MongoEntity> type, String oid);
+    boolean removeObject(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context);
 
-    boolean removeObjects(Class<? extends MongoEntity> type, DBObject query);
+    boolean removeObjects(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context);
 
-    <S> boolean pushItemToList(MongoEntity object, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent);
+    <S> boolean pushItemToList(MongoIdentifiableEntity object, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context);
 
-    <S> void pullItemFromList(MongoEntity object, String listPropertyName, S itemToPull);
+    <S> boolean pullItemFromList(MongoIdentifiableEntity object, String listPropertyName, S itemToPull, MongoStoreInvocationContext context);
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/SimpleMongoStoreInvocationContext.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/SimpleMongoStoreInvocationContext.java
new file mode 100644
index 0000000..18c2d6a
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/SimpleMongoStoreInvocationContext.java
@@ -0,0 +1,56 @@
+package org.keycloak.models.mongo.impl.context;
+
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.api.context.MongoTask;
+
+/**
+ * Context, which is not doing any postponing of tasks and does not cache anything
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SimpleMongoStoreInvocationContext implements MongoStoreInvocationContext {
+
+    private final MongoStore store;
+
+    public SimpleMongoStoreInvocationContext(MongoStore store) {
+        this.store = store;
+    }
+
+    @Override
+    public void addLoadedObject(MongoIdentifiableEntity entity) {
+    }
+
+    @Override
+    public <T extends MongoIdentifiableEntity> T getLoadedObject(Class<T> type, String id) {
+        return null;
+    }
+
+    @Override
+    public void addUpdateTask(MongoIdentifiableEntity entityToUpdate, MongoTask task) {
+        task.execute();
+    }
+
+    @Override
+    public void addRemovedObject(MongoIdentifiableEntity entityToRemove) {
+        entityToRemove.afterRemove(store, this);
+    }
+
+    @Override
+    public void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType) {
+    }
+
+    @Override
+    public void begin() {
+    }
+
+    @Override
+    public void commit() {
+    }
+
+    @Override
+    public void rollback() {
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/TransactionMongoStoreInvocationContext.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/TransactionMongoStoreInvocationContext.java
new file mode 100644
index 0000000..1f9f66d
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/TransactionMongoStoreInvocationContext.java
@@ -0,0 +1,129 @@
+package org.keycloak.models.mongo.impl.context;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.api.context.MongoTask;
+
+/**
+ * Invocation context, which has some very basic support for transactions, and is able to cache loaded objects.
+ * It always execute all pending update tasks before start searching for other objects
+ *
+ * It's per-request object (not thread safe)
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class TransactionMongoStoreInvocationContext implements MongoStoreInvocationContext {
+
+    // Assumption is that all objects has unique ID (unique across all the types)
+    private Map<String, MongoIdentifiableEntity> loadedObjects = new HashMap<String, MongoIdentifiableEntity>();
+
+    private Map<MongoIdentifiableEntity, Set<MongoTask>> pendingUpdateTasks = new HashMap<MongoIdentifiableEntity, Set<MongoTask>>();
+
+    private final MongoStore mongoStore;
+
+    public TransactionMongoStoreInvocationContext(MongoStore mongoStore) {
+        this.mongoStore = mongoStore;
+    }
+
+    @Override
+    public void addLoadedObject(MongoIdentifiableEntity entity) {
+        loadedObjects.put(entity.getId(), entity);
+    }
+
+    @Override
+    public <T extends MongoIdentifiableEntity> T getLoadedObject(Class<T> type, String id) {
+        return (T)loadedObjects.get(id);
+    }
+
+    @Override
+    public void addUpdateTask(MongoIdentifiableEntity entityToUpdate, MongoTask task) {
+        if (!loadedObjects.containsValue(entityToUpdate)) {
+            throw new IllegalStateException("Entity " + entityToUpdate + " not found in loaded objects");
+        }
+
+        Set<MongoTask> currentObjectTasks = pendingUpdateTasks.get(entityToUpdate);
+        if (currentObjectTasks == null) {
+            currentObjectTasks = new HashSet<MongoTask>();
+            pendingUpdateTasks.put(entityToUpdate, currentObjectTasks);
+        } else {
+            // if task is full update, then remove all other tasks as we need to do full update of object anyway
+            if (task.isFullUpdate()) {
+                currentObjectTasks.clear();
+            } else {
+                // If it already contains task for fullUpdate, then we don't need to add ours as we need to do full update of object anyway
+                for (MongoTask current : currentObjectTasks) {
+                     if (current.isFullUpdate()) {
+                         return;
+                     }
+                }
+            }
+        }
+
+        currentObjectTasks.add(task);
+    }
+
+    @Override
+    public void addRemovedObject(MongoIdentifiableEntity entityToRemove) {
+        // Remove all pending tasks and object from cache
+        pendingUpdateTasks.remove(entityToRemove);
+        loadedObjects.remove(entityToRemove.getId());
+
+        entityToRemove.afterRemove(mongoStore, this);
+    }
+
+    @Override
+    public void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType) {
+        // Now execute pending update tasks of type, which will be searched
+        Set<MongoIdentifiableEntity> toRemove = new HashSet<MongoIdentifiableEntity>();
+
+        for (MongoIdentifiableEntity currentEntity : pendingUpdateTasks.keySet()) {
+            if (currentEntity.getClass().equals(entityType)) {
+                Set<MongoTask> mongoTasks = pendingUpdateTasks.get(currentEntity);
+                for (MongoTask currentTask : mongoTasks) {
+                    currentTask.execute();
+                }
+
+                toRemove.add(currentEntity);
+            }
+        }
+
+        // Now remove all done tasks
+        for (MongoIdentifiableEntity entity : toRemove) {
+            pendingUpdateTasks.remove(entity);
+        }
+    }
+
+    @Override
+    public void begin() {
+        loadedObjects.clear();
+        pendingUpdateTasks.clear();
+    }
+
+    @Override
+    public void commit() {
+        loadedObjects.clear();
+
+        // Now execute all pending update tasks
+        for (Set<MongoTask> mongoTasks : pendingUpdateTasks.values()) {
+            for (MongoTask currentTask : mongoTasks) {
+                currentTask.execute();
+            }
+        }
+
+        // And clear it
+        pendingUpdateTasks.clear();
+    }
+
+    @Override
+    public void rollback() {
+        // Just clear the map without executions of tasks
+        loadedObjects.clear();
+        pendingUpdateTasks.clear();
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoStoreImpl.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoStoreImpl.java
index 0c2d561..5fe1d0a 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoStoreImpl.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoStoreImpl.java
@@ -11,8 +11,10 @@ import org.jboss.logging.Logger;
 import org.keycloak.models.mongo.api.MongoCollection;
 import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
-import org.keycloak.models.mongo.api.MongoId;
+import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.api.context.MongoTask;
 import org.keycloak.models.mongo.api.types.Converter;
 import org.keycloak.models.mongo.api.types.ConverterContext;
 import org.keycloak.models.mongo.api.types.TypeConverter;
@@ -105,7 +107,7 @@ public class MongoStoreImpl implements MongoStore {
     }
 
     @Override
-    public void insertObject(MongoEntity object) {
+    public void insertObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context) {
         Class<? extends MongoEntity> clazz = object.getClass();
 
         // Find annotations for ID, for all the properties and for the name of the collection.
@@ -116,8 +118,7 @@ public class MongoStoreImpl implements MongoStore {
 
         DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
 
-        Property<String> oidProperty = objectInfo.getOidProperty();
-        String currentId = oidProperty == null ? null : oidProperty.getValue(object);
+        String currentId = object.getId();
 
         // Inserting object, which already has oid property set. So we need to set "_id"
         if (currentId != null) {
@@ -126,48 +127,73 @@ public class MongoStoreImpl implements MongoStore {
 
         dbCollection.insert(dbObject);
 
-        // Add oid to value of given object
-        if (currentId == null && oidProperty != null) {
-            oidProperty.setValue(object, dbObject.getString("_id"));
+        // Add id to value of given object
+        if (currentId == null) {
+            object.setId(dbObject.getString("_id"));
         }
+
+        // Treat object as if it is read (It is already submited to transaction)
+        context.addLoadedObject(object);
     }
 
     @Override
-    public void updateObject(MongoEntity object) {
-        Class<? extends MongoEntity> clazz = object.getClass();
-        ObjectInfo objectInfo = getObjectInfo(clazz);
-        BasicDBObject dbObject = typeConverter.convertApplicationObjectToDBObject(object, BasicDBObject.class);
-        DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
+    public void updateObject(final MongoIdentifiableEntity object, MongoStoreInvocationContext context) {
+        MongoTask fullUpdateTask = new MongoTask() {
+
+            @Override
+            public void execute() {
+                Class<? extends MongoEntity> clazz = object.getClass();
+                ObjectInfo objectInfo = getObjectInfo(clazz);
+                BasicDBObject dbObject = typeConverter.convertApplicationObjectToDBObject(object, BasicDBObject.class);
+                DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
+
+                String currentId = object.getId();
+
+                if (currentId == null) {
+                    throw new IllegalStateException("Can't update object without id: " + object);
+                } else {
+                    BasicDBObject query = new BasicDBObject("_id", getObjectId(currentId));
+                    dbCollection.update(query, dbObject);
+                }
+            }
 
-        Property<String> oidProperty = objectInfo.getOidProperty();
-        String currentId = oidProperty == null ? null : oidProperty.getValue(object);
+            @Override
+            public boolean isFullUpdate() {
+                return true;
+            }
+        };
 
-        if (currentId == null) {
-            throw new IllegalStateException("Can't update object without id: " + object);
-        } else {
-            BasicDBObject query = new BasicDBObject("_id", getObjectId(currentId));
-            dbCollection.update(query, dbObject);
-        }
+        // update is just added to context and postponed
+        context.addUpdateTask(object, fullUpdateTask);
     }
 
 
     @Override
-    public <T extends MongoEntity> T loadObject(Class<T> type, String oid) {
+    public <T extends MongoIdentifiableEntity> T loadObject(Class<T> type, String id, MongoStoreInvocationContext context) {
+        // First look if we already read the object with this oid and type during this transaction. If yes, use it instead of DB lookup
+        T cached = context.getLoadedObject(type, id);
+        if (cached != null) return cached;
+
         DBCollection dbCollection = getDBCollectionForType(type);
 
-        BasicDBObject idQuery = new BasicDBObject("_id", getObjectId(oid));
+        BasicDBObject idQuery = new BasicDBObject("_id", getObjectId(id));
         DBObject dbObject = dbCollection.findOne(idQuery);
 
         if (dbObject == null) return null;
 
         ConverterContext<Object> converterContext = new ConverterContext<Object>(dbObject, type, null);
-        return (T)typeConverter.convertDBObjectToApplicationObject(converterContext);
+        T converted = (T)typeConverter.convertDBObjectToApplicationObject(converterContext);
+
+        // Now add it to loaded objects
+        context.addLoadedObject(converted);
+
+        return converted;
     }
 
 
     @Override
-    public <T extends MongoEntity> T loadSingleObject(Class<T> type, DBObject query) {
-        List<T> result = loadObjects(type, query);
+    public <T extends MongoIdentifiableEntity> T loadSingleObject(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
+        List<T> result = loadObjects(type, query, context);
         if (result.size() > 1) {
             throw new IllegalStateException("There are " + result.size() + " results for type=" + type + ", query=" + query + ". We expect just one");
         } else if (result.size() == 1) {
@@ -180,47 +206,43 @@ public class MongoStoreImpl implements MongoStore {
 
 
     @Override
-    public <T extends MongoEntity> List<T> loadObjects(Class<T> type, DBObject query) {
-        DBCollection dbCollection = getDBCollectionForType(type);
+    public <T extends MongoIdentifiableEntity> List<T> loadObjects(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
+        // First we should execute all pending tasks before searching DB
+        context.beforeDBSearch(type);
 
+        DBCollection dbCollection = getDBCollectionForType(type);
         DBCursor cursor = dbCollection.find(query);
 
-        return convertCursor(type, cursor);
+        return convertCursor(type, cursor, context);
     }
 
 
     @Override
-    public boolean removeObject(MongoEntity object) {
-        Class<? extends MongoEntity> type = object.getClass();
-        ObjectInfo objectInfo = getObjectInfo(type);
-
-        Property<String> idProperty = objectInfo.getOidProperty();
-        String oid = idProperty.getValue(object);
-
-        return removeObject(type, oid);
+    public boolean removeObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context) {
+        return removeObject(object.getClass(), object.getId(), context);
     }
 
 
     @Override
-    public boolean removeObject(Class<? extends MongoEntity> type, String oid) {
-        MongoEntity found = loadObject(type, oid);
+    public boolean removeObject(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context) {
+        MongoIdentifiableEntity found = loadObject(type, id, context);
         if (found == null) {
             return false;
         } else {
             DBCollection dbCollection = getDBCollectionForType(type);
-            BasicDBObject dbQuery = new BasicDBObject("_id", getObjectId(oid));
+            BasicDBObject dbQuery = new BasicDBObject("_id", getObjectId(id));
             dbCollection.remove(dbQuery);
-            logger.info("Object of type: " + type + ", oid: " + oid + " removed from MongoDB.");
+            logger.info("Object of type: " + type + ", id: " + id + " removed from MongoDB.");
 
-            found.afterRemove(this);
+            context.addRemovedObject(found);
             return true;
         }
     }
 
 
     @Override
-    public boolean removeObjects(Class<? extends MongoEntity> type, DBObject query) {
-        List<? extends MongoEntity> foundObjects = loadObjects(type, query);
+    public boolean removeObjects(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context) {
+        List<? extends MongoIdentifiableEntity> foundObjects = loadObjects(type, query, context);
         if (foundObjects.size() == 0) {
             return false;
         } else {
@@ -228,23 +250,18 @@ public class MongoStoreImpl implements MongoStore {
             dbCollection.remove(query);
             logger.info("Removed " + foundObjects.size() + " objects of type: " + type + ", query: " + query);
 
-            for (MongoEntity found : foundObjects) {
-                found.afterRemove(this);
+            for (MongoIdentifiableEntity found : foundObjects) {
+                context.addRemovedObject(found);;
             }
             return true;
         }
     }
 
     @Override
-    public <S> boolean pushItemToList(MongoEntity object, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent) {
-        Class<? extends MongoEntity> type = object.getClass();
+    public <S> boolean pushItemToList(final MongoIdentifiableEntity object, final String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context) {
+        final Class<? extends MongoEntity> type = object.getClass();
         ObjectInfo objectInfo = getObjectInfo(type);
 
-        Property<String> oidProperty = getObjectInfo(type).getOidProperty();
-        if (oidProperty == null) {
-            throw new IllegalArgumentException("List pushes not supported for properties without oid");
-        }
-
         // Add item to list directly in this object
         Property<Object> listProperty = objectInfo.getPropertyByName(listPropertyName);
         if (listProperty == null) {
@@ -257,34 +274,44 @@ public class MongoStoreImpl implements MongoStore {
             listProperty.setValue(object, list);
         }
 
-        // Return if item is already in list
+        // Skip if item is already in list
         if (skipIfAlreadyPresent && list.contains(itemToPush)) {
             return false;
         }
 
+        // Update java object
         list.add(itemToPush);
 
-        // Push item to DB. We always convert whole list, so it's not so optimal...TODO: use $push if possible
-        BasicDBList dbList = typeConverter.convertApplicationObjectToDBObject(list, BasicDBList.class);
+        // Add update of list to pending tasks
+        final List<S> listt = list;
+        context.addUpdateTask(object, new MongoTask() {
+
+            @Override
+            public void execute() {
+                // Now DB update of new list with usage of $set
+                BasicDBList dbList = typeConverter.convertApplicationObjectToDBObject(listt, BasicDBList.class);
+
+                BasicDBObject query = new BasicDBObject("_id", getObjectId(object.getId()));
+                BasicDBObject listObject = new BasicDBObject(listPropertyName, dbList);
+                BasicDBObject setCommand = new BasicDBObject("$set", listObject);
+                getDBCollectionForType(type).update(query, setCommand);
+            }
+
+            @Override
+            public boolean isFullUpdate() {
+                return false;
+            }
+        });
 
-        BasicDBObject query = new BasicDBObject("_id", getObjectId(oidProperty.getValue(object)));
-        BasicDBObject listObject = new BasicDBObject(listPropertyName, dbList);
-        BasicDBObject setCommand = new BasicDBObject("$set", listObject);
-        getDBCollectionForType(type).update(query, setCommand);
         return true;
     }
 
 
     @Override
-    public <S> void pullItemFromList(MongoEntity object, String listPropertyName, S itemToPull) {
-        Class<? extends MongoEntity> type = object.getClass();
+    public <S> boolean pullItemFromList(final MongoIdentifiableEntity object, final String listPropertyName, final S itemToPull, MongoStoreInvocationContext context) {
+        final Class<? extends MongoEntity> type = object.getClass();
         ObjectInfo objectInfo = getObjectInfo(type);
 
-        Property<String> oidProperty = getObjectInfo(type).getOidProperty();
-        if (oidProperty == null) {
-            throw new IllegalArgumentException("List pulls not supported for properties without oid");
-        }
-
         // Remove item from list directly in this object
         Property<Object> listProperty = objectInfo.getPropertyByName(listPropertyName);
         if (listProperty == null) {
@@ -293,15 +320,33 @@ public class MongoStoreImpl implements MongoStore {
         List<S> list = (List<S>)listProperty.getValue(object);
 
         // If list is null, we skip both object and DB update
-        if (list != null) {
+        if (list == null || !list.contains(itemToPull)) {
+            return false;
+        } else {
+
+            // Update java object
             list.remove(itemToPull);
 
-            // Pull item from DB
-            Object dbItemToPull = typeConverter.convertApplicationObjectToDBObject(itemToPull, Object.class);
-            BasicDBObject query = new BasicDBObject("_id", getObjectId(oidProperty.getValue(object)));
-            BasicDBObject pullObject = new BasicDBObject(listPropertyName, dbItemToPull);
-            BasicDBObject pullCommand = new BasicDBObject("$pull", pullObject);
-            getDBCollectionForType(type).update(query, pullCommand);
+            // Add update of list to pending tasks
+            context.addUpdateTask(object, new MongoTask() {
+
+                @Override
+                public void execute() {
+                    // Pull item from DB
+                    Object dbItemToPull = typeConverter.convertApplicationObjectToDBObject(itemToPull, Object.class);
+                    BasicDBObject query = new BasicDBObject("_id", getObjectId(object.getId()));
+                    BasicDBObject pullObject = new BasicDBObject(listPropertyName, dbItemToPull);
+                    BasicDBObject pullCommand = new BasicDBObject("$pull", pullObject);
+                    getDBCollectionForType(type).update(query, pullCommand);
+                }
+
+                @Override
+                public boolean isFullUpdate() {
+                    return false;
+                }
+            });
+
+            return true;
         }
     }
 
@@ -317,14 +362,12 @@ public class MongoStoreImpl implements MongoStore {
     public ObjectInfo getObjectInfo(Class<? extends MongoEntity> objectClass) {
         ObjectInfo objectInfo = objectInfoCache.get(objectClass);
         if (objectInfo == null) {
-            Property<String> idProperty = PropertyQueries.<String>createQuery(objectClass).addCriteria(new AnnotatedPropertyCriteria(MongoId.class)).getFirstResult();
-
             List<Property<Object>> properties = PropertyQueries.createQuery(objectClass).addCriteria(new AnnotatedPropertyCriteria(MongoField.class)).getResultList();
 
             MongoCollection classAnnotation = objectClass.getAnnotation(MongoCollection.class);
 
             String dbCollectionName = classAnnotation==null ? null : classAnnotation.collectionName();
-            objectInfo = new ObjectInfo(objectClass, dbCollectionName, idProperty, properties);
+            objectInfo = new ObjectInfo(objectClass, dbCollectionName, properties);
 
             ObjectInfo existing = objectInfoCache.putIfAbsent(objectClass, objectInfo);
             if (existing != null) {
@@ -335,14 +378,23 @@ public class MongoStoreImpl implements MongoStore {
         return objectInfo;
     }
 
-    private <T extends MongoEntity> List<T> convertCursor(Class<T> type, DBCursor cursor) {
+    protected <T extends MongoIdentifiableEntity> List<T> convertCursor(Class<T> type, DBCursor cursor, MongoStoreInvocationContext context) {
         List<T> result = new ArrayList<T>();
 
         try {
             for (DBObject dbObject : cursor) {
-                ConverterContext<Object> converterContext = new ConverterContext<Object>(dbObject, type, null);
-                T converted = (T)typeConverter.convertDBObjectToApplicationObject(converterContext);
-                result.add(converted);
+                // First look if we already have loaded object cached. If yes, we will use cached instance
+                String id = dbObject.get("_id").toString();
+                T object = context.getLoadedObject(type, id);
+
+                if (object == null) {
+                    // So convert and use fresh instance from DB
+                    ConverterContext<Object> converterContext = new ConverterContext<Object>(dbObject, type, null);
+                    object = (T)typeConverter.convertDBObjectToApplicationObject(converterContext);
+                    context.addLoadedObject(object);
+                }
+
+                result.add(object);
             }
         } finally {
             cursor.close();
@@ -351,14 +403,14 @@ public class MongoStoreImpl implements MongoStore {
         return result;
     }
 
-    private DBCollection getDBCollectionForType(Class<? extends MongoEntity> type) {
+    protected DBCollection getDBCollectionForType(Class<? extends MongoEntity> type) {
         ObjectInfo objectInfo = getObjectInfo(type);
         String dbCollectionName = objectInfo.getDbCollectionName();
         return dbCollectionName==null ? null : database.getCollection(objectInfo.getDbCollectionName());
     }
 
     // We allow ObjectId to be both "ObjectId" or "String".
-    private Object getObjectId(String idAsString) {
+    protected Object getObjectId(String idAsString) {
         if (ObjectId.isValid(idAsString)) {
             return new ObjectId(idAsString);
         } else {
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java
index 0662ad6..797954a 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java
@@ -18,14 +18,11 @@ public class ObjectInfo {
 
     private final String dbCollectionName;
 
-    private final Property<String> oidProperty;
-
     private final Map<String, Property<Object>> properties;
 
-    public ObjectInfo(Class<? extends MongoEntity> objectClass, String dbCollectionName, Property<String> oidProperty, List<Property<Object>> properties) {
+    public ObjectInfo(Class<? extends MongoEntity> objectClass, String dbCollectionName, List<Property<Object>> properties) {
         this.objectClass = objectClass;
         this.dbCollectionName = dbCollectionName;
-        this.oidProperty = oidProperty;
 
         Map<String, Property<Object>> props= new HashMap<String, Property<Object>>();
         for (Property<Object> property : properties) {
@@ -42,10 +39,6 @@ public class ObjectInfo {
         return dbCollectionName;
     }
 
-    public Property<String> getOidProperty() {
-        return oidProperty;
-    }
-
     public Collection<Property<Object>> getProperties() {
         return properties.values();
     }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectConverter.java
index 62d0a82..2d130e6 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectConverter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectConverter.java
@@ -8,6 +8,7 @@ import java.util.List;
 import com.mongodb.BasicDBObject;
 import org.jboss.logging.Logger;
 import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.types.Converter;
 import org.keycloak.models.mongo.api.types.ConverterContext;
 import org.keycloak.models.mongo.api.types.TypeConverter;
@@ -55,9 +56,8 @@ public class BasicDBObjectConverter<S extends MongoEntity> implements Converter<
 
             if ("_id".equals(key)) {
                 // Current property is "id"
-                Property<String> idProperty = objectInfo.getOidProperty();
-                if (idProperty != null) {
-                    idProperty.setValue(object, value.toString());
+                if (object instanceof MongoIdentifiableEntity) {
+                    ((MongoIdentifiableEntity)object).setId(value.toString());
                 }
 
             } else if ((property = objectInfo.getPropertyByName(key)) != null) {
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractAdapter.java
new file mode 100644
index 0000000..cd6a1dd
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractAdapter.java
@@ -0,0 +1,38 @@
+package org.keycloak.models.mongo.keycloak.adapters;
+
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class AbstractAdapter {
+
+    protected MongoStore mongoStore;
+    protected MongoStoreInvocationContext invocationContext;
+
+    public AbstractAdapter(MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
+        this.mongoStore = mongoStore;
+        this.invocationContext = invocationContext;
+    }
+
+    public abstract AbstractMongoIdentifiableEntity getMongoEntity();
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AbstractAdapter that = (AbstractAdapter) o;
+
+        if (getMongoEntity() == null && that.getMongoEntity() == null) return true;
+        return getMongoEntity().equals(that.getMongoEntity());
+    }
+
+    @Override
+    public int hashCode() {
+        return getMongoEntity()!=null ? getMongoEntity().hashCode() : super.hashCode();
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
index 835d18d..e4ca675 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
@@ -5,7 +5,9 @@ import com.mongodb.QueryBuilder;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
 import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
 import org.keycloak.models.mongo.keycloak.entities.UserEntity;
@@ -19,37 +21,35 @@ import java.util.Set;
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class ApplicationAdapter implements ApplicationModel {
+public class ApplicationAdapter extends AbstractAdapter implements ApplicationModel {
 
     private final ApplicationEntity application;
-    private final MongoStore mongoStore;
-
     private UserAdapter resourceUser;
 
-    public ApplicationAdapter(ApplicationEntity applicationEntity, MongoStore mongoStore) {
-        this(applicationEntity, null, mongoStore);
+    public ApplicationAdapter(ApplicationEntity applicationEntity, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
+        this(applicationEntity, null, mongoStore, invContext);
     }
 
-    public ApplicationAdapter(ApplicationEntity applicationEntity, UserAdapter resourceUser, MongoStore mongoStore) {
+    public ApplicationAdapter(ApplicationEntity applicationEntity, UserAdapter resourceUser, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
+        super(mongoStore, invContext);
         this.application = applicationEntity;
         this.resourceUser = resourceUser;
-        this.mongoStore = mongoStore;
     }
 
     @Override
     public void updateApplication() {
-        mongoStore.updateObject(application);
+        mongoStore.updateObject(application, invocationContext);
     }
 
     @Override
     public UserAdapter getApplicationUser() {
         // This is not thread-safe. Assumption is that ApplicationAdapter instance is per-client object
         if (resourceUser == null) {
-            UserEntity userEntity = mongoStore.loadObject(UserEntity.class, application.getResourceUserId());
+            UserEntity userEntity = mongoStore.loadObject(UserEntity.class, application.getResourceUserId(), invocationContext);
             if (userEntity == null) {
                 throw new IllegalStateException("User " + application.getResourceUserId() + " not found");
             }
-            resourceUser = new UserAdapter(userEntity, mongoStore);
+            resourceUser = new UserAdapter(userEntity, mongoStore, invocationContext);
         }
 
         return resourceUser;
@@ -116,21 +116,21 @@ public class ApplicationAdapter implements ApplicationModel {
                 .and("name").is(name)
                 .and("applicationId").is(getId())
                 .get();
-        RoleEntity role = mongoStore.loadSingleObject(RoleEntity.class, query);
+        RoleEntity role = mongoStore.loadSingleObject(RoleEntity.class, query, invocationContext);
         if (role == null) {
             return null;
         } else {
-            return new RoleAdapter(role, this, mongoStore);
+            return new RoleAdapter(role, this, mongoStore, invocationContext);
         }
     }
 
     @Override
     public RoleModel getRoleById(String id) {
-        RoleEntity role = mongoStore.loadObject(RoleEntity.class, id);
+        RoleEntity role = mongoStore.loadObject(RoleEntity.class, id, invocationContext);
         if (role == null) {
             return null;
         } else {
-            return new RoleAdapter(role, this, mongoStore);
+            return new RoleAdapter(role, this, mongoStore, invocationContext);
         }
     }
 
@@ -145,13 +145,13 @@ public class ApplicationAdapter implements ApplicationModel {
         roleEntity.setName(name);
         roleEntity.setApplicationId(getId());
 
-        mongoStore.insertObject(roleEntity);
-        return new RoleAdapter(roleEntity, this, mongoStore);
+        mongoStore.insertObject(roleEntity, invocationContext);
+        return new RoleAdapter(roleEntity, this, mongoStore, invocationContext);
     }
 
     @Override
     public boolean removeRoleById(String id) {
-        return mongoStore.removeObject(RoleEntity.class ,id);
+        return mongoStore.removeObject(RoleEntity.class ,id, invocationContext);
     }
 
     @Override
@@ -159,11 +159,11 @@ public class ApplicationAdapter implements ApplicationModel {
         DBObject query = new QueryBuilder()
                 .and("applicationId").is(getId())
                 .get();
-        List<RoleEntity> roles = mongoStore.loadObjects(RoleEntity.class, query);
+        List<RoleEntity> roles = mongoStore.loadObjects(RoleEntity.class, query, invocationContext);
 
         Set<RoleModel> result = new HashSet<RoleModel>();
         for (RoleEntity role : roles) {
-            result.add(new RoleAdapter(role, this, mongoStore));
+            result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
         }
 
         return result;
@@ -172,11 +172,11 @@ public class ApplicationAdapter implements ApplicationModel {
     @Override
     public Set<RoleModel> getApplicationRoleMappings(UserModel user) {
         Set<RoleModel> result = new HashSet<RoleModel>();
-        List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, mongoStore);
+        List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, mongoStore, invocationContext);
 
         for (RoleEntity role : roles) {
             if (getId().equals(role.getApplicationId())) {
-                result.add(new RoleAdapter(role, this, mongoStore));
+                result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
             }
         }
         return result;
@@ -185,17 +185,17 @@ public class ApplicationAdapter implements ApplicationModel {
     @Override
     public void addScope(RoleModel role) {
         UserAdapter appUser = getApplicationUser();
-        mongoStore.pushItemToList(appUser.getUser(), "scopeIds", role.getId(), true);
+        mongoStore.pushItemToList(appUser.getUser(), "scopeIds", role.getId(), true, invocationContext);
     }
 
     @Override
     public Set<RoleModel> getApplicationScopeMappings(UserModel user) {
         Set<RoleModel> result = new HashSet<RoleModel>();
-        List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, mongoStore);
+        List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, mongoStore, invocationContext);
 
         for (RoleEntity role : roles) {
             if (getId().equals(role.getApplicationId())) {
-                result.add(new RoleAdapter(role, this, mongoStore));
+                result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
             }
         }
         return result;
@@ -213,7 +213,7 @@ public class ApplicationAdapter implements ApplicationModel {
             addRole(name);
         }
 
-        mongoStore.pushItemToList(application, "defaultRoles", name, true);
+        mongoStore.pushItemToList(application, "defaultRoles", name, true, invocationContext);
     }
 
     @Override
@@ -232,16 +232,7 @@ public class ApplicationAdapter implements ApplicationModel {
     }
 
     @Override
-    public boolean equals(Object o) {
-        if (o == null) return false;
-        if (o == this) return true;
-        if (!(o instanceof ApplicationAdapter)) return false;
-        ApplicationAdapter app = (ApplicationAdapter)o;
-        return app.getId().equals(getId());
-    }
-
-    @Override
-    public int hashCode() {
-        return getId().hashCode();
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
+        return application;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSession.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSession.java
index 6fd1cfc..e612a54 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSession.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSession.java
@@ -8,6 +8,9 @@ import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.impl.context.SimpleMongoStoreInvocationContext;
+import org.keycloak.models.mongo.impl.context.TransactionMongoStoreInvocationContext;
 import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
 import org.keycloak.models.utils.KeycloakModelUtils;
 
@@ -19,20 +22,25 @@ import java.util.List;
  */
 public class MongoKeycloakSession implements KeycloakSession {
 
-    private static final MongoKeycloakTransaction PLACEHOLDER = new MongoKeycloakTransaction();
+    private final MongoStoreInvocationContext invocationContext;
+    private final MongoKeycloakTransaction transaction;
     private final MongoStore mongoStore;
 
     public MongoKeycloakSession(MongoStore mongoStore) {
         this.mongoStore = mongoStore;
+        // this.invocationContext = new SimpleMongoStoreInvocationContext(mongoStore);
+        this.invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
+        this.transaction = new MongoKeycloakTransaction(invocationContext);
     }
 
     @Override
     public KeycloakTransaction getTransaction() {
-        return PLACEHOLDER;
+        return transaction;
     }
 
     @Override
     public void close() {
+        // TODO
     }
 
     @Override
@@ -50,26 +58,25 @@ public class MongoKeycloakSession implements KeycloakSession {
         newRealm.setId(id);
         newRealm.setName(name);
 
-        mongoStore.insertObject(newRealm);
+        mongoStore.insertObject(newRealm, invocationContext);
 
-        RealmAdapter realm = new RealmAdapter(newRealm, mongoStore);
-        return realm;
+        return new RealmAdapter(newRealm, mongoStore, invocationContext);
     }
 
     @Override
     public RealmModel getRealm(String id) {
-        RealmEntity realmEntity = mongoStore.loadObject(RealmEntity.class, id);
-        return realmEntity != null ? new RealmAdapter(realmEntity, mongoStore) : null;
+        RealmEntity realmEntity = mongoStore.loadObject(RealmEntity.class, id, invocationContext);
+        return realmEntity != null ? new RealmAdapter(realmEntity, mongoStore, invocationContext) : null;
     }
 
     @Override
     public List<RealmModel> getRealms(UserModel admin) {
         DBObject query = new BasicDBObject();
-        List<RealmEntity> realms = mongoStore.loadObjects(RealmEntity.class, query);
+        List<RealmEntity> realms = mongoStore.loadObjects(RealmEntity.class, query, invocationContext);
 
         List<RealmModel> results = new ArrayList<RealmModel>();
         for (RealmEntity realmEntity : realms) {
-            results.add(new RealmAdapter(realmEntity, mongoStore));
+            results.add(new RealmAdapter(realmEntity, mongoStore, invocationContext));
         }
         return results;
     }
@@ -79,14 +86,14 @@ public class MongoKeycloakSession implements KeycloakSession {
         DBObject query = new QueryBuilder()
                 .and("name").is(name)
                 .get();
-        RealmEntity realm = mongoStore.loadSingleObject(RealmEntity.class, query);
+        RealmEntity realm = mongoStore.loadSingleObject(RealmEntity.class, query, invocationContext);
 
         if (realm == null) return null;
-        return new RealmAdapter(realm, mongoStore);
+        return new RealmAdapter(realm, mongoStore, invocationContext);
     }
 
     @Override
     public boolean removeRealm(String id) {
-        return mongoStore.removeObject(RealmEntity.class, id);
+        return mongoStore.removeObject(RealmEntity.class, id, invocationContext);
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakTransaction.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakTransaction.java
index db2de6f..c9d9d37 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakTransaction.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakTransaction.java
@@ -1,39 +1,60 @@
 package org.keycloak.models.mongo.keycloak.adapters;
 
 import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 public class MongoKeycloakTransaction implements KeycloakTransaction {
 
+    private final MongoStoreInvocationContext invocationContext;
+
+    private boolean started = false;
+    private boolean rollbackOnly = false;
+
+    public MongoKeycloakTransaction(MongoStoreInvocationContext invocationContext) {
+        this.invocationContext = invocationContext;
+    }
+
     @Override
     public void begin() {
-        //To change body of implemented methods use File | Settings | File Templates.
+        if (started) {
+            throw new IllegalStateException("Transaction already started");
+        }
+        started = true;
+        invocationContext.begin();
     }
 
     @Override
     public void commit() {
-        //To change body of implemented methods use File | Settings | File Templates.
+        if (!started) {
+            throw new IllegalStateException("Transaction not yet started");
+        }
+        if (rollbackOnly) {
+            throw new IllegalStateException("Can't commit as transaction marked for rollback");
+        }
+
+        invocationContext.commit();
     }
 
     @Override
     public void rollback() {
-        //To change body of implemented methods use File | Settings | File Templates.
+        invocationContext.rollback();
     }
 
     @Override
     public void setRollbackOnly() {
-        //To change body of implemented methods use File | Settings | File Templates.
+        this.rollbackOnly = true;
     }
 
     @Override
     public boolean getRollbackOnly() {
-        return false;  //To change body of implemented methods use File | Settings | File Templates.
+        return rollbackOnly;
     }
 
     @Override
     public boolean isActive() {
-        return true;
+        return started;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
index e4b866d..3684872 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
@@ -2,27 +2,28 @@ package org.keycloak.models.mongo.keycloak.adapters;
 
 import org.keycloak.models.OAuthClientModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
 import org.keycloak.models.mongo.keycloak.entities.UserEntity;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class OAuthClientAdapter implements OAuthClientModel {
+public class OAuthClientAdapter extends AbstractAdapter implements OAuthClientModel {
 
     private final OAuthClientEntity delegate;
     private UserAdapter oauthAgent;
-    private final MongoStore mongoStore;
 
-    public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, UserAdapter oauthAgent, MongoStore mongoStore) {
+    public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, UserAdapter oauthAgent, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
+        super(mongoStore, invContext);
         this.delegate = oauthClientEntity;
         this.oauthAgent = oauthAgent;
-        this.mongoStore = mongoStore;
     }
 
-    public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, MongoStore mongoStore) {
-        this(oauthClientEntity, null, mongoStore);
+    public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
+        this(oauthClientEntity, null, mongoStore, invContext);
     }
 
     @Override
@@ -34,10 +35,14 @@ public class OAuthClientAdapter implements OAuthClientModel {
     public UserModel getOAuthAgent() {
         // This is not thread-safe. Assumption is that OAuthClientAdapter instance is per-client object
         if (oauthAgent == null) {
-            UserEntity user = mongoStore.loadObject(UserEntity.class, delegate.getOauthAgentId());
-            oauthAgent = user!=null ? new UserAdapter(user, mongoStore) : null;
+            UserEntity user = mongoStore.loadObject(UserEntity.class, delegate.getOauthAgentId(), invocationContext);
+            oauthAgent = user!=null ? new UserAdapter(user, mongoStore, invocationContext) : null;
         }
         return oauthAgent;
     }
 
+    @Override
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
+        return delegate;
+    }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 48ab577..c5c558c 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -12,7 +12,9 @@ import org.keycloak.models.RoleModel;
 import org.keycloak.models.SocialLinkModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
 import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
 import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
@@ -30,6 +32,7 @@ import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -40,21 +43,20 @@ import java.util.regex.Pattern;
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class RealmAdapter implements RealmModel {
+public class RealmAdapter extends AbstractAdapter implements RealmModel {
 
     private static final Logger logger = Logger.getLogger(RealmAdapter.class);
 
     private final RealmEntity realm;
-    private final MongoStore mongoStore;
 
     protected volatile transient PublicKey publicKey;
     protected volatile transient PrivateKey privateKey;
 
     private volatile transient PasswordPolicy passwordPolicy;
 
-    public RealmAdapter(RealmEntity realmEntity, MongoStore mongoStore) {
+    public RealmAdapter(RealmEntity realmEntity, MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
+        super(mongoStore, invocationContext);
         this.realm = realmEntity;
-        this.mongoStore = mongoStore;
     }
 
     @Override
@@ -251,17 +253,39 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
+    public String getLoginTheme() {
+        return realm.getLoginTheme();
+    }
+
+    @Override
+    public void setLoginTheme(String name) {
+        realm.setLoginTheme(name);
+        updateRealm();
+    }
+
+    @Override
+    public String getAccountTheme() {
+        return realm.getAccountTheme();
+    }
+
+    @Override
+    public void setAccountTheme(String name) {
+        realm.setAccountTheme(name);
+        updateRealm();
+    }
+
+    @Override
     public UserAdapter getUser(String name) {
         DBObject query = new QueryBuilder()
                 .and("loginName").is(name)
                 .and("realmId").is(getId())
                 .get();
-        UserEntity user = mongoStore.loadSingleObject(UserEntity.class, query);
+        UserEntity user = mongoStore.loadSingleObject(UserEntity.class, query, invocationContext);
 
         if (user == null) {
             return null;
         } else {
-            return new UserAdapter(user, mongoStore);
+            return new UserAdapter(user, mongoStore, invocationContext);
         }
     }
 
@@ -271,12 +295,12 @@ public class RealmAdapter implements RealmModel {
                 .and("email").is(email)
                 .and("realmId").is(getId())
                 .get();
-        UserEntity user = mongoStore.loadSingleObject(UserEntity.class, query);
+        UserEntity user = mongoStore.loadSingleObject(UserEntity.class, query, invocationContext);
 
         if (user == null) {
             return null;
         } else {
-            return new UserAdapter(user, mongoStore);
+            return new UserAdapter(user, mongoStore, invocationContext);
         }
     }
 
@@ -308,8 +332,8 @@ public class RealmAdapter implements RealmModel {
         userEntity.setEnabled(true);
         userEntity.setRealmId(getId());
 
-        mongoStore.insertObject(userEntity);
-        return new UserAdapter(userEntity, mongoStore);
+        mongoStore.insertObject(userEntity, invocationContext);
+        return new UserAdapter(userEntity, mongoStore, invocationContext);
     }
 
     @Override
@@ -318,7 +342,7 @@ public class RealmAdapter implements RealmModel {
                 .and("loginName").is(name)
                 .and("realmId").is(getId())
                 .get();
-        return mongoStore.removeObjects(UserEntity.class, query);
+        return mongoStore.removeObjects(UserEntity.class, query, invocationContext);
     }
 
     @Override
@@ -327,11 +351,11 @@ public class RealmAdapter implements RealmModel {
                 .and("name").is(name)
                 .and("realmId").is(getId())
                 .get();
-        RoleEntity role = mongoStore.loadSingleObject(RoleEntity.class, query);
+        RoleEntity role = mongoStore.loadSingleObject(RoleEntity.class, query, invocationContext);
         if (role == null) {
             return null;
         } else {
-            return new RoleAdapter(role, this, mongoStore);
+            return new RoleAdapter(role, this, mongoStore, invocationContext);
         }
     }
 
@@ -348,13 +372,13 @@ public class RealmAdapter implements RealmModel {
         roleEntity.setName(name);
         roleEntity.setRealmId(getId());
 
-        mongoStore.insertObject(roleEntity);
-        return new RoleAdapter(roleEntity, this, mongoStore);
+        mongoStore.insertObject(roleEntity, invocationContext);
+        return new RoleAdapter(roleEntity, this, mongoStore, invocationContext);
     }
 
     @Override
     public boolean removeRoleById(String id) {
-        return mongoStore.removeObject(RoleEntity.class ,id);
+        return mongoStore.removeObject(RoleEntity.class ,id, invocationContext);
     }
 
     @Override
@@ -362,13 +386,13 @@ public class RealmAdapter implements RealmModel {
         DBObject query = new QueryBuilder()
                 .and("realmId").is(getId())
                 .get();
-        List<RoleEntity> roles = mongoStore.loadObjects(RoleEntity.class, query);
+        List<RoleEntity> roles = mongoStore.loadObjects(RoleEntity.class, query, invocationContext);
 
         Set<RoleModel> result = new HashSet<RoleModel>();
 
         if (roles == null) return result;
         for (RoleEntity role : roles) {
-            result.add(new RoleAdapter(role, this, mongoStore));
+            result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
         }
 
         return result;
@@ -376,11 +400,11 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public RoleModel getRoleById(String id) {
-        RoleEntity role = mongoStore.loadObject(RoleEntity.class, id);
+        RoleEntity role = mongoStore.loadObject(RoleEntity.class, id, invocationContext);
         if (role == null) {
             return null;
         } else {
-            return new RoleAdapter(role, this, mongoStore);
+            return new RoleAdapter(role, this, mongoStore, invocationContext);
         }
     }
 
@@ -396,7 +420,7 @@ public class RealmAdapter implements RealmModel {
             addRole(name);
         }
 
-        mongoStore.pushItemToList(realm, "defaultRoles", name, true);
+        mongoStore.pushItemToList(realm, "defaultRoles", name, true, invocationContext);
     }
 
     @Override
@@ -417,15 +441,14 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public ApplicationModel getApplicationById(String id) {
-        ApplicationEntity appData = mongoStore.loadObject(ApplicationEntity.class, id);
+        ApplicationEntity appData = mongoStore.loadObject(ApplicationEntity.class, id, invocationContext);
 
         // Check if application belongs to this realm
         if (appData == null || !getId().equals(appData.getRealmId())) {
             return null;
         }
 
-        ApplicationModel model = new ApplicationAdapter(appData, mongoStore);
-        return model;
+        return new ApplicationAdapter(appData, mongoStore, invocationContext);
     }
 
     @Override
@@ -434,8 +457,8 @@ public class RealmAdapter implements RealmModel {
                 .and("realmId").is(getId())
                 .and("name").is(name)
                 .get();
-        ApplicationEntity appEntity = mongoStore.loadSingleObject(ApplicationEntity.class, query);
-        return appEntity==null ? null : new ApplicationAdapter(appEntity, mongoStore);
+        ApplicationEntity appEntity = mongoStore.loadSingleObject(ApplicationEntity.class, query, invocationContext);
+        return appEntity==null ? null : new ApplicationAdapter(appEntity, mongoStore, invocationContext);
     }
 
     @Override
@@ -452,11 +475,11 @@ public class RealmAdapter implements RealmModel {
         DBObject query = new QueryBuilder()
                 .and("realmId").is(getId())
                 .get();
-        List<ApplicationEntity> appDatas = mongoStore.loadObjects(ApplicationEntity.class, query);
+        List<ApplicationEntity> appDatas = mongoStore.loadObjects(ApplicationEntity.class, query, invocationContext);
 
         List<ApplicationModel> result = new ArrayList<ApplicationModel>();
         for (ApplicationEntity appData : appDatas) {
-            result.add(new ApplicationAdapter(appData, mongoStore));
+            result.add(new ApplicationAdapter(appData, mongoStore, invocationContext));
         }
         return result;
     }
@@ -470,15 +493,14 @@ public class RealmAdapter implements RealmModel {
         appData.setRealmId(getId());
         appData.setEnabled(true);
         appData.setResourceUserId(resourceUser.getUser().getId());
-        mongoStore.insertObject(appData);
+        mongoStore.insertObject(appData, invocationContext);
 
-        ApplicationModel resource = new ApplicationAdapter(appData, resourceUser, mongoStore);
-        return resource;
+        return new ApplicationAdapter(appData, resourceUser, mongoStore, invocationContext);
     }
 
     @Override
     public boolean removeApplication(String id) {
-        return mongoStore.removeObject(ApplicationEntity.class, id);
+        return mongoStore.removeObject(ApplicationEntity.class, id, invocationContext);
     }
 
     @Override
@@ -495,20 +517,20 @@ public class RealmAdapter implements RealmModel {
     @Override
     public void grantRole(UserModel user, RoleModel role) {
         UserEntity userEntity = ((UserAdapter)user).getUser();
-        mongoStore.pushItemToList(userEntity, "roleIds", role.getId(), true);
+        mongoStore.pushItemToList(userEntity, "roleIds", role.getId(), true, invocationContext);
     }
 
     @Override
     public Set<RoleModel> getRoleMappings(UserModel user) {
         Set<RoleModel> result = new HashSet<RoleModel>();
-        List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, mongoStore);
+        List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, mongoStore, invocationContext);
 
         for (RoleEntity role : roles) {
             if (getId().equals(role.getRealmId())) {
-                result.add(new RoleAdapter(role, this, mongoStore));
+                result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
             } else {
                 // Likely applicationRole, but we don't have this application yet
-                result.add(new RoleAdapter(role, mongoStore));
+                result.add(new RoleAdapter(role, mongoStore, invocationContext));
             }
         }
         return result;
@@ -533,20 +555,20 @@ public class RealmAdapter implements RealmModel {
     @Override
     public void deleteRoleMapping(UserModel user, RoleModel role) {
         UserEntity userEntity = ((UserAdapter)user).getUser();
-        mongoStore.pullItemFromList(userEntity, "roleIds", role.getId());
+        mongoStore.pullItemFromList(userEntity, "roleIds", role.getId(), invocationContext);
     }
 
     @Override
     public Set<RoleModel> getScopeMappings(UserModel user) {
         Set<RoleModel> result = new HashSet<RoleModel>();
-        List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, mongoStore);
+        List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, mongoStore, invocationContext);
 
         for (RoleEntity role : roles) {
             if (getId().equals(role.getRealmId())) {
-                result.add(new RoleAdapter(role, this, mongoStore));
+                result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
             } else {
                 // Likely applicationRole, but we don't have this application yet
-                result.add(new RoleAdapter(role, mongoStore));
+                result.add(new RoleAdapter(role, mongoStore, invocationContext));
             }
         }
         return result;
@@ -571,13 +593,13 @@ public class RealmAdapter implements RealmModel {
     @Override
     public void addScopeMapping(UserModel agent, RoleModel role) {
         UserEntity userEntity = ((UserAdapter)agent).getUser();
-        mongoStore.pushItemToList(userEntity, "scopeIds", role.getId(), true);
+        mongoStore.pushItemToList(userEntity, "scopeIds", role.getId(), true, invocationContext);
     }
 
     @Override
     public void deleteScopeMapping(UserModel user, RoleModel role) {
         UserEntity userEntity = ((UserAdapter)user).getUser();
-        mongoStore.pullItemFromList(userEntity, "scopeIds", role.getId());
+        mongoStore.pullItemFromList(userEntity, "scopeIds", role.getId(), invocationContext);
     }
 
     @Override
@@ -588,14 +610,14 @@ public class RealmAdapter implements RealmModel {
         oauthClient.setOauthAgentId(oauthAgent.getUser().getId());
         oauthClient.setRealmId(getId());
         oauthClient.setName(name);
-        mongoStore.insertObject(oauthClient);
+        mongoStore.insertObject(oauthClient, invocationContext);
 
-        return new OAuthClientAdapter(oauthClient, oauthAgent, mongoStore);
+        return new OAuthClientAdapter(oauthClient, oauthAgent, mongoStore, invocationContext);
     }
 
     @Override
     public boolean removeOAuthClient(String id) {
-        return mongoStore.removeObject(OAuthClientEntity.class, id);
+        return mongoStore.removeObject(OAuthClientEntity.class, id, invocationContext);
     }
 
     @Override
@@ -606,15 +628,15 @@ public class RealmAdapter implements RealmModel {
                 .and("realmId").is(getId())
                 .and("oauthAgentId").is(user.getUser().getId())
                 .get();
-        OAuthClientEntity oauthClient = mongoStore.loadSingleObject(OAuthClientEntity.class, query);
-        return oauthClient == null ? null : new OAuthClientAdapter(oauthClient, user, mongoStore);
+        OAuthClientEntity oauthClient = mongoStore.loadSingleObject(OAuthClientEntity.class, query, invocationContext);
+        return oauthClient == null ? null : new OAuthClientAdapter(oauthClient, user, mongoStore, invocationContext);
     }
 
     @Override
     public OAuthClientModel getOAuthClientById(String id) {
-        OAuthClientEntity clientEntity = mongoStore.loadObject(OAuthClientEntity.class, id);
+        OAuthClientEntity clientEntity = mongoStore.loadObject(OAuthClientEntity.class, id, invocationContext);
         if (clientEntity == null) return null;
-        return new OAuthClientAdapter(clientEntity, mongoStore);
+        return new OAuthClientAdapter(clientEntity, mongoStore, invocationContext);
     }
 
     @Override
@@ -622,10 +644,10 @@ public class RealmAdapter implements RealmModel {
         DBObject query = new QueryBuilder()
                 .and("realmId").is(getId())
                 .get();
-        List<OAuthClientEntity> results = mongoStore.loadObjects(OAuthClientEntity.class, query);
+        List<OAuthClientEntity> results = mongoStore.loadObjects(OAuthClientEntity.class, query, invocationContext);
         List<OAuthClientModel> list = new ArrayList<OAuthClientModel>();
         for (OAuthClientEntity data : results) {
-            list.add(new OAuthClientAdapter(data, mongoStore));
+            list.add(new OAuthClientAdapter(data, mongoStore, invocationContext));
         }
         return list;
     }
@@ -686,7 +708,7 @@ public class RealmAdapter implements RealmModel {
             }
         }
         for (RequiredCredentialEntity entity : toRemove) {
-            creds.remove(entity);
+            credsEntities.remove(entity);
         }
         for (String cred : creds) {
             logger.info("updating cred: " + cred);
@@ -773,39 +795,31 @@ public class RealmAdapter implements RealmModel {
         }
         credentialEntity.setDevice(cred.getDevice());
 
-        mongoStore.updateObject(userEntity);
+        mongoStore.updateObject(userEntity, invocationContext);
     }
 
     @Override
     public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
         DBObject query = new QueryBuilder()
-                .and("socialProvider").is(socialLink.getSocialProvider())
-                .and("socialUsername").is(socialLink.getSocialUsername())
+                .and("socialLinks.socialProvider").is(socialLink.getSocialProvider())
+                .and("socialLinks.socialUsername").is(socialLink.getSocialUsername())
                 .and("realmId").is(getId())
                 .get();
-        SocialLinkEntity socialLinkEntity = mongoStore.loadSingleObject(SocialLinkEntity.class, query);
-
-        if (socialLinkEntity == null) {
-            return null;
-        } else {
-            UserEntity userEntity = mongoStore.loadObject(UserEntity.class, socialLinkEntity.getUserId());
-            // TODO: Programmatically remove binding if userEntity doesn't exists? (There are more similar places where this should be handled)
-            return userEntity==null ? null : new UserAdapter(userEntity, mongoStore);
-        }
+        UserEntity userEntity = mongoStore.loadSingleObject(UserEntity.class, query, invocationContext);
+        return userEntity==null ? null : new UserAdapter(userEntity, mongoStore, invocationContext);
     }
 
     @Override
     public Set<SocialLinkModel> getSocialLinks(UserModel user) {
         UserEntity userEntity = ((UserAdapter)user).getUser();
-        String userId = userEntity.getId();
+        List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
 
-        DBObject query = new QueryBuilder()
-                .and("userId").is(userId)
-                .get();
-        List<SocialLinkEntity> dbSocialLinks = mongoStore.loadObjects(SocialLinkEntity.class, query);
+        if (linkEntities == null) {
+            return Collections.EMPTY_SET;
+        }
 
         Set<SocialLinkModel> result = new HashSet<SocialLinkModel>();
-        for (SocialLinkEntity socialLinkEntity : dbSocialLinks) {
+        for (SocialLinkEntity socialLinkEntity : linkEntities) {
             SocialLinkModel model = new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUsername());
             result.add(model);
         }
@@ -818,26 +832,22 @@ public class RealmAdapter implements RealmModel {
         SocialLinkEntity socialLinkEntity = new SocialLinkEntity();
         socialLinkEntity.setSocialProvider(socialLink.getSocialProvider());
         socialLinkEntity.setSocialUsername(socialLink.getSocialUsername());
-        socialLinkEntity.setUserId(userEntity.getId());
-        socialLinkEntity.setRealmId(getId());
 
-        mongoStore.insertObject(socialLinkEntity);
+        mongoStore.pushItemToList(userEntity, "socialLinks", socialLinkEntity, true, invocationContext);
     }
 
     @Override
     public void removeSocialLink(UserModel user, SocialLinkModel socialLink) {
+        SocialLinkEntity socialLinkEntity = new SocialLinkEntity();
+        socialLinkEntity.setSocialProvider(socialLink.getSocialProvider());
+        socialLinkEntity.setSocialUsername(socialLink.getSocialUsername());
+
         UserEntity userEntity = ((UserAdapter)user).getUser();
-        String userId = userEntity.getId();
-        DBObject query = new QueryBuilder()
-                .and("socialProvider").is(socialLink.getSocialProvider())
-                .and("socialUsername").is(socialLink.getSocialUsername())
-                .and("userId").is(userId)
-                .get();
-        mongoStore.removeObjects(SocialLinkEntity.class, query);
+        mongoStore.pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext);
     }
 
     protected void updateRealm() {
-        mongoStore.updateObject(realm);
+        mongoStore.updateObject(realm, invocationContext);
     }
 
     protected RequiredCredentialModel initRequiredCredentialModel(String type) {
@@ -853,7 +863,7 @@ public class RealmAdapter implements RealmModel {
         DBObject query = new QueryBuilder()
                 .and("realmId").is(getId())
                 .get();
-        List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, query);
+        List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, query, invocationContext);
         return convertUserEntities(users);
     }
 
@@ -893,7 +903,7 @@ public class RealmAdapter implements RealmModel {
                 ).get()
         );
 
-        List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, builder.get());
+        List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, builder.get(), invocationContext);
         return convertUserEntities(users);
     }
 
@@ -915,14 +925,14 @@ public class RealmAdapter implements RealmModel {
                 queryBuilder.and(UserModel.EMAIL).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
             }
         }
-        List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, queryBuilder.get());
+        List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, queryBuilder.get(), invocationContext);
         return convertUserEntities(users);
     }
 
     protected List<UserModel> convertUserEntities(List<UserEntity> userEntities) {
         List<UserModel> userModels = new ArrayList<UserModel>();
         for (UserEntity user : userEntities) {
-            userModels.add(new UserAdapter(user, mongoStore));
+            userModels.add(new UserAdapter(user, mongoStore, invocationContext));
         }
         return userModels;
     }
@@ -950,15 +960,7 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public boolean equals(Object o) {
-        if (o == null) return false;
-        if (!(o instanceof RealmAdapter)) return false;
-        RealmAdapter r = (RealmAdapter)o;
-        return r.getId().equals(getId());
-    }
-
-    @Override
-    public int hashCode() {
-        return getId().hashCode();
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
+        return realm;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
index 21bdc9e..b6be681 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
@@ -9,7 +9,9 @@ import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
 import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
 import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
@@ -21,17 +23,17 @@ import org.keycloak.models.utils.KeycloakModelUtils;
  *
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class RoleAdapter implements RoleModel {
+public class RoleAdapter extends AbstractAdapter implements RoleModel {
 
     private final RoleEntity role;
     private RoleContainerModel roleContainer;
-    private final MongoStore mongoStore;
 
-    public RoleAdapter(RoleEntity roleEntity, MongoStore mongoStore) {
-        this(roleEntity, null, mongoStore);
+    public RoleAdapter(RoleEntity roleEntity, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
+        this(roleEntity, null, mongoStore, invContext);
     }
 
-    public RoleAdapter(RoleEntity roleEntity, RoleContainerModel roleContainer, MongoStore mongoStore) {
+    public RoleAdapter(RoleEntity roleEntity, RoleContainerModel roleContainer, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
+        super(mongoStore, invContext);
         this.role = roleEntity;
         this.roleContainer = roleContainer;
         this.mongoStore = mongoStore;
@@ -66,27 +68,21 @@ public class RoleAdapter implements RoleModel {
 
     @Override
     public boolean isComposite() {
-        return role.isComposite();
-    }
-
-    @Override
-    public void setComposite(boolean flag) {
-        role.setComposite(flag);
-        updateRole();
+        return role.getCompositeRoleIds() != null && role.getCompositeRoleIds().size() > 0;
     }
 
     protected void updateRole() {
-        mongoStore.updateObject(role);
+        mongoStore.updateObject(role, invocationContext);
     }
 
     @Override
     public void addCompositeRole(RoleModel childRole) {
-        mongoStore.pushItemToList(role, "compositeRoleIds", childRole.getId(), true);
+        mongoStore.pushItemToList(role, "compositeRoleIds", childRole.getId(), true, invocationContext);
     }
 
     @Override
     public void removeCompositeRole(RoleModel childRole) {
-        mongoStore.pullItemFromList(role, "compositeRoleIds", childRole.getId());
+        mongoStore.pullItemFromList(role, "compositeRoleIds", childRole.getId(), invocationContext);
     }
 
     @Override
@@ -98,11 +94,11 @@ public class RoleAdapter implements RoleModel {
         DBObject query = new QueryBuilder()
                 .and("_id").in(MongoModelUtils.convertStringsToObjectIds(role.getCompositeRoleIds()))
                 .get();
-        List<RoleEntity> childRoles = mongoStore.loadObjects(RoleEntity.class, query);
+        List<RoleEntity> childRoles = mongoStore.loadObjects(RoleEntity.class, query, invocationContext);
 
         Set<RoleModel> set = new HashSet<RoleModel>();
         for (RoleEntity childRole : childRoles) {
-            set.add(new RoleAdapter(childRole, roleContainer, mongoStore));
+            set.add(new RoleAdapter(childRole, mongoStore, invocationContext));
         }
         return set;
     }
@@ -112,17 +108,17 @@ public class RoleAdapter implements RoleModel {
         if (roleContainer == null) {
             // Compute it
             if (role.getRealmId() != null) {
-                RealmEntity realm = mongoStore.loadObject(RealmEntity.class, role.getRealmId());
+                RealmEntity realm = mongoStore.loadObject(RealmEntity.class, role.getRealmId(), invocationContext);
                 if (realm == null) {
                     throw new IllegalStateException("Realm with id: " + role.getRealmId() + " doesn't exists");
                 }
-                roleContainer = new RealmAdapter(realm, mongoStore);
+                roleContainer = new RealmAdapter(realm, mongoStore, invocationContext);
             } else if (role.getApplicationId() != null) {
-                ApplicationEntity appEntity = mongoStore.loadObject(ApplicationEntity.class, role.getApplicationId());
+                ApplicationEntity appEntity = mongoStore.loadObject(ApplicationEntity.class, role.getApplicationId(), invocationContext);
                 if (appEntity == null) {
                     throw new IllegalStateException("Application with id: " + role.getApplicationId() + " doesn't exists");
                 }
-                roleContainer = new ApplicationAdapter(appEntity, mongoStore);
+                roleContainer = new ApplicationAdapter(appEntity, mongoStore, invocationContext);
             } else {
                 throw new IllegalStateException("Both realmId and applicationId are null for role: " + this);
             }
@@ -144,19 +140,7 @@ public class RoleAdapter implements RoleModel {
     }
 
     @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        RoleAdapter that = (RoleAdapter) o;
-
-        if (!getId().equals(that.getId())) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return getId().hashCode();
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
+        return role;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
index d3fe02f..c66fefa 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
@@ -1,8 +1,9 @@
 package org.keycloak.models.mongo.keycloak.adapters;
 
 import org.keycloak.models.UserModel;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoStore;
-import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.mongo.keycloak.entities.UserEntity;
 
 import java.util.ArrayList;
@@ -18,14 +19,13 @@ import java.util.Set;
  *
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class UserAdapter implements UserModel {
+public class UserAdapter extends AbstractAdapter implements UserModel {
 
     private final UserEntity user;
-    private final MongoStore mongoStore;
 
-    public UserAdapter(UserEntity userEntity, MongoStore mongoStore) {
+    public UserAdapter(UserEntity userEntity, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
+        super(mongoStore, invContext);
         this.user = userEntity;
-        this.mongoStore = mongoStore;
     }
 
     @Override
@@ -139,12 +139,12 @@ public class UserAdapter implements UserModel {
 
     @Override
     public void addWebOrigin(String webOrigin) {
-        mongoStore.pushItemToList(user, "webOrigins", webOrigin, true);
+        mongoStore.pushItemToList(user, "webOrigins", webOrigin, true, invocationContext);
     }
 
     @Override
     public void removeWebOrigin(String webOrigin) {
-        mongoStore.pullItemFromList(user, "webOrigins", webOrigin);
+        mongoStore.pullItemFromList(user, "webOrigins", webOrigin, invocationContext);
     }
 
     @Override
@@ -166,12 +166,12 @@ public class UserAdapter implements UserModel {
 
     @Override
     public void addRedirectUri(String redirectUri) {
-        mongoStore.pushItemToList(user, "redirectUris", redirectUri, true);
+        mongoStore.pushItemToList(user, "redirectUris", redirectUri, true, invocationContext);
     }
 
     @Override
     public void removeRedirectUri(String redirectUri) {
-        mongoStore.pullItemFromList(user, "redirectUris", redirectUri);
+        mongoStore.pullItemFromList(user, "redirectUris", redirectUri, invocationContext);
     }
 
     @Override
@@ -185,12 +185,12 @@ public class UserAdapter implements UserModel {
 
     @Override
     public void addRequiredAction(RequiredAction action) {
-        mongoStore.pushItemToList(user, "requiredActions", action, true);
+        mongoStore.pushItemToList(user, "requiredActions", action, true, invocationContext);
     }
 
     @Override
     public void removeRequiredAction(RequiredAction action) {
-        mongoStore.pullItemFromList(user, "requiredActions", action);
+        mongoStore.pullItemFromList(user, "requiredActions", action, invocationContext);
     }
 
     @Override
@@ -205,6 +205,11 @@ public class UserAdapter implements UserModel {
     }
 
     protected void updateUser() {
-        mongoStore.updateObject(user);
+        mongoStore.updateObject(user, invocationContext);
+    }
+
+    @Override
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
+        return user;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/ApplicationEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/ApplicationEntity.java
index 5791353..1780581 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/ApplicationEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/ApplicationEntity.java
@@ -5,19 +5,19 @@ import java.util.List;
 
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoCollection;
 import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
-import org.keycloak.models.mongo.api.MongoId;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "applications")
-public class ApplicationEntity implements MongoEntity {
+public class ApplicationEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
 
-    private String id;
     private String name;
     private boolean enabled;
     private boolean surrogateAuthRequired;
@@ -30,15 +30,6 @@ public class ApplicationEntity implements MongoEntity {
     // We are using names of defaultRoles (not ids)
     private List<String> defaultRoles = new ArrayList<String>();
 
-    @MongoId
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
     @MongoField
     public String getName() {
         return name;
@@ -112,14 +103,14 @@ public class ApplicationEntity implements MongoEntity {
     }
 
     @Override
-    public void afterRemove(MongoStore mongoStore) {
+    public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invContext) {
         // Remove resourceUser of this application
-        mongoStore.removeObject(UserEntity.class, resourceUserId);
+        mongoStore.removeObject(UserEntity.class, resourceUserId, invContext);
 
         // Remove all roles, which belongs to this application
         DBObject query = new QueryBuilder()
-                .and("applicationId").is(id)
+                .and("applicationId").is(getId())
                 .get();
-        mongoStore.removeObjects(RoleEntity.class, query);
+        mongoStore.removeObjects(RoleEntity.class, query, invContext);
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java
index f29eadc..6ab322d 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java
@@ -1,12 +1,12 @@
 package org.keycloak.models.mongo.keycloak.entities;
 
-import org.keycloak.models.mongo.api.AbstractMongoEntity;
+import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class CredentialEntity extends AbstractMongoEntity {
+public class CredentialEntity implements MongoEntity {
 
     private String type;
     private String value;
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/OAuthClientEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/OAuthClientEntity.java
index a2a902d..52dfece 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/OAuthClientEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/OAuthClientEntity.java
@@ -1,32 +1,23 @@
 package org.keycloak.models.mongo.keycloak.entities;
 
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoCollection;
 import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
-import org.keycloak.models.mongo.api.MongoId;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "oauthClients")
-public class OAuthClientEntity implements MongoEntity {
+public class OAuthClientEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
 
-    private String id;
     private String name;
 
     private String oauthAgentId;
     private String realmId;
 
-    @MongoId
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
     @MongoField
     public String getName() {
         return name;
@@ -55,8 +46,8 @@ public class OAuthClientEntity implements MongoEntity {
     }
 
     @Override
-    public void afterRemove(MongoStore mongoStore) {
+    public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invContext) {
         // Remove user of this oauthClient
-        mongoStore.removeObject(UserEntity.class, oauthAgentId);
+        mongoStore.removeObject(UserEntity.class, oauthAgentId, invContext);
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java
index abb2041..34652c7 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java
@@ -2,11 +2,12 @@ package org.keycloak.models.mongo.keycloak.entities;
 
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoCollection;
 import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
-import org.keycloak.models.mongo.api.MongoId;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -17,9 +18,7 @@ import java.util.Map;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "realms")
-public class RealmEntity implements MongoEntity {
-
-    private String id;
+public class RealmEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
 
     private String name;
     private boolean enabled;
@@ -38,6 +37,9 @@ public class RealmEntity implements MongoEntity {
     private String publicKeyPem;
     private String privateKeyPem;
 
+    private String loginTheme;
+    private String accountTheme;
+
     // We are using names of defaultRoles (not ids)
     private List<String> defaultRoles = new ArrayList<String>();
 
@@ -48,15 +50,6 @@ public class RealmEntity implements MongoEntity {
     private Map<String, String> smtpConfig = new HashMap<String, String>();
     private Map<String, String> socialConfig = new HashMap<String, String>();
 
-    @MongoId
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
     @MongoField
     public String getName() {
         return name;
@@ -184,6 +177,24 @@ public class RealmEntity implements MongoEntity {
     }
 
     @MongoField
+    public String getLoginTheme() {
+        return loginTheme;
+    }
+
+    public void setLoginTheme(String loginTheme) {
+        this.loginTheme = loginTheme;
+    }
+
+    @MongoField
+    public String getAccountTheme() {
+        return accountTheme;
+    }
+
+    public void setAccountTheme(String accountTheme) {
+        this.accountTheme = accountTheme;
+    }
+
+    @MongoField
     public List<String> getDefaultRoles() {
         return defaultRoles;
     }
@@ -238,18 +249,18 @@ public class RealmEntity implements MongoEntity {
     }
 
     @Override
-    public void afterRemove(MongoStore mongoStore) {
+    public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invContext) {
         DBObject query = new QueryBuilder()
-                .and("realmId").is(id)
+                .and("realmId").is(getId())
                 .get();
 
         // Remove all users of this realm
-        mongoStore.removeObjects(UserEntity.class, query);
+        mongoStore.removeObjects(UserEntity.class, query, invContext);
 
         // Remove all roles of this realm
-        mongoStore.removeObjects(RoleEntity.class, query);
+        mongoStore.removeObjects(RoleEntity.class, query, invContext);
 
         // Remove all applications of this realm
-        mongoStore.removeObjects(ApplicationEntity.class, query);
+        mongoStore.removeObjects(ApplicationEntity.class, query, invContext);
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RequiredCredentialEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RequiredCredentialEntity.java
index f36f438..f39e327 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RequiredCredentialEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RequiredCredentialEntity.java
@@ -1,12 +1,12 @@
 package org.keycloak.models.mongo.keycloak.entities;
 
-import org.keycloak.models.mongo.api.AbstractMongoEntity;
+import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class RequiredCredentialEntity extends AbstractMongoEntity {
+public class RequiredCredentialEntity implements MongoEntity {
 
     private String type;
     private boolean input;
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RoleEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RoleEntity.java
index 2c367c6..1aaeac2 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RoleEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RoleEntity.java
@@ -3,11 +3,12 @@ package org.keycloak.models.mongo.keycloak.entities;
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.jboss.logging.Logger;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoCollection;
 import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
-import org.keycloak.models.mongo.api.MongoId;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 
 import java.util.List;
 
@@ -15,29 +16,18 @@ import java.util.List;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "roles")
-public class RoleEntity implements MongoEntity {
+public class RoleEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
 
     private static final Logger logger = Logger.getLogger(RoleEntity.class);
 
-    private String id;
     private String name;
     private String description;
-    private boolean composite;
 
     private List<String> compositeRoleIds;
 
     private String realmId;
     private String applicationId;
 
-    @MongoId
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
     @MongoField
     public String getName() {
         return name;
@@ -57,15 +47,6 @@ public class RoleEntity implements MongoEntity {
     }
 
     @MongoField
-    public boolean isComposite() {
-        return composite;
-    }
-
-    public void setComposite(boolean composite) {
-        this.composite = composite;
-    }
-
-    @MongoField
     public List<String> getCompositeRoleIds() {
         return compositeRoleIds;
     }
@@ -93,46 +74,46 @@ public class RoleEntity implements MongoEntity {
     }
 
     @Override
-    public void afterRemove(MongoStore mongoStore) {
+    public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invContext) {
         // Remove this role from all users, which has it
         DBObject query = new QueryBuilder()
-                .and("roleIds").is(id)
+                .and("roleIds").is(getId())
                 .get();
 
-        List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, query);
+        List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, query, invContext);
         for (UserEntity user : users) {
             logger.info("Removing role " + getName() + " from user " + user.getLoginName());
-            mongoStore.pullItemFromList(user, "roleIds", getId());
+            mongoStore.pullItemFromList(user, "roleIds", getId(), invContext);
         }
 
         // Remove this scope from all users, which has it
         query = new QueryBuilder()
-                .and("scopeIds").is(id)
+                .and("scopeIds").is(getId())
                 .get();
 
-        users = mongoStore.loadObjects(UserEntity.class, query);
+        users = mongoStore.loadObjects(UserEntity.class, query, invContext);
         for (UserEntity user : users) {
             logger.info("Removing scope " + getName() + " from user " + user.getLoginName());
-            mongoStore.pullItemFromList(user, "scopeIds", getId());
+            mongoStore.pullItemFromList(user, "scopeIds", getId(), invContext);
         }
 
         // Remove defaultRoles from realm
         if (realmId != null) {
-            RealmEntity realmEntity = mongoStore.loadObject(RealmEntity.class, realmId);
+            RealmEntity realmEntity = mongoStore.loadObject(RealmEntity.class, realmId, invContext);
 
             // Realm might be already removed at this point
             if (realmEntity != null) {
-                mongoStore.pullItemFromList(realmEntity, "defaultRoles", getId());
+                mongoStore.pullItemFromList(realmEntity, "defaultRoles", getId(), invContext);
             }
         }
 
         // Remove defaultRoles from application
         if (applicationId != null) {
-            ApplicationEntity appEntity = mongoStore.loadObject(ApplicationEntity.class, applicationId);
+            ApplicationEntity appEntity = mongoStore.loadObject(ApplicationEntity.class, applicationId, invContext);
 
             // Application might be already removed at this point
             if (appEntity != null) {
-                mongoStore.pullItemFromList(appEntity, "defaultRoles", getId());
+                mongoStore.pullItemFromList(appEntity, "defaultRoles", getId(), invContext);
             }
         }
 
@@ -140,9 +121,9 @@ public class RoleEntity implements MongoEntity {
         query = new QueryBuilder()
                 .and("compositeRoleIds").is(getId())
                 .get();
-        List<RoleEntity> parentRoles = mongoStore.loadObjects(RoleEntity.class, query);
+        List<RoleEntity> parentRoles = mongoStore.loadObjects(RoleEntity.class, query, invContext);
         for (RoleEntity role : parentRoles) {
-            mongoStore.pullItemFromList(role, "compositeRoleIds", getId());
+            mongoStore.pullItemFromList(role, "compositeRoleIds", getId(), invContext);
         }
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java
index 3a34a25..85ae5c0 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java
@@ -1,21 +1,15 @@
 package org.keycloak.models.mongo.keycloak.entities;
 
-import org.keycloak.models.mongo.api.AbstractMongoEntity;
-import org.keycloak.models.mongo.api.MongoCollection;
+import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-@MongoCollection(collectionName = "socialLinks")
-public class SocialLinkEntity extends AbstractMongoEntity {
+public class SocialLinkEntity implements MongoEntity {
 
     private String socialUsername;
     private String socialProvider;
-    private String userId;
-    // realmId is needed to allow searching as combination socialUsername+socialProvider may not be unique
-    // (Same user could have mapped same facebook account to username "foo" in "realm1" and to username "bar" in "realm2")
-    private String realmId;
 
     @MongoField
     public String getSocialUsername() {
@@ -35,21 +29,30 @@ public class SocialLinkEntity extends AbstractMongoEntity {
         this.socialProvider = socialProvider;
     }
 
-    @MongoField
-    public String getUserId() {
-        return userId;
-    }
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
 
-    public void setUserId(String userId) {
-        this.userId = userId;
-    }
+        SocialLinkEntity that = (SocialLinkEntity) o;
 
-    @MongoField
-    public String getRealmId() {
-        return realmId;
+        if (socialProvider != null && (that.socialProvider == null || !socialProvider.equals(that.socialProvider))) return false;
+        if (socialUsername != null && (that.socialUsername == null || !socialUsername.equals(that.socialUsername))) return false;
+        if (socialProvider == null && that.socialProvider != null)return false;
+        if (socialUsername == null && that.socialUsername != null) return false;
+
+        return true;
     }
 
-    public void setRealmId(String realmId) {
-        this.realmId = realmId;
+    @Override
+    public int hashCode() {
+        int code = 1;
+        if (socialUsername != null) {
+            code = code * 13;
+        }
+        if (socialProvider != null) {
+            code = code * 17;
+        }
+        return code;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java
index fc90f3b..fdd69a2 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java
@@ -1,16 +1,12 @@
 package org.keycloak.models.mongo.keycloak.entities;
 
-import com.mongodb.DBObject;
-import com.mongodb.QueryBuilder;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoCollection;
 import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
-import org.keycloak.models.mongo.api.MongoId;
-import org.keycloak.models.mongo.api.MongoStore;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -18,9 +14,8 @@ import java.util.Map;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "users")
-public class UserEntity implements MongoEntity {
+public class UserEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
 
-    private String id;
     private String loginName;
     private String firstName;
     private String lastName;
@@ -39,15 +34,7 @@ public class UserEntity implements MongoEntity {
     private List<String> redirectUris;
     private List<UserModel.RequiredAction> requiredActions;
     private List<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
-
-    @MongoId
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
+    private List<SocialLinkEntity> socialLinks;
 
     @MongoField
     public String getLoginName() {
@@ -184,13 +171,12 @@ public class UserEntity implements MongoEntity {
         this.credentials = credentials;
     }
 
-    @Override
-    public void afterRemove(MongoStore mongoStore) {
-        DBObject query = new QueryBuilder()
-                .and("userId").is(id)
-                .get();
+    @MongoField
+    public List<SocialLinkEntity> getSocialLinks() {
+        return socialLinks;
+    }
 
-        // Remove social links of this user
-        mongoStore.removeObjects(SocialLinkEntity.class, query);
+    public void setSocialLinks(List<SocialLinkEntity> socialLinks) {
+        this.socialLinks = socialLinks;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
index 260d182..fe4387f 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
@@ -10,6 +10,7 @@ import com.mongodb.QueryBuilder;
 import org.bson.types.ObjectId;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.mongo.keycloak.adapters.UserAdapter;
 import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
 import org.keycloak.models.mongo.keycloak.entities.UserEntity;
@@ -28,7 +29,7 @@ public class MongoModelUtils {
     }
 
     // Get everything including both application and realm roles
-    public static List<RoleEntity> getAllRolesOfUser(UserModel user, MongoStore mongoStore) {
+    public static List<RoleEntity> getAllRolesOfUser(UserModel user, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
         UserEntity userEntity = ((UserAdapter)user).getUser();
         List<String> roleIds = userEntity.getRoleIds();
 
@@ -39,11 +40,11 @@ public class MongoModelUtils {
         DBObject query = new QueryBuilder()
                 .and("_id").in(convertStringsToObjectIds(roleIds))
                 .get();
-        return mongoStore.loadObjects(RoleEntity.class, query);
+        return mongoStore.loadObjects(RoleEntity.class, query, invContext);
     }
 
     // Get everything including both application and realm scopes
-    public static List<RoleEntity> getAllScopesOfUser(UserModel user, MongoStore mongoStore) {
+    public static List<RoleEntity> getAllScopesOfUser(UserModel user, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
         UserEntity userEntity = ((UserAdapter)user).getUser();
         List<String> scopeIds = userEntity.getScopeIds();
 
@@ -54,6 +55,6 @@ public class MongoModelUtils {
         DBObject query = new QueryBuilder()
                 .and("_id").in(convertStringsToObjectIds(scopeIds))
                 .get();
-        return mongoStore.loadObjects(RoleEntity.class, query);
+        return mongoStore.loadObjects(RoleEntity.class, query, invContext);
     }
 }
diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java
index d68ce0d..81bd7d8 100755
--- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java
+++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java
@@ -1,6 +1,6 @@
 package org.keycloak.models.mongo.test;
 
-import org.keycloak.models.mongo.api.AbstractMongoEntity;
+import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoField;
 
 import java.util.List;
@@ -8,7 +8,7 @@ import java.util.List;
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class Address extends AbstractMongoEntity {
+public class Address implements MongoEntity {
 
     private String street;
     private int number;
diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java
index 88f1745..b2d093d 100755
--- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java
+++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java
@@ -10,7 +10,10 @@ import org.junit.Before;
 import org.junit.Test;
 import org.keycloak.models.mongo.api.MongoEntity;
 import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.mongo.impl.MongoStoreImpl;
+import org.keycloak.models.mongo.impl.context.SimpleMongoStoreInvocationContext;
+import org.keycloak.models.mongo.impl.context.TransactionMongoStoreInvocationContext;
 
 import java.net.UnknownHostException;
 import java.util.ArrayList;
@@ -28,7 +31,7 @@ public class MongoDBModelTest {
     };
 
     private MongoClient mongoClient;
-    private MongoStore mongoDB;
+    private MongoStore mongoStore;
 
     @Before
     public void before() throws Exception {
@@ -37,7 +40,7 @@ public class MongoDBModelTest {
             mongoClient = new MongoClient("localhost", 27017);
 
             DB db = mongoClient.getDB("keycloakTest");
-            mongoDB = new MongoStoreImpl(db, true, MANAGED_DATA_TYPES);
+            mongoStore = new MongoStoreImpl(db, true, MANAGED_DATA_TYPES);
 
         } catch (UnknownHostException e) {
             throw new RuntimeException(e);
@@ -51,23 +54,25 @@ public class MongoDBModelTest {
 
     @Test
     public void mongoModelTest() throws Exception {
+        MongoStoreInvocationContext context = new TransactionMongoStoreInvocationContext(mongoStore);
+
         // Add some user
         Person john = new Person();
         john.setFirstName("john");
         john.setAge(25);
         john.setGender(Person.Gender.MALE);
 
-        mongoDB.insertObject(john);
+        mongoStore.insertObject(john, context);
 
         // Add another user
         Person mary = new Person();
         mary.setFirstName("mary");
-        mary.setKids(Arrays.asList("Peter", "Paul", "Wendy"));
+        mary.setKids(asList("Peter", "Paul", "Wendy"));
 
         Address addr1 = new Address();
         addr1.setStreet("Elm");
         addr1.setNumber(5);
-        addr1.setFlatNumbers(Arrays.asList("flat1", "flat2"));
+        addr1.setFlatNumbers(asList("flat1", "flat2"));
         Address addr2 = new Address();
         List<Address> addresses = new ArrayList<Address>();
         addresses.add(addr1);
@@ -76,14 +81,14 @@ public class MongoDBModelTest {
         mary.setAddresses(addresses);
         mary.setMainAddress(addr1);
         mary.setGender(Person.Gender.FEMALE);
-        mary.setGenders(Arrays.asList(Person.Gender.FEMALE));
+        mary.setGenders(asList(Person.Gender.FEMALE));
 
-        mongoDB.insertObject(mary);
+        mongoStore.insertObject(mary, context);
 
-        Assert.assertEquals(2, mongoDB.loadObjects(Person.class, new QueryBuilder().get()).size());
+        Assert.assertEquals(2, mongoStore.loadObjects(Person.class, new QueryBuilder().get(), context).size());
 
         DBObject query = new QueryBuilder().and("addresses.flatNumbers").is("flat1").get();
-        List<Person> persons = mongoDB.loadObjects(Person.class, query);
+        List<Person> persons = mongoStore.loadObjects(Person.class, query, context);
         Assert.assertEquals(1, persons.size());
         mary = persons.get(0);
         Assert.assertEquals(mary.getFirstName(), "mary");
@@ -92,15 +97,15 @@ public class MongoDBModelTest {
         Assert.assertEquals(Address.class, mary.getAddresses().get(0).getClass());
 
         // Test push/pull
-        mongoDB.pushItemToList(mary, "kids", "Pauline", true);
-        mongoDB.pullItemFromList(mary, "kids", "Paul");
+        mongoStore.pushItemToList(mary, "kids", "Pauline", true, context);
+        mongoStore.pullItemFromList(mary, "kids", "Paul", context);
 
         Address addr3 = new Address();
         addr3.setNumber(6);
         addr3.setStreet("Broadway");
-        mongoDB.pushItemToList(mary, "addresses", addr3, true);
+        mongoStore.pushItemToList(mary, "addresses", addr3, true, context);
 
-        mary = mongoDB.loadObject(Person.class, mary.getId());
+        mary = mongoStore.loadObject(Person.class, mary.getId(), context);
         Assert.assertEquals(3, mary.getKids().size());
         Assert.assertTrue(mary.getKids().contains("Pauline"));
         Assert.assertFalse(mary.getKids().contains("Paul"));
@@ -116,18 +121,26 @@ public class MongoDBModelTest {
         mary.addAttribute("attr1", "value1");
         mary.addAttribute("attr2", "value2");
         mary.addAttribute("attr.some3", "value3");
-        mongoDB.updateObject(mary);
+        mongoStore.updateObject(mary, context);
 
-        mary = mongoDB.loadObject(Person.class, mary.getId());
+        mary = mongoStore.loadObject(Person.class, mary.getId(), context);
         Assert.assertEquals(3, mary.getAttributes().size());
 
         mary.removeAttribute("attr2");
         mary.removeAttribute("nonExisting");
-        mongoDB.updateObject(mary);
+        mongoStore.updateObject(mary, context);
 
-        mary = mongoDB.loadObject(Person.class, mary.getId());
+        mary = mongoStore.loadObject(Person.class, mary.getId(), context);
         Assert.assertEquals(2, mary.getAttributes().size());
         Assert.assertEquals("value1", mary.getAttributes().get("attr1"));
         Assert.assertEquals("value3", mary.getAttributes().get("attr.some3"));
+
+        context.commit();
+    }
+
+    private <T> List<T> asList(T... objects) {
+        List<T> list = new ArrayList<T>();
+        list.addAll(Arrays.asList(objects));
+        return list;
     }
 }
diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java
index 7298c6e..47ac69b 100755
--- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java
+++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java
@@ -1,9 +1,8 @@
 package org.keycloak.models.mongo.test;
 
-import org.keycloak.models.mongo.api.AbstractMongoEntity;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
 import org.keycloak.models.mongo.api.MongoCollection;
 import org.keycloak.models.mongo.api.MongoField;
-import org.keycloak.models.mongo.api.MongoId;
 
 import java.util.HashMap;
 import java.util.List;
@@ -13,9 +12,8 @@ import java.util.Map;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "persons")
-public class Person extends AbstractMongoEntity {
+public class Person extends AbstractMongoIdentifiableEntity {
 
-    private String id;
     private String firstName;
     private int age;
     private List<String> kids;
@@ -25,16 +23,6 @@ public class Person extends AbstractMongoEntity {
     private List<Gender> genders;
     private Map<String, String> attributes = new HashMap<String, String>();
 
-
-    @MongoId
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
     @MongoField
     public String getFirstName() {
         return firstName;
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 60ee3b8..bc937d7 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -11,6 +11,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.SkeletonKeyToken;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.resources.AccountService;
@@ -41,7 +42,7 @@ public class AuthenticationManager {
 
     public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) {
         SkeletonKeyToken token = new SkeletonKeyToken();
-        token.id(RealmManager.generateId());
+        token.id(KeycloakModelUtils.generateId());
         token.issuedNow();
         token.principal(username);
         token.audience(realm.getName());
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 4611599..b9c0e64 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -13,6 +13,7 @@ import org.keycloak.models.SocialLinkModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserModel.RequiredAction;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.OAuthClientRepresentation;
@@ -44,11 +45,6 @@ import java.util.concurrent.atomic.AtomicLong;
  */
 public class RealmManager {
     protected static final Logger logger = Logger.getLogger(RealmManager.class);
-    private static AtomicLong counter = new AtomicLong(1);
-
-    public static String generateId() {
-        return counter.getAndIncrement() + "-" + System.currentTimeMillis();
-    }
 
     protected KeycloakSession identitySession;
 
@@ -73,7 +69,7 @@ public class RealmManager {
     }
 
     public RealmModel createRealm(String id, String name) {
-        if (id == null) id = generateId();
+        if (id == null) id = KeycloakModelUtils.generateId();
         RealmModel realm = identitySession.createRealm(id, name);
         realm.setName(name);
         realm.addRole(Constants.APPLICATION_ROLE);
@@ -166,7 +162,7 @@ public class RealmManager {
     public RealmModel importRealm(RealmRepresentation rep, UserModel realmCreator) {
         String id = rep.getId();
         if (id == null) {
-            id = generateId();
+            id = KeycloakModelUtils.generateId();
         }
         RealmModel realm = createRealm(id, rep.getRealm());
         importRealm(rep, realm);
diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
index 87d4025..a7eeb86 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -3,9 +3,11 @@ package org.keycloak.services.managers;
 import org.jboss.resteasy.logging.Logger;
 import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.Constants;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.SkeletonKeyScope;
 import org.keycloak.representations.SkeletonKeyToken;
 import org.keycloak.util.Base64Url;
@@ -14,6 +16,7 @@ import org.keycloak.util.JsonSerialization;
 import javax.ws.rs.core.MultivaluedMap;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -132,7 +135,7 @@ public class TokenManager {
 
     protected SkeletonKeyToken initToken(RealmModel realm, UserModel client, UserModel user) {
         SkeletonKeyToken token = new SkeletonKeyToken();
-        token.id(RealmManager.generateId());
+        token.id(KeycloakModelUtils.generateId());
         token.principal(user.getLoginName());
         token.audience(realm.getName());
         token.issuedNow();
@@ -219,7 +222,7 @@ public class TokenManager {
 
     public SkeletonKeyToken createAccessToken(RealmModel realm, UserModel user) {
         SkeletonKeyToken token = new SkeletonKeyToken();
-        token.id(RealmManager.generateId());
+        token.id(KeycloakModelUtils.generateId());
         token.issuedNow();
         token.principal(user.getLoginName());
         token.audience(realm.getName());
diff --git a/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java b/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java
index 404e8f2..0a9197b 100755
--- a/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java
+++ b/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java
@@ -10,7 +10,7 @@ import org.keycloak.models.UserModel.RequiredAction;
 import org.keycloak.models.utils.TimeBasedOTP;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
-import org.keycloak.test.common.AbstractKeycloakTest;
+import org.keycloak.test.AbstractKeycloakTest;
 
 import javax.ws.rs.core.MultivaluedHashMap;
 import javax.ws.rs.core.MultivaluedMap;
diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java
index 1e45914..e729554 100755
--- a/services/src/test/java/org/keycloak/test/AdapterTest.java
+++ b/services/src/test/java/org/keycloak/test/AdapterTest.java
@@ -17,7 +17,6 @@ import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.ApplianceBootstrap;
 import org.keycloak.services.managers.OAuthClientManager;
 import org.keycloak.services.managers.RealmManager;
-import org.keycloak.test.common.AbstractKeycloakTest;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/services/src/test/java/org/keycloak/test/ApplicationModelTest.java b/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
index 8c3c3f2..783d967 100755
--- a/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
+++ b/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
@@ -9,7 +9,6 @@ import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.services.managers.ApplicationManager;
-import org.keycloak.test.common.AbstractKeycloakTest;
 
 import java.util.Iterator;
 import java.util.List;
diff --git a/services/src/test/java/org/keycloak/test/CompositeRolesModelTest.java b/services/src/test/java/org/keycloak/test/CompositeRolesModelTest.java
new file mode 100644
index 0000000..898a183
--- /dev/null
+++ b/services/src/test/java/org/keycloak/test/CompositeRolesModelTest.java
@@ -0,0 +1,105 @@
+package org.keycloak.test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CompositeRolesModelTest extends AbstractKeycloakTest {
+
+    public CompositeRolesModelTest(String providerId) {
+        super(providerId);
+    }
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+        RealmManager manager = realmManager;
+        RealmRepresentation rep = AbstractKeycloakServerTest.loadJson("testcomposites.json");
+        RealmModel realm = manager.createRealm("Test", rep.getRealm());
+        manager.importRealm(rep, realm);
+    }
+
+    @Test
+    public void testAppComposites() {
+        Set<RoleModel> requestedRoles = getRequestedRoles("APP_COMPOSITE_APPLICATION", "APP_COMPOSITE_USER");
+        Assert.assertEquals(2, requestedRoles.size());
+
+        RoleModel expectedRole1 = getRole("APP_ROLE_APPLICATION", "APP_ROLE_1");
+        RoleModel expectedRole2 = getRole("realm", "REALM_ROLE_1");
+
+        assertContains(requestedRoles, expectedRole1);
+        assertContains(requestedRoles, expectedRole2);
+    }
+
+    // TODO: more tests...
+
+    // Same algorithm as in TokenManager.createAccessCode
+    private Set<RoleModel> getRequestedRoles(String applicationName, String username) {
+        Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
+
+        RealmModel realm = realmManager.getRealm("Test");
+        UserModel user = realm.getUser(username);
+        ApplicationModel application = realm.getApplicationByName(applicationName);
+
+        Set<RoleModel> roleMappings = realm.getRoleMappings(user);
+        Set<RoleModel> scopeMappings = realm.getScopeMappings(application.getApplicationUser());
+        Set<RoleModel> appRoles = application.getRoles();
+        if (appRoles != null) scopeMappings.addAll(appRoles);
+
+        for (RoleModel role : roleMappings) {
+            if (role.getContainer().equals(application)) requestedRoles.add(role);
+            for (RoleModel desiredRole : scopeMappings) {
+                Set<RoleModel> visited = new HashSet<RoleModel>();
+                applyScope(role, desiredRole, visited, requestedRoles);
+            }
+        }
+
+        return requestedRoles;
+    }
+
+    private static void applyScope(RoleModel role, RoleModel scope, Set<RoleModel> visited, Set<RoleModel> requested) {
+        if (visited.contains(scope)) return;
+        visited.add(scope);
+        if (role.hasRole(scope)) {
+            requested.add(scope);
+            return;
+        }
+        if (!scope.isComposite()) return;
+
+        for (RoleModel contained : scope.getComposites()) {
+            applyScope(role, contained, visited, requested);
+        }
+    }
+
+    private RoleModel getRole(String appName, String roleName) {
+        RealmModel realm = realmManager.getRealm("Test");
+        if ("realm".equals(appName)) {
+            return realm.getRole(roleName);
+        }  else {
+            return realm.getApplicationByName(appName).getRole(roleName);
+        }
+    }
+
+    private void assertContains(Set<RoleModel> requestedRoles, RoleModel expectedRole) {
+        Assert.assertTrue(requestedRoles.contains(expectedRole));
+
+        // Check if requestedRole has correct role container
+        for (RoleModel role : requestedRoles) {
+            if (role.equals(expectedRole)) {
+                Assert.assertEquals(role.getContainer(), expectedRole.getContainer());
+            }
+        }
+    }
+}
diff --git a/services/src/test/java/org/keycloak/test/ImportTest.java b/services/src/test/java/org/keycloak/test/ImportTest.java
index 8b7287c..d8977ff 100755
--- a/services/src/test/java/org/keycloak/test/ImportTest.java
+++ b/services/src/test/java/org/keycloak/test/ImportTest.java
@@ -5,6 +5,7 @@ import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runners.MethodSorters;
 import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.Constants;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.RoleModel;
@@ -12,9 +13,9 @@ import org.keycloak.models.SocialLinkModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.RealmManager;
-import org.keycloak.test.common.AbstractKeycloakTest;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -56,14 +57,68 @@ public class ImportTest extends AbstractKeycloakTest {
         List<ApplicationModel> resources = realm.getApplications();
         Assert.assertEquals(3, resources.size());
 
+        // Test applications imported
+        ApplicationModel application = realm.getApplicationByName("Application");
+        ApplicationModel otherApp = realm.getApplicationByName("OtherApp");
+        ApplicationModel accountApp = realm.getApplicationByName(Constants.ACCOUNT_APPLICATION);
+        ApplicationModel nonExisting = realm.getApplicationByName("NonExisting");
+        Assert.assertNotNull(application);
+        Assert.assertNotNull(otherApp);
+        Assert.assertNull(nonExisting);
+        Map<String, ApplicationModel> apps = realm.getApplicationNameMap();
+        Assert.assertEquals(3, apps.size());
+        Assert.assertTrue(apps.values().contains(application));
+        Assert.assertTrue(apps.values().contains(otherApp));
+        Assert.assertTrue(apps.values().contains(accountApp));
+        realm.getApplications().containsAll(apps.values());
+
+        // Test finding applications by ID
+        Assert.assertNull(realm.getApplicationById("982734"));
+        Assert.assertEquals(application, realm.getApplicationById(application.getId()));
+
+
+        // Test role mappings
+        UserModel admin = realm.getUser("admin");
+        Set<RoleModel> allRoles = realm.getRoleMappings(admin);
+        Assert.assertEquals(5, allRoles.size());
+        Assert.assertTrue(allRoles.contains(realm.getRole("admin")));
+        Assert.assertTrue(allRoles.contains(application.getRole("app-admin")));
+        Assert.assertTrue(allRoles.contains(otherApp.getRole("otherapp-admin")));
+        Assert.assertTrue(allRoles.contains(accountApp.getRole(Constants.ACCOUNT_PROFILE_ROLE)));
+        Assert.assertTrue(allRoles.contains(accountApp.getRole(Constants.ACCOUNT_MANAGE_ROLE)));
+
+        UserModel wburke = realm.getUser("wburke");
+        allRoles = realm.getRoleMappings(wburke);
+        Assert.assertEquals(4, allRoles.size());
+        Assert.assertFalse(allRoles.contains(realm.getRole("admin")));
+        Assert.assertTrue(allRoles.contains(application.getRole("app-user")));
+        Assert.assertTrue(allRoles.contains(otherApp.getRole("otherapp-user")));
+
+        Assert.assertEquals(0, realm.getRealmRoleMappings(wburke).size());
+
+        Set<RoleModel> realmRoles = realm.getRealmRoleMappings(admin);
+        Assert.assertEquals(1, realmRoles.size());
+        Assert.assertEquals("admin", realmRoles.iterator().next().getName());
+
+        Set<RoleModel> appRoles = application.getApplicationRoleMappings(admin);
+        Assert.assertEquals(1, appRoles.size());
+        Assert.assertEquals("app-admin", appRoles.iterator().next().getName());
+
+
         // Test scope relationship
-        ApplicationModel application = realm.getApplicationNameMap().get("Application");
         UserModel oauthClient = realm.getUser("oauthclient");
-        Assert.assertNotNull(application);
         Assert.assertNotNull(oauthClient);
+        Set<RoleModel> allScopes = realm.getScopeMappings(oauthClient);
+        Assert.assertEquals(2, allScopes.size());
+        Assert.assertTrue(allScopes.contains(realm.getRole("admin")));
+        Assert.assertTrue(allScopes.contains(application.getRole("app-user")));
+
+        Set<RoleModel> realmScopes = realm.getRealmScopeMappings(oauthClient);
+        Assert.assertTrue(realmScopes.contains(realm.getRole("admin")));
+
         Set<RoleModel> appScopes = application.getApplicationScopeMappings(oauthClient);
-        RoleModel appUserRole = application.getRole("user");
-        Assert.assertTrue(appScopes.contains(appUserRole));
+        Assert.assertTrue(appScopes.contains(application.getRole("app-user")));
+
 
         // Test social linking
         UserModel socialUser = realm.getUser("mySocialUser");
@@ -86,6 +141,8 @@ public class ImportTest extends AbstractKeycloakTest {
         Assert.assertEquals(foundSocialUser.getLoginName(), socialUser.getLoginName());
         Assert.assertNull(realm.getUserBySocialLink(new SocialLinkModel("facebook", "not-existing")));
 
+
+
     }
 
     @Test
diff --git a/services/src/test/java/org/keycloak/test/ModelTest.java b/services/src/test/java/org/keycloak/test/ModelTest.java
index 1c9b5d5..b244d80 100755
--- a/services/src/test/java/org/keycloak/test/ModelTest.java
+++ b/services/src/test/java/org/keycloak/test/ModelTest.java
@@ -7,7 +7,6 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.ModelToRepresentation;
-import org.keycloak.test.common.AbstractKeycloakTest;
 
 import java.util.HashMap;
 import java.util.Iterator;
diff --git a/services/src/test/java/org/keycloak/test/UserModelTest.java b/services/src/test/java/org/keycloak/test/UserModelTest.java
index d808174..cf50478 100755
--- a/services/src/test/java/org/keycloak/test/UserModelTest.java
+++ b/services/src/test/java/org/keycloak/test/UserModelTest.java
@@ -7,7 +7,6 @@ import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserModel.RequiredAction;
 import org.keycloak.services.managers.RealmManager;
-import org.keycloak.test.common.AbstractKeycloakTest;
 
 import java.util.Iterator;
 import java.util.List;
diff --git a/services/src/test/resources/testcomposites.json b/services/src/test/resources/testcomposites.json
new file mode 100644
index 0000000..73e4300
--- /dev/null
+++ b/services/src/test/resources/testcomposites.json
@@ -0,0 +1,231 @@
+{
+    "id": "Test",
+    "realm": "Test",
+    "enabled": true,
+    "tokenLifespan": 600,
+    "accessCodeLifespan": 600,
+    "accessCodeLifespanUserAction": 600,
+    "sslNotRequired": true,
+    "registrationAllowed": true,
+    "resetPasswordAllowed": true,
+    "requiredCredentials": [ "password" ],
+    "requiredApplicationCredentials": [ "password" ],
+    "requiredOAuthClientCredentials": [ "password" ],
+    "smtpServer": {
+        "from": "auto@keycloak.org",
+        "host": "localhost",
+        "port":"3025"
+    },
+    "users" : [
+        {
+            "username" : "REALM_COMPOSITE_1_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "REALM_ROLE_1_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "REALM_APP_COMPOSITE_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "REALM_APP_ROLE_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "APP_COMPOSITE_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        }
+    ],
+    "oauthClients" : [
+        {
+            "name" : "third-party",
+            "enabled": true,
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        }
+    ],
+    "roleMappings": [
+        {
+            "username": "REALM_COMPOSITE_1_USER",
+            "roles": ["REALM_COMPOSITE_1"]
+        },
+        {
+            "username": "REALM_ROLE_1_USER",
+            "roles": ["REALM_ROLE_1"]
+        },
+        {
+            "username": "REALM_APP_COMPOSITE_USER",
+            "roles": ["REALM_APP_COMPOSITE_ROLE"]
+        },
+        {
+            "username": "APP_COMPOSITE_USER",
+            "roles": ["REALM_APP_COMPOSITE_ROLE", "REALM_COMPOSITE_1"]
+        }
+    ],
+    "scopeMappings": [
+        {
+            "username": "REALM_COMPOSITE_1_APPLICATION",
+            "roles": ["REALM_COMPOSITE_1"]
+        },
+        {
+            "username": "REALM_ROLE_1_APPLICATION",
+            "roles": ["REALM_ROLE_1"]
+        }
+    ],
+    "applications": [
+        {
+            "name": "REALM_COMPOSITE_1_APPLICATION",
+            "enabled": true,
+            "baseUrl": "http://localhost:8081/app",
+            "adminUrl": "http://localhost:8081/app/logout",
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+         },
+        {
+            "name": "REALM_ROLE_1_APPLICATION",
+            "enabled": true,
+            "baseUrl": "http://localhost:8081/app",
+            "adminUrl": "http://localhost:8081/app/logout",
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+        },
+        {
+            "name": "APP_ROLE_APPLICATION",
+            "enabled": true,
+            "baseUrl": "http://localhost:8081/app",
+            "adminUrl": "http://localhost:8081/app/logout",
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+        },
+        {
+            "name": "APP_COMPOSITE_APPLICATION",
+            "enabled": true,
+            "baseUrl": "http://localhost:8081/app",
+            "adminUrl": "http://localhost:8081/app/logout",
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "REALM_ROLE_1"
+            },
+            {
+                "name": "REALM_ROLE_2"
+            },
+            {
+                "name": "REALM_ROLE_3"
+            },
+            {
+                "name": "REALM_COMPOSITE_1",
+                "composites": {
+                    "realm": ["REALM_ROLE_1"]
+                }
+            },
+            {
+                "name": "REALM_APP_COMPOSITE_ROLE",
+                "composites": {
+                    "application": {
+                        "APP_ROLE_APPLICATION" :[
+                            "APP_ROLE_1"
+                        ]
+                    }
+                }
+            }
+        ],
+        "application" : {
+            "APP_ROLE_APPLICATION" : [
+                {
+                    "name": "APP_ROLE_1"
+                },
+                {
+                    "name": "APP_ROLE_2"
+                }
+            ],
+            "APP_COMPOSITE_APPLICATION" : [
+                {
+                    "name": "APP_COMPOSITE_ROLE",
+                    "composites": {
+                        "realm" : [
+                            "REALM_ROLE_1",
+                            "REALM_ROLE_2",
+                            "REALM_ROLE_3"
+                        ],
+                        "application": {
+                            "APP_ROLE_APPLICATION" :[
+                                "APP_ROLE_1"
+                            ]
+                        }
+                    }
+                },
+                {
+                    "name": "APP_ROLE_2"
+                }
+            ]
+        }
+
+    },
+
+    "applicationRoleMappings": {
+        "APP_ROLE_APPLICATION": [
+            {
+                "username": "REALM_APP_ROLE_USER",
+                "roles": ["APP_ROLE_2"]
+            }
+        ]
+    },
+    "applicationScopeMappings": {
+        "APP_ROLE_APPLICATION": [
+            {
+                "username": "APP_COMPOSITE_APPLICATION",
+                "roles": ["APP_ROLE_2"]
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/services/src/test/resources/testrealm.json b/services/src/test/resources/testrealm.json
index 76f90a9..16ccf46 100755
--- a/services/src/test/resources/testrealm.json
+++ b/services/src/test/resources/testrealm.json
@@ -44,16 +44,6 @@
             ]
         },
         {
-            "username": "oauthclient",
-            "enabled": true,
-            "credentials": [
-                {
-                    "type": "password",
-                    "value": "clientpassword"
-                }
-            ]
-        },
-        {
             "username": "mySocialUser",
             "enabled": true
         }
@@ -88,6 +78,16 @@
         }
 
     ],
+    "oauthClients" : [
+        {
+            "name" : "oauthclient",
+            "enabled": true,
+            "credentials" : [
+                { "type" : "password",
+                  "value" : "clientpassword" }
+            ]
+        }
+    ],
     "roles" : {
         "realm" : [
             {
@@ -97,18 +97,18 @@
         "application" : {
             "Application" : [
                 {
-                    "name": "admin"
+                    "name": "app-admin"
                 },
                 {
-                    "name": "user"
+                    "name": "app-user"
                 }
             ],
             "OtherApp" : [
                 {
-                    "name": "admin"
+                    "name": "otherapp-admin"
                 },
                 {
-                    "name": "user"
+                    "name": "otherapp-user"
                 }
             ]
         }
@@ -119,25 +119,31 @@
             "roles": ["admin"]
         }
     ],
+    "scopeMappings": [
+        {
+            "username": "oauthclient",
+            "roles": ["admin"]
+        }
+    ],
     "applicationRoleMappings": {
         "Application": [
             {
                 "username": "wburke",
-                "roles": ["user"]
+                "roles": ["app-user"]
             },
             {
                 "username": "admin",
-                "roles": ["admin"]
+                "roles": ["app-admin"]
             }
         ],
         "OtherApp": [
             {
                 "username": "wburke",
-                "roles": ["user"]
+                "roles": ["otherapp-user"]
             },
             {
                 "username": "admin",
-                "roles": ["admin"]
+                "roles": ["otherapp-admin"]
             }
         ]
     },
@@ -145,7 +151,7 @@
         "Application": [
             {
                 "username": "oauthclient",
-                "roles": ["user"]
+                "roles": ["app-user"]
             }
         ]
 
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 115dc5d..9489da1 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -251,10 +251,22 @@
             <groupId>org.seleniumhq.selenium</groupId>
             <artifactId>selenium-chrome-driver</artifactId>
         </dependency>
+
+        <!-- Mongo dependencies specified here and not in mongo profile, just to allow running tests from IDE -->
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-mongo</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.mongodb</groupId>
             <artifactId>mongo-java-driver</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-common</artifactId>
+        </dependency>
+
     </dependencies>
     <build>
         <plugins>
@@ -326,5 +338,23 @@
                 </plugins>
             </build>
         </profile>
+
+        <profile>
+            <id>mongo</id>
+            <activation>
+                <property>
+                    <name>keycloak.model</name>
+                    <value>mongo</value>
+                </property>
+            </activation>
+
+            <properties>
+                <keycloak.mongo.host>localhost</keycloak.mongo.host>
+                <keycloak.mongo.port>27017</keycloak.mongo.port>
+                <keycloak.mongo.db>keycloak</keycloak.mongo.db>
+                <keycloak.mongo.clearCollectionsOnStartup>true</keycloak.mongo.clearCollectionsOnStartup>
+            </properties>
+
+        </profile>
     </profiles>
 </project>
\ No newline at end of file
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
index 136a400..571eda2 100755
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
@@ -273,7 +273,7 @@ public class KeycloakServer {
 
         server.deploy(di);
 
-        factory = KeycloakApplication.createSessionFactory();
+        factory = ((KeycloakApplication)deployment.getApplication()).getFactory();
 
         setupDefaultRealm();