keycloak-uncached

Changes

model/jpa/pom.xml 139(+139 -0)

server/pom.xml 22(+1 -21)

Details

diff --git a/export-import/export-import-impl/pom.xml b/export-import/export-import-impl/pom.xml
index 683cfe9..9b10c8a 100755
--- a/export-import/export-import-impl/pom.xml
+++ b/export-import/export-import-impl/pom.xml
@@ -103,6 +103,19 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-jpa</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-jpa</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-model-mongo</artifactId>
             <version>${project.version}</version>
             <scope>test</scope>
diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java
index c1491d9..8cc6a17 100644
--- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java
+++ b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java
@@ -1,6 +1,5 @@
 package org.keycloak.exportimport;
 
-import org.junit.Ignore;
 import org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider;
 import org.keycloak.models.KeycloakSessionFactory;
 
@@ -8,10 +7,7 @@ import org.keycloak.models.KeycloakSessionFactory;
  * Test for full export of data from JPA and import them to Mongo. Using "directory" provider
  *
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
- *
- * TODO Update to work with hybrid model provider
  */
-@Ignore
 public class JPAToMongoExportImportTest extends ExportImportTestBase {
 
     @Override
diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java
index 0de953e..5fe08f7 100644
--- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java
+++ b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java
@@ -1,7 +1,6 @@
 package org.keycloak.exportimport;
 
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider;
 import org.keycloak.models.KeycloakSessionFactory;
 
@@ -11,10 +10,7 @@ import java.io.File;
  * Test for full export of data from Mongo and import them to JPA. Using export into encrypted ZIP and import from it
  *
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
- *
- * TODO Update to work with hybrid model provider
  */
-@Ignore
 public class MongoToJPAExportImportTest extends ExportImportTestBase {
 
     private static final String zipFile = "keycloak-export.zip";

model/jpa/pom.xml 139(+139 -0)

diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml
new file mode 100755
index 0000000..987abd2
--- /dev/null
+++ b/model/jpa/pom.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-beta-4-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-model-jpa</artifactId>
+    <name>Keycloak Model JPA</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk16</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.iharder</groupId>
+            <artifactId>base64</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-invalidation-cache-model</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate.javax.persistence</groupId>
+            <artifactId>hibernate-jpa-2.0-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-entitymanager</artifactId>
+            <version>${hibernate.entitymanager.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-simple</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-tests</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-tests</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+
+            <!-- Test jar used in export-import -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>package-tests-jar</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>default-test</id>
+                        <configuration>
+                            <dependenciesToScan>
+                                <dependency>org.keycloak:keycloak-model-tests</dependency>
+                            </dependenciesToScan>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+    </build>
+
+</project>
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
new file mode 100755
index 0000000..0ebe017
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
@@ -0,0 +1,268 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.jpa.entities.*;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ApplicationAdapter extends ClientAdapter implements ApplicationModel {
+
+    protected EntityManager em;
+    protected ApplicationEntity applicationEntity;
+
+    public ApplicationAdapter(RealmModel realm, EntityManager em, ApplicationEntity applicationEntity) {
+        super(realm, applicationEntity, em);
+        this.realm = realm;
+        this.em = em;
+        this.applicationEntity = applicationEntity;
+    }
+
+    @Override
+    public void updateApplication() {
+        em.flush();
+    }
+
+    @Override
+    public String getName() {
+        return entity.getName();
+    }
+
+    @Override
+    public void setName(String name) {
+        entity.setName(name);
+    }
+
+    @Override
+    public boolean isSurrogateAuthRequired() {
+        return applicationEntity.isSurrogateAuthRequired();
+    }
+
+    @Override
+    public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
+        applicationEntity.setSurrogateAuthRequired(surrogateAuthRequired);
+    }
+
+    @Override
+    public String getManagementUrl() {
+        return applicationEntity.getManagementUrl();
+    }
+
+    @Override
+    public void setManagementUrl(String url) {
+        applicationEntity.setManagementUrl(url);
+    }
+
+    @Override
+    public String getBaseUrl() {
+        return applicationEntity.getBaseUrl();
+    }
+
+    @Override
+    public void setBaseUrl(String url) {
+        applicationEntity.setBaseUrl(url);
+    }
+
+    @Override
+    public boolean isBearerOnly() {
+        return applicationEntity.isBearerOnly();
+    }
+
+    @Override
+    public void setBearerOnly(boolean only) {
+        applicationEntity.setBearerOnly(only);
+    }
+
+    @Override
+    public boolean isDirectGrantsOnly() {
+        return false;  // applications can't be grant only
+    }
+
+    @Override
+    public void setDirectGrantsOnly(boolean flag) {
+        // applications can't be grant only
+    }
+
+    @Override
+    public RoleModel getRole(String name) {
+        TypedQuery<RoleEntity> query = em.createNamedQuery("getAppRoleByName", RoleEntity.class);
+        query.setParameter("name", name);
+        query.setParameter("application", entity);
+        List<RoleEntity> roles = query.getResultList();
+        if (roles.size() == 0) return null;
+        return new RoleAdapter(realm, em, roles.get(0));
+    }
+
+    @Override
+    public RoleModel addRole(String name) {
+        return this.addRole(KeycloakModelUtils.generateId(), name);
+    }
+
+    @Override
+    public RoleModel addRole(String id, String name) {
+        RoleEntity roleEntity = new RoleEntity();
+        roleEntity.setId(id);
+        roleEntity.setName(name);
+        roleEntity.setApplication(applicationEntity);
+        roleEntity.setApplicationRole(true);
+        roleEntity.setRealmId(realm.getId());
+        em.persist(roleEntity);
+        applicationEntity.getRoles().add(roleEntity);
+        em.flush();
+        return new RoleAdapter(realm, em, roleEntity);
+    }
+
+    @Override
+    public boolean removeRole(RoleModel roleModel) {
+        if (roleModel == null) {
+            return false;
+        }
+        if (!roleModel.getContainer().equals(this)) return false;
+
+        RoleEntity role = RoleAdapter.toRoleEntity(roleModel, em);
+        if (!role.isApplicationRole()) return false;
+
+
+        applicationEntity.getRoles().remove(role);
+        applicationEntity.getDefaultRoles().remove(role);
+        em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", role).executeUpdate();
+        em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", role).executeUpdate();
+        em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", role).executeUpdate();
+        role.setApplication(null);
+        em.flush();
+        em.remove(role);
+        em.flush();
+
+        return true;
+    }
+
+    @Override
+    public Set<RoleModel> getRoles() {
+        Set<RoleModel> list = new HashSet<RoleModel>();
+        Collection<RoleEntity> roles = applicationEntity.getRoles();
+        if (roles == null) return list;
+        for (RoleEntity entity : roles) {
+            list.add(new RoleAdapter(realm, em, entity));
+        }
+        return list;
+    }
+
+    @Override
+    public Set<RoleModel> getApplicationScopeMappings(ClientModel client) {
+        Set<RoleModel> roleMappings = client.getScopeMappings();
+
+        Set<RoleModel> appRoles = new HashSet<RoleModel>();
+        for (RoleModel role : roleMappings) {
+            RoleContainerModel container = role.getContainer();
+            if (container instanceof RealmModel) {
+            } else {
+                ApplicationModel app = (ApplicationModel)container;
+                if (app.getId().equals(getId())) {
+                    appRoles.add(role);
+                }
+            }
+        }
+
+        return appRoles;
+    }
+
+
+
+
+    @Override
+    public List<String> getDefaultRoles() {
+        Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
+        List<String> roles = new ArrayList<String>();
+        if (entities == null) return roles;
+        for (RoleEntity entity : entities) {
+            roles.add(entity.getName());
+        }
+        return roles;
+    }
+
+    @Override
+    public void addDefaultRole(String name) {
+        RoleModel role = getRole(name);
+        if (role == null) {
+            role = addRole(name);
+        }
+        Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
+        for (RoleEntity entity : entities) {
+            if (entity.getId().equals(role.getId())) {
+                return;
+            }
+        }
+        RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+        entities.add(roleEntity);
+        em.flush();
+    }
+
+    public static boolean contains(String str, String[] array) {
+        for (String s : array) {
+            if (str.equals(s)) return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void updateDefaultRoles(String[] defaultRoles) {
+        Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
+        Set<String> already = new HashSet<String>();
+        List<RoleEntity> remove = new ArrayList<RoleEntity>();
+        for (RoleEntity rel : entities) {
+            if (!contains(rel.getName(), defaultRoles)) {
+                remove.add(rel);
+            } else {
+                already.add(rel.getName());
+            }
+        }
+        for (RoleEntity entity : remove) {
+            entities.remove(entity);
+        }
+        em.flush();
+        for (String roleName : defaultRoles) {
+            if (!already.contains(roleName)) {
+                addDefaultRole(roleName);
+            }
+        }
+        em.flush();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof ApplicationModel)) return false;
+
+        ApplicationModel that = (ApplicationModel) o;
+        return that.getId().equals(getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+
+    public String toString() {
+        return getName();
+    }
+
+    ApplicationEntity getJpaEntity() {
+        return applicationEntity;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
new file mode 100755
index 0000000..2e99658
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -0,0 +1,268 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.jpa.entities.ClientEntity;
+import org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.jpa.entities.ScopeMappingEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class ClientAdapter implements ClientModel {
+    protected ClientEntity entity;
+    protected RealmModel realm;
+    protected EntityManager em;
+
+    public ClientAdapter(RealmModel realm, ClientEntity entity, EntityManager em) {
+        this.realm = realm;
+        this.entity = entity;
+        this.em = em;
+    }
+
+    public ClientEntity getEntity() {
+        return entity;
+    }
+
+    @Override
+    public String getId() {
+        return entity.getId();
+    }
+
+    @Override
+    public RealmModel getRealm() {
+        return realm;
+    }
+
+    @Override
+    public String getClientId() {
+        return entity.getName();
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return entity.isEnabled();
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        entity.setEnabled(enabled);
+    }
+
+    @Override
+    public long getAllowedClaimsMask() {
+        return entity.getAllowedClaimsMask();
+    }
+
+    @Override
+    public void setAllowedClaimsMask(long mask) {
+        entity.setAllowedClaimsMask(mask);
+    }
+
+    @Override
+    public boolean isPublicClient() {
+        return entity.isPublicClient();
+    }
+
+    @Override
+    public void setPublicClient(boolean flag) {
+        entity.setPublicClient(flag);
+    }
+
+    @Override
+    public Set<String> getWebOrigins() {
+        Set<String> result = new HashSet<String>();
+        result.addAll(entity.getWebOrigins());
+        return result;
+    }
+
+
+
+    @Override
+    public void setWebOrigins(Set<String> webOrigins) {
+        entity.setWebOrigins(webOrigins);
+    }
+
+    @Override
+    public void addWebOrigin(String webOrigin) {
+        entity.getWebOrigins().add(webOrigin);
+    }
+
+    @Override
+    public void removeWebOrigin(String webOrigin) {
+        entity.getWebOrigins().remove(webOrigin);
+    }
+
+    @Override
+    public Set<String> getRedirectUris() {
+        Set<String> result = new HashSet<String>();
+        result.addAll(entity.getRedirectUris());
+        return result;
+    }
+
+    @Override
+    public void setRedirectUris(Set<String> redirectUris) {
+        entity.setRedirectUris(redirectUris);
+    }
+
+    @Override
+    public void addRedirectUri(String redirectUri) {
+        entity.getRedirectUris().add(redirectUri);
+    }
+
+    @Override
+    public void removeRedirectUri(String redirectUri) {
+        entity.getRedirectUris().remove(redirectUri);
+    }
+
+    @Override
+    public String getSecret() {
+        return entity.getSecret();
+    }
+
+    @Override
+    public void setSecret(String secret) {
+        entity.setSecret(secret);
+    }
+
+    @Override
+    public boolean validateSecret(String secret) {
+        return secret.equals(entity.getSecret());
+    }
+
+    @Override
+    public int getNotBefore() {
+        return entity.getNotBefore();
+    }
+
+    @Override
+    public void setNotBefore(int notBefore) {
+        entity.setNotBefore(notBefore);
+    }
+
+    @Override
+    public int getActiveUserSessions() {
+        Query query = em.createNamedQuery("getActiveClientSessions");
+        query.setParameter("clientId", getId());
+        Object count = query.getSingleResult();
+        return ((Number)count).intValue();
+    }
+
+    @Override
+    public Set<UserSessionModel> getUserSessions() {
+        Set<UserSessionModel> list = new HashSet<UserSessionModel>();
+        TypedQuery<ClientUserSessionAssociationEntity> query = em.createNamedQuery("getClientUserSessionByClient", ClientUserSessionAssociationEntity.class);
+        String id = getId();
+        query.setParameter("clientId", id);
+        List<ClientUserSessionAssociationEntity> results = query.getResultList();
+        for (ClientUserSessionAssociationEntity entity : results) {
+            list.add(new UserSessionAdapter(em, realm, entity.getSession()));
+        }
+        return list;
+    }
+
+    public void deleteUserSessionAssociation() {
+        em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", getId()).executeUpdate();
+    }
+
+    @Override
+    public Set<RoleModel> getRealmScopeMappings() {
+        Set<RoleModel> roleMappings = getScopeMappings();
+
+        Set<RoleModel> appRoles = new HashSet<RoleModel>();
+        for (RoleModel role : roleMappings) {
+            RoleContainerModel container = role.getContainer();
+            if (container instanceof RealmModel) {
+                if (((RealmModel) container).getId().equals(realm.getId())) {
+                    appRoles.add(role);
+                }
+            }
+        }
+
+        return appRoles;
+    }
+
+
+
+    @Override
+    public Set<RoleModel> getScopeMappings() {
+        TypedQuery<String> query = em.createNamedQuery("clientScopeMappingIds", String.class);
+        query.setParameter("client", getEntity());
+        List<String> ids = query.getResultList();
+        Set<RoleModel> roles = new HashSet<RoleModel>();
+        for (String roleId : ids) {
+            RoleModel role = realm.getRoleById(roleId);
+            if (role == null) continue;
+            roles.add(role);
+        }
+        return roles;
+    }
+
+    @Override
+    public void addScopeMapping(RoleModel role) {
+        if (hasScope(role)) return;
+        ScopeMappingEntity entity = new ScopeMappingEntity();
+        entity.setClient(getEntity());
+        RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+        entity.setRole(roleEntity);
+        em.persist(entity);
+        em.flush();
+        em.detach(entity);
+    }
+
+    @Override
+    public void deleteScopeMapping(RoleModel role) {
+        TypedQuery<ScopeMappingEntity> query = getRealmScopeMappingQuery(role);
+        List<ScopeMappingEntity> results = query.getResultList();
+        if (results.size() == 0) return;
+        for (ScopeMappingEntity entity : results) {
+            em.remove(entity);
+        }
+    }
+
+    protected TypedQuery<ScopeMappingEntity> getRealmScopeMappingQuery(RoleModel role) {
+        TypedQuery<ScopeMappingEntity> query = em.createNamedQuery("hasScope", ScopeMappingEntity.class);
+        query.setParameter("client", getEntity());
+        RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+        query.setParameter("role", roleEntity);
+        return query;
+    }
+
+    @Override
+    public boolean hasScope(RoleModel role) {
+        Set<RoleModel> roles = getScopeMappings();
+        if (roles.contains(role)) return true;
+
+        for (RoleModel mapping : roles) {
+            if (mapping.hasRole(role)) return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!this.getClass().equals(o.getClass())) return false;
+
+        ClientAdapter that = (ClientAdapter) o;
+        return that.getId().equals(getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return entity.getId().hashCode();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AbstractRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AbstractRoleMappingEntity.java
new file mode 100755
index 0000000..2ab0d00
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AbstractRoleMappingEntity.java
@@ -0,0 +1,52 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.MappedSuperclass;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@MappedSuperclass
+public class AbstractRoleMappingEntity {
+    @Id
+    @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+    @GeneratedValue(generator = "keycloak_generator")
+    protected String id;
+    @ManyToOne(fetch= FetchType.LAZY)
+    protected UserEntity user;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="roleId")
+    protected RoleEntity role;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public UserEntity getUser() {
+        return user;
+    }
+
+    public void setUser(UserEntity user) {
+        this.user = user;
+    }
+
+    public RoleEntity getRole() {
+        return role;
+    }
+
+    public void setRole(RoleEntity role) {
+        this.role = role;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
new file mode 100755
index 0000000..8327bd0
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
@@ -0,0 +1,90 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+public class ApplicationEntity extends ClientEntity {
+
+    private boolean surrogateAuthRequired;
+    private String baseUrl;
+    private String managementUrl;
+    private boolean bearerOnly;
+
+    @OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "application")
+    Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
+
+    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+    @JoinTable(name="ApplicationDefaultRoles")
+    Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
+
+    public boolean isSurrogateAuthRequired() {
+        return surrogateAuthRequired;
+    }
+
+    public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
+        this.surrogateAuthRequired = surrogateAuthRequired;
+    }
+
+    public String getBaseUrl() {
+        return baseUrl;
+    }
+
+    public void setBaseUrl(String baseUrl) {
+        this.baseUrl = baseUrl;
+    }
+
+    public String getManagementUrl() {
+        return managementUrl;
+    }
+
+    public void setManagementUrl(String managementUrl) {
+        this.managementUrl = managementUrl;
+    }
+
+    public Collection<RoleEntity> getRoles() {
+        return roles;
+    }
+
+    public void setRoles(Collection<RoleEntity> roles) {
+        this.roles = roles;
+    }
+
+    public Collection<RoleEntity> getDefaultRoles() {
+        return defaultRoles;
+    }
+
+    public void setDefaultRoles(Collection<RoleEntity> defaultRoles) {
+        this.defaultRoles = defaultRoles;
+    }
+
+    public boolean isBearerOnly() {
+        return bearerOnly;
+    }
+
+    public void setBearerOnly(boolean bearerOnly) {
+        this.bearerOnly = bearerOnly;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
new file mode 100644
index 0000000..e6ae8d4
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
@@ -0,0 +1,50 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToOne;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@Entity
+public class AuthenticationLinkEntity {
+
+    @Id
+    @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+    @GeneratedValue(generator = "keycloak_generator")
+    private String id;
+
+    protected String authProvider;
+    protected String authUserId;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAuthProvider() {
+        return authProvider;
+    }
+
+    public void setAuthProvider(String authProvider) {
+        this.authProvider = authProvider;
+    }
+
+    public String getAuthUserId() {
+        return authUserId;
+    }
+
+    public void setAuthUserId(String authUserId) {
+        this.authUserId = authUserId;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java
new file mode 100644
index 0000000..2ce4c12
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java
@@ -0,0 +1,80 @@
+package org.keycloak.models.jpa.entities;
+
+import java.util.Map;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.Table;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@Entity
+@Table(name="AuthProviderEntity")
+public class AuthenticationProviderEntity {
+
+    @Id
+    @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+    @GeneratedValue(generator = "keycloak_generator")
+    protected String id;
+
+    private String providerName;
+    private boolean passwordUpdateSupported;
+    private int priority;
+
+    @ElementCollection
+    @MapKeyColumn(name="name")
+    @Column(name="value")
+    @CollectionTable(name="AuthProviderEntity_cfg", joinColumns = {
+            @JoinColumn(name = "AuthProviderEntity_id")
+    })
+    private Map<String, String> config;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getProviderName() {
+        return providerName;
+    }
+
+    public void setProviderName(String providerName) {
+        this.providerName = providerName;
+    }
+
+    public boolean isPasswordUpdateSupported() {
+        return passwordUpdateSupported;
+    }
+
+    public void setPasswordUpdateSupported(boolean passwordUpdateSupported) {
+        this.passwordUpdateSupported = passwordUpdateSupported;
+    }
+
+    public int getPriority() {
+        return priority;
+    }
+
+    public void setPriority(int priority) {
+        this.priority = priority;
+    }
+
+    public Map<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(Map<String, String> config) {
+        this.config = config;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
new file mode 100755
index 0000000..9367d25
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -0,0 +1,131 @@
+package org.keycloak.models.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"realm", "name"})})
+public abstract class ClientEntity {
+    @Id
+    private String id;
+    @Column(name = "name")
+    private String name;
+    private boolean enabled;
+    private String secret;
+    private long allowedClaimsMask;
+    private int notBefore;
+    private boolean publicClient;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "realm")
+    protected RealmEntity realm;
+
+    @ElementCollection
+    @CollectionTable
+    protected Set<String> webOrigins = new HashSet<String>();
+    @ElementCollection
+    @CollectionTable
+    protected Set<String> redirectUris = new HashSet<String>();
+
+    public RealmEntity getRealm() {
+        return realm;
+    }
+
+    public void setRealm(RealmEntity realm) {
+        this.realm = realm;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public long getAllowedClaimsMask() {
+        return allowedClaimsMask;
+    }
+
+    public void setAllowedClaimsMask(long allowedClaimsMask) {
+        this.allowedClaimsMask = allowedClaimsMask;
+    }
+
+    public Set<String> getWebOrigins() {
+        return webOrigins;
+    }
+
+    public void setWebOrigins(Set<String> webOrigins) {
+        this.webOrigins = webOrigins;
+    }
+
+    public Set<String> getRedirectUris() {
+        return redirectUris;
+    }
+
+    public void setRedirectUris(Set<String> redirectUris) {
+        this.redirectUris = redirectUris;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    public void setSecret(String secret) {
+        this.secret = secret;
+    }
+
+    public int getNotBefore() {
+        return notBefore;
+    }
+
+    public void setNotBefore(int notBefore) {
+        this.notBefore = notBefore;
+    }
+
+    public boolean isPublicClient() {
+        return publicClient;
+    }
+
+    public void setPublicClient(boolean publicClient) {
+        this.publicClient = publicClient;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientUserSessionAssociationEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientUserSessionAssociationEntity.java
new file mode 100755
index 0000000..490e75d
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientUserSessionAssociationEntity.java
@@ -0,0 +1,84 @@
+package org.keycloak.models.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@Table(name = "ClientUserSessionAscEntity")
+@NamedQueries({
+        @NamedQuery(name = "getAllClientUserSessions", query = "select s from ClientUserSessionAssociationEntity s"),
+        @NamedQuery(name = "getClientUserSessionBySession", query = "select s from ClientUserSessionAssociationEntity s where s.session = :session"),
+        @NamedQuery(name = "getClientUserSessionByClient", query = "select s from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
+        @NamedQuery(name = "getActiveClientSessions", query = "select COUNT(s) from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
+        @NamedQuery(name = "removeClientUserSessionByClient", query = "delete from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
+        @NamedQuery(name = "removeClientUserSessionByUser", query = "delete from ClientUserSessionAssociationEntity s where s.userId = :userId"),
+        @NamedQuery(name = "removeClientUserSessionByRealm", query = "delete from ClientUserSessionAssociationEntity s where s.realmId = :realmId")})
+public class ClientUserSessionAssociationEntity {
+    @Id
+    @GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+    @GeneratedValue(generator = "uuid_generator")
+    private String id;
+
+    // we use ids to avoid select for update contention
+    private String userId;
+    private String realmId;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    private UserSessionEntity session;
+
+
+
+    private String clientId;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public UserSessionEntity getSession() {
+        return session;
+    }
+
+    public void setSession(UserSessionEntity session) {
+        this.session = session;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
new file mode 100755
index 0000000..6d792f8
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
@@ -0,0 +1,91 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name="credentialByUserAndType", query="select cred from CredentialEntity cred where cred.user = :user and cred.type = :type")
+})
+@Entity
+public class CredentialEntity {
+    @Id
+    @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+    @GeneratedValue(generator = "keycloak_generator")
+    protected String id;
+
+    protected String type;
+    protected String value;
+    protected String device;
+    protected byte[] salt;
+    protected int hashIterations;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    protected UserEntity user;
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getDevice() {
+        return device;
+    }
+
+    public void setDevice(String device) {
+        this.device = device;
+    }
+
+    public UserEntity getUser() {
+        return user;
+    }
+
+    public void setUser(UserEntity user) {
+        this.user = user;
+    }
+
+    public byte[] getSalt() {
+        return salt;
+    }
+
+    public void setSalt(byte[] salt) {
+        this.salt = salt;
+    }
+
+    public int getHashIterations() {
+        return hashIterations;
+    }
+
+    public void setHashIterations(int hashIterations) {
+        this.hashIterations = hashIterations;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
new file mode 100755
index 0000000..dbdf080
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
@@ -0,0 +1,27 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name="findOAuthClientByName", query="select o from OAuthClientEntity o where o.name=:name and o.realm = :realm"),
+        @NamedQuery(name="findOAuthClientByRealm", query="select o from OAuthClientEntity o where o.realm = :realm")
+
+})
+@Entity
+public class OAuthClientEntity extends ClientEntity {
+    protected boolean directGrantsOnly;
+
+    public boolean isDirectGrantsOnly() {
+        return directGrantsOnly;
+    }
+
+    public void setDirectGrantsOnly(boolean directGrantsOnly) {
+        this.directGrantsOnly = directGrantsOnly;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
new file mode 100755
index 0000000..8fd8a56
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -0,0 +1,479 @@
+package org.keycloak.models.jpa.entities;
+
+
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinTable;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@NamedQueries({
+        @NamedQuery(name="getAllRealms", query="select realm from RealmEntity realm"),
+        @NamedQuery(name="getRealmByName", query="select realm from RealmEntity realm where realm.name = :name"),
+})
+public class RealmEntity {
+    @Id
+    protected String id;
+
+    @Column(unique = true)
+    protected String name;
+
+    protected boolean enabled;
+    protected boolean sslNotRequired;
+    protected boolean registrationAllowed;
+    protected boolean passwordCredentialGrantAllowed;
+    protected boolean verifyEmail;
+    protected boolean resetPasswordAllowed;
+    protected boolean social;
+    protected boolean rememberMe;
+    //--- brute force settings
+    protected boolean bruteForceProtected;
+    protected int maxFailureWaitSeconds;
+    protected int minimumQuickLoginWaitSeconds;
+    protected int waitIncrementSeconds;
+    protected long quickLoginCheckMilliSeconds;
+    protected int maxDeltaTimeSeconds;
+    protected int failureFactor;
+    //--- end brute force settings
+
+
+    @Column(name="updateProfileOnInitSocLogin")
+    protected boolean updateProfileOnInitialSocialLogin;
+    protected String passwordPolicy;
+
+    private int ssoSessionIdleTimeout;
+    private int ssoSessionMaxLifespan;
+    protected int accessTokenLifespan;
+    protected int accessCodeLifespan;
+    protected int accessCodeLifespanUserAction;
+    protected int notBefore;
+
+    @Column(length = 2048)
+    protected String publicKeyPem;
+    @Column(length = 2048)
+    protected String privateKeyPem;
+
+    protected String loginTheme;
+    protected String accountTheme;
+    protected String adminTheme;
+    protected String emailTheme;
+
+    @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+    @JoinTable(name="User_RequiredCreds")
+    Collection<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
+
+
+    @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+    @JoinTable(name="AuthProviders")
+    List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
+
+    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+    Collection<ApplicationEntity> applications = new ArrayList<ApplicationEntity>();
+
+    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
+    Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
+
+    @ElementCollection
+    @MapKeyColumn(name="name")
+    @Column(name="value")
+    @CollectionTable
+    protected Map<String, String> smtpConfig = new HashMap<String, String>();
+
+    @ElementCollection
+    @MapKeyColumn(name="name")
+    @Column(name="value")
+    @CollectionTable
+    protected Map<String, String> socialConfig = new HashMap<String, String>();
+
+    @ElementCollection
+    @MapKeyColumn(name="name")
+    @Column(name="value")
+    @CollectionTable
+    protected Map<String, String> ldapServerConfig = new HashMap<String, String>();
+
+    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+    @JoinTable(name="RealmDefaultRoles")
+    protected Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
+
+    protected boolean auditEnabled;
+    protected long auditExpiration;
+
+    @ElementCollection
+    protected Set<String> auditListeners= new HashSet<String>();
+
+    @OneToOne
+    protected ApplicationEntity masterAdminApp;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public boolean isSslNotRequired() {
+        return sslNotRequired;
+    }
+
+    public void setSslNotRequired(boolean sslNotRequired) {
+        this.sslNotRequired = sslNotRequired;
+    }
+
+    public boolean isPasswordCredentialGrantAllowed() {
+        return passwordCredentialGrantAllowed;
+    }
+
+    public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+        this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed;
+    }
+
+    public boolean isRegistrationAllowed() {
+        return registrationAllowed;
+    }
+
+    public void setRegistrationAllowed(boolean registrationAllowed) {
+        this.registrationAllowed = registrationAllowed;
+    }
+
+    public boolean isRememberMe() {
+        return rememberMe;
+    }
+
+    public void setRememberMe(boolean rememberMe) {
+        this.rememberMe = rememberMe;
+    }
+
+    public boolean isVerifyEmail() {
+        return verifyEmail;
+    }
+
+    public void setVerifyEmail(boolean verifyEmail) {
+        this.verifyEmail = verifyEmail;
+    }
+
+    public boolean isResetPasswordAllowed() {
+        return resetPasswordAllowed;
+    }
+
+    public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
+        this.resetPasswordAllowed = resetPasswordAllowed;
+    }
+
+    public boolean isSocial() {
+        return social;
+    }
+
+    public void setSocial(boolean social) {
+        this.social = social;
+    }
+
+    public boolean isUpdateProfileOnInitialSocialLogin() {
+        return updateProfileOnInitialSocialLogin;
+    }
+
+    public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
+        this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
+    }
+
+    public int getSsoSessionIdleTimeout() {
+        return ssoSessionIdleTimeout;
+    }
+
+    public void setSsoSessionIdleTimeout(int ssoSessionIdleTimeout) {
+        this.ssoSessionIdleTimeout = ssoSessionIdleTimeout;
+    }
+
+    public int getSsoSessionMaxLifespan() {
+        return ssoSessionMaxLifespan;
+    }
+
+    public void setSsoSessionMaxLifespan(int ssoSessionMaxLifespan) {
+        this.ssoSessionMaxLifespan = ssoSessionMaxLifespan;
+    }
+
+    public int getAccessTokenLifespan() {
+        return accessTokenLifespan;
+    }
+
+    public void setAccessTokenLifespan(int accessTokenLifespan) {
+        this.accessTokenLifespan = accessTokenLifespan;
+    }
+
+    public int getAccessCodeLifespan() {
+        return accessCodeLifespan;
+    }
+
+    public void setAccessCodeLifespan(int accessCodeLifespan) {
+        this.accessCodeLifespan = accessCodeLifespan;
+    }
+
+    public int getAccessCodeLifespanUserAction() {
+        return accessCodeLifespanUserAction;
+    }
+
+    public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
+        this.accessCodeLifespanUserAction = accessCodeLifespanUserAction;
+    }
+
+    public String getPublicKeyPem() {
+        return publicKeyPem;
+    }
+
+    public void setPublicKeyPem(String publicKeyPem) {
+        this.publicKeyPem = publicKeyPem;
+    }
+
+    public String getPrivateKeyPem() {
+        return privateKeyPem;
+    }
+
+    public void setPrivateKeyPem(String privateKeyPem) {
+        this.privateKeyPem = privateKeyPem;
+    }
+
+    public Collection<RequiredCredentialEntity> getRequiredCredentials() {
+        return requiredCredentials;
+    }
+
+    public void setRequiredCredentials(Collection<RequiredCredentialEntity> requiredCredentials) {
+        this.requiredCredentials = requiredCredentials;
+    }
+
+    public List<AuthenticationProviderEntity> getAuthenticationProviders() {
+        return authenticationProviders;
+    }
+
+    public void setAuthenticationProviders(List<AuthenticationProviderEntity> authenticationProviders) {
+        this.authenticationProviders = authenticationProviders;
+    }
+
+    public Collection<ApplicationEntity> getApplications() {
+        return applications;
+    }
+
+    public void setApplications(Collection<ApplicationEntity> applications) {
+        this.applications = applications;
+    }
+
+    public Collection<RoleEntity> getRoles() {
+        return roles;
+    }
+
+    public void setRoles(Collection<RoleEntity> roles) {
+        this.roles = roles;
+    }
+
+    public void addRole(RoleEntity role) {
+        if (roles == null) {
+            roles = new ArrayList<RoleEntity>();
+        }
+        roles.add(role);
+    }
+
+    public Map<String, String> getSmtpConfig() {
+        return smtpConfig;
+    }
+
+    public void setSmtpConfig(Map<String, String> smtpConfig) {
+        this.smtpConfig = smtpConfig;
+    }
+
+    public Map<String, String> getSocialConfig() {
+        return socialConfig;
+    }
+
+    public void setSocialConfig(Map<String, String> socialConfig) {
+        this.socialConfig = socialConfig;
+    }
+
+    public Map<String, String> getLdapServerConfig() {
+        return ldapServerConfig;
+    }
+
+    public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
+        this.ldapServerConfig = ldapServerConfig;
+    }
+
+    public Collection<RoleEntity> getDefaultRoles() {
+        return defaultRoles;
+    }
+
+    public void setDefaultRoles(Collection<RoleEntity> defaultRoles) {
+        this.defaultRoles = defaultRoles;
+    }
+
+    public String getPasswordPolicy() {
+        return passwordPolicy;
+    }
+
+    public void setPasswordPolicy(String passwordPolicy) {
+        this.passwordPolicy = passwordPolicy;
+    }
+
+    public String getLoginTheme() {
+        return loginTheme;
+    }
+
+    public void setLoginTheme(String theme) {
+        this.loginTheme = theme;
+    }
+
+    public String getAccountTheme() {
+        return accountTheme;
+    }
+
+    public void setAccountTheme(String theme) {
+        this.accountTheme = theme;
+    }
+
+    public String getAdminTheme() {
+        return adminTheme;
+    }
+
+    public void setAdminTheme(String adminTheme) {
+        this.adminTheme = adminTheme;
+    }
+
+    public String getEmailTheme() {
+        return emailTheme;
+    }
+
+    public void setEmailTheme(String emailTheme) {
+        this.emailTheme = emailTheme;
+    }
+
+    public int getNotBefore() {
+        return notBefore;
+    }
+
+    public void setNotBefore(int notBefore) {
+        this.notBefore = notBefore;
+    }
+
+    public boolean isBruteForceProtected() {
+        return bruteForceProtected;
+    }
+
+    public void setBruteForceProtected(boolean bruteForceProtected) {
+        this.bruteForceProtected = bruteForceProtected;
+    }
+
+    public int getMaxFailureWaitSeconds() {
+        return maxFailureWaitSeconds;
+    }
+
+    public void setMaxFailureWaitSeconds(int maxFailureWaitSeconds) {
+        this.maxFailureWaitSeconds = maxFailureWaitSeconds;
+    }
+
+    public int getMinimumQuickLoginWaitSeconds() {
+        return minimumQuickLoginWaitSeconds;
+    }
+
+    public void setMinimumQuickLoginWaitSeconds(int minimumQuickLoginWaitSeconds) {
+        this.minimumQuickLoginWaitSeconds = minimumQuickLoginWaitSeconds;
+    }
+
+    public int getWaitIncrementSeconds() {
+        return waitIncrementSeconds;
+    }
+
+    public void setWaitIncrementSeconds(int waitIncrementSeconds) {
+        this.waitIncrementSeconds = waitIncrementSeconds;
+    }
+
+    public long getQuickLoginCheckMilliSeconds() {
+        return quickLoginCheckMilliSeconds;
+    }
+
+    public void setQuickLoginCheckMilliSeconds(long quickLoginCheckMilliSeconds) {
+        this.quickLoginCheckMilliSeconds = quickLoginCheckMilliSeconds;
+    }
+
+    public int getMaxDeltaTimeSeconds() {
+        return maxDeltaTimeSeconds;
+    }
+
+    public void setMaxDeltaTimeSeconds(int maxDeltaTimeSeconds) {
+        this.maxDeltaTimeSeconds = maxDeltaTimeSeconds;
+    }
+
+    public int getFailureFactor() {
+        return failureFactor;
+    }
+
+    public void setFailureFactor(int failureFactor) {
+        this.failureFactor = failureFactor;
+    }
+
+    public boolean isAuditEnabled() {
+        return auditEnabled;
+    }
+
+    public void setAuditEnabled(boolean auditEnabled) {
+        this.auditEnabled = auditEnabled;
+    }
+
+    public long getAuditExpiration() {
+        return auditExpiration;
+    }
+
+    public void setAuditExpiration(long auditExpiration) {
+        this.auditExpiration = auditExpiration;
+    }
+
+    public Set<String> getAuditListeners() {
+        return auditListeners;
+    }
+
+    public void setAuditListeners(Set<String> auditListeners) {
+        this.auditListeners = auditListeners;
+    }
+
+    public ApplicationEntity getMasterAdminApp() {
+        return masterAdminApp;
+    }
+
+    public void setMasterAdminApp(ApplicationEntity masterAdminApp) {
+        this.masterAdminApp = masterAdminApp;
+    }
+
+}
+
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java
new file mode 100755
index 0000000..9487022
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java
@@ -0,0 +1,64 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+public class RequiredCredentialEntity {
+    @Id
+    @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+    @GeneratedValue(generator = "keycloak_generator")
+    protected String id;
+
+    protected String type;
+    protected boolean input;
+    protected boolean secret;
+    protected String formLabel;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public boolean isInput() {
+        return input;
+    }
+
+    public void setInput(boolean input) {
+        this.input = input;
+    }
+
+    public boolean isSecret() {
+        return secret;
+    }
+
+    public void setSecret(boolean secret) {
+        this.secret = secret;
+    }
+
+    public String getFormLabel() {
+        return formLabel;
+    }
+
+    public void setFormLabel(String formLabel) {
+        this.formLabel = formLabel;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
new file mode 100755
index 0000000..d3fc6df
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
@@ -0,0 +1,157 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@Table(uniqueConstraints = {
+        @UniqueConstraint(columnNames = { "name", "appRealmConstraint" })
+})
+@NamedQueries({
+        @NamedQuery(name="getAppRoleByName", query="select role from RoleEntity role where role.name = :name and role.application = :application"),
+        @NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.applicationRole = false and role.name = :name and role.realm = :realm")
+})
+
+public class RoleEntity {
+    @Id
+    @Column(name="id")
+    private String id;
+
+    private String name;
+    private String description;
+
+    // hax! couldn't get constraint to work properly
+    private String realmId;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "realm")
+    private RealmEntity realm;
+
+    @Column(name="applicationRole")
+    private boolean applicationRole;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "application")
+    private ApplicationEntity application;
+
+    // Hack to ensure that either name+application or name+realm are unique. Needed due to MS-SQL as it don't allow multiple NULL values in the column, which is part of constraint
+    private String appRealmConstraint;
+
+    @ManyToMany(fetch = FetchType.LAZY, cascade = {})
+    @JoinTable(name = "CompositeRole", joinColumns = @JoinColumn(name = "composite"), inverseJoinColumns = @JoinColumn(name = "childRole"))
+    private Collection<RoleEntity> compositeRoles = new ArrayList<RoleEntity>();
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Collection<RoleEntity> getCompositeRoles() {
+        return compositeRoles;
+    }
+
+    public void setCompositeRoles(Collection<RoleEntity> compositeRoles) {
+        this.compositeRoles = compositeRoles;
+    }
+
+    public boolean isApplicationRole() {
+        return applicationRole;
+    }
+
+    public void setApplicationRole(boolean applicationRole) {
+        this.applicationRole = applicationRole;
+    }
+
+    public RealmEntity getRealm() {
+        return realm;
+    }
+
+    public void setRealm(RealmEntity realm) {
+        this.realm = realm;
+        this.appRealmConstraint = realm.getId();
+    }
+
+    public ApplicationEntity getApplication() {
+        return application;
+    }
+
+    public void setApplication(ApplicationEntity application) {
+        this.application = application;
+        if (application != null) {
+            this.appRealmConstraint = application.getId();
+        }
+    }
+
+    public String getAppRealmConstraint() {
+        return appRealmConstraint;
+    }
+
+    public void setAppRealmConstraint(String appRealmConstraint) {
+        this.appRealmConstraint = appRealmConstraint;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        RoleEntity that = (RoleEntity) o;
+
+        if (!id.equals(that.id)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java
new file mode 100755
index 0000000..b45b69b
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java
@@ -0,0 +1,60 @@
+package org.keycloak.models.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name="hasScope", query="select m from ScopeMappingEntity m where m.client = :client and m.role = :role"),
+        @NamedQuery(name="clientScopeMappings", query="select m from ScopeMappingEntity m where m.client = :client"),
+        @NamedQuery(name="clientScopeMappingIds", query="select m.role.id from ScopeMappingEntity m where m.client = :client")
+})
+@Entity
+public class ScopeMappingEntity {
+    @Id
+    @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+    @GeneratedValue(generator = "keycloak_generator")
+    protected String id;
+    @ManyToOne(fetch= FetchType.LAZY)
+    protected ClientEntity client;
+
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="roleId")
+    protected RoleEntity role;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public ClientEntity getClient() {
+        return client;
+    }
+
+    public void setClient(ClientEntity client) {
+        this.client = client;
+    }
+
+    public RoleEntity getRole() {
+        return role;
+    }
+
+    public void setRole(RoleEntity role) {
+        this.role = role;
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java
new file mode 100755
index 0000000..a1de3af
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java
@@ -0,0 +1,86 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name="findSocialLinkByUser", query="select link from SocialLinkEntity link where link.user = :user"),
+        @NamedQuery(name="findSocialLinkByUserAndProvider", query="select link from SocialLinkEntity link where link.user = :user and link.socialProvider = :socialProvider"),
+        @NamedQuery(name="findUserByLinkAndRealm", query="select link.user from SocialLinkEntity link where link.realm = :realm and link.socialProvider = :socialProvider and link.socialUserId = :socialUserId")
+})
+@Entity
+public class SocialLinkEntity {
+    @Id
+    @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+    @GeneratedValue(generator = "keycloak_generator")
+    private String id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    private UserEntity user;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    protected RealmEntity realm;
+
+    protected String socialProvider;
+    protected String socialUserId;
+    protected String socialUsername;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public UserEntity getUser() {
+        return user;
+    }
+
+    public void setUser(UserEntity user) {
+        this.user = user;
+    }
+
+    public String getSocialProvider() {
+        return socialProvider;
+    }
+
+    public void setSocialProvider(String socialProvider) {
+        this.socialProvider = socialProvider;
+    }
+
+    public String getSocialUserId() {
+        return socialUserId;
+    }
+
+    public void setSocialUserId(String socialUserId) {
+        this.socialUserId = socialUserId;
+    }
+
+    public String getSocialUsername() {
+        return socialUsername;
+    }
+
+    public void setSocialUsername(String socialUsername) {
+        this.socialUsername = socialUsername;
+    }
+
+    public RealmEntity getRealm() {
+        return realm;
+    }
+
+    public void setRealm(RealmEntity realm) {
+        this.realm = realm;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
new file mode 100755
index 0000000..75389c9
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
@@ -0,0 +1,196 @@
+package org.keycloak.models.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name="getRealmUserById", query="select u from UserEntity u where u.id = :id and u.realm = :realm"),
+        @NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realm = :realm"),
+        @NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realm = :realm"),
+        @NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realm = :realm"),
+        @NamedQuery(name="getRealmUserByFirstLastName", query="select u from UserEntity u where u.firstName = :first and u.lastName = :last and u.realm = :realm")
+})
+@Entity
+@Table(uniqueConstraints = {
+        @UniqueConstraint(columnNames = { "realm", "username" }),
+        @UniqueConstraint(columnNames = { "realm", "emailConstraint" })
+})
+public class UserEntity {
+    @Id
+    protected String id;
+
+    protected String username;
+    protected String firstName;
+    protected String lastName;
+    protected String email;
+    protected boolean enabled;
+    protected boolean totp;
+    protected boolean emailVerified;
+
+    // Hack just to workaround the fact that on MS-SQL you can't have unique constraint with multiple NULL values TODO: Find better solution (like unique index with 'where' but that's proprietary)
+    protected String emailConstraint = KeycloakModelUtils.generateId();
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "realm")
+    protected RealmEntity realm;
+
+    @ElementCollection
+    @MapKeyColumn(name="name")
+    @Column(name="value")
+    @CollectionTable
+    protected Map<String, String> attributes = new HashMap<String, String>();
+
+    @ElementCollection
+    @CollectionTable
+    protected Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>();
+
+    @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
+    protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
+
+    @OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
+    protected AuthenticationLinkEntity authenticationLink;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+        this.emailConstraint = email != null ? email : KeycloakModelUtils.generateId();
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public String getEmailConstraint() {
+        return emailConstraint;
+    }
+
+    public void setEmailConstraint(String emailConstraint) {
+        this.emailConstraint = emailConstraint;
+    }
+
+    public boolean isTotp() {
+        return totp;
+    }
+
+    public void setTotp(boolean totp) {
+        this.totp = totp;
+    }
+
+    public boolean isEmailVerified() {
+        return emailVerified;
+    }
+
+    public void setEmailVerified(boolean emailVerified) {
+        this.emailVerified = emailVerified;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
+
+    public Set<UserModel.RequiredAction> getRequiredActions() {
+        return requiredActions;
+    }
+
+    public void setRequiredActions(Set<UserModel.RequiredAction> requiredActions) {
+        this.requiredActions = requiredActions;
+    }
+
+    public RealmEntity getRealm() {
+        return realm;
+    }
+
+    public void setRealm(RealmEntity realm) {
+        this.realm = realm;
+    }
+
+    public Collection<CredentialEntity> getCredentials() {
+        return credentials;
+    }
+
+    public void setCredentials(Collection<CredentialEntity> credentials) {
+        this.credentials = credentials;
+    }
+
+    public AuthenticationLinkEntity getAuthenticationLink() {
+        return authenticationLink;
+    }
+
+    public void setAuthenticationLink(AuthenticationLinkEntity authenticationLink) {
+        this.authenticationLink = authenticationLink;
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UsernameLoginFailureEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UsernameLoginFailureEntity.java
new file mode 100755
index 0000000..f3cffb4
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UsernameLoginFailureEntity.java
@@ -0,0 +1,88 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@NamedQueries({
+        @NamedQuery(name="getAllFailures", query="select failure from UsernameLoginFailureEntity failure"),
+})
+public class UsernameLoginFailureEntity {
+    // we manually set the id to be username-realmid
+    // we may have a concurrent creation of the same login failure entry that we want to avoid
+    @Id
+    protected String id;
+    protected String username;
+    protected int failedLoginNotBefore;
+    protected int numFailures;
+    protected long lastFailure;
+    protected String lastIPFailure;
+
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    protected RealmEntity realm;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public int getFailedLoginNotBefore() {
+        return failedLoginNotBefore;
+    }
+
+    public void setFailedLoginNotBefore(int failedLoginNotBefore) {
+        this.failedLoginNotBefore = failedLoginNotBefore;
+    }
+
+    public int getNumFailures() {
+        return numFailures;
+    }
+
+    public void setNumFailures(int numFailures) {
+        this.numFailures = numFailures;
+    }
+
+    public long getLastFailure() {
+        return lastFailure;
+    }
+
+    public void setLastFailure(long lastFailure) {
+        this.lastFailure = lastFailure;
+    }
+
+    public String getLastIPFailure() {
+        return lastIPFailure;
+    }
+
+    public void setLastIPFailure(String lastIPFailure) {
+        this.lastIPFailure = lastIPFailure;
+    }
+
+    public RealmEntity getRealm() {
+        return realm;
+    }
+
+    public void setRealm(RealmEntity realm) {
+        this.realm = realm;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java
new file mode 100755
index 0000000..647acea
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java
@@ -0,0 +1,19 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name="userHasRole", query="select m from UserRoleMappingEntity m where m.user = :user and m.role = :role"),
+        @NamedQuery(name="userRoleMappings", query="select m from UserRoleMappingEntity m where m.user = :user"),
+        @NamedQuery(name="userRoleMappingIds", query="select m.role.id from UserRoleMappingEntity m where m.user = :user")
+})
+@Entity
+public class UserRoleMappingEntity extends AbstractRoleMappingEntity {
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java
new file mode 100755
index 0000000..4be2853
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java
@@ -0,0 +1,107 @@
+package org.keycloak.models.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.keycloak.models.UserModel;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+@Entity
+@NamedQueries({
+        @NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.userId = :userId"),
+        @NamedQuery(name = "removeRealmUserSessions", query = "delete from UserSessionEntity s where s.realmId = :realmId"),
+        @NamedQuery(name = "removeUserSessionByUser", query = "delete from UserSessionEntity s where s.userId = :userId"),
+        @NamedQuery(name = "getUserSessionExpired", query = "select s from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime"),
+        @NamedQuery(name = "removeUserSessionExpired", query = "delete from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime")
+})
+public class UserSessionEntity {
+
+    @Id
+    @GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+    @GeneratedValue(generator = "uuid_generator")
+    private String id;
+
+    // we use ids to avoid select for update contention
+    private String userId;
+    private String realmId;
+
+    private String ipAddress;
+
+    private int started;
+
+    private int lastSessionRefresh;
+
+    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy="session")
+    private Collection<ClientUserSessionAssociationEntity> clients = new ArrayList<ClientUserSessionAssociationEntity>();
+
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+
+    public String getIpAddress() {
+        return ipAddress;
+    }
+
+    public void setIpAddress(String ipAddress) {
+        this.ipAddress = ipAddress;
+    }
+
+    public int getStarted() {
+        return started;
+    }
+
+    public void setStarted(int started) {
+        this.started = started;
+    }
+
+    public int getLastSessionRefresh() {
+        return lastSessionRefresh;
+    }
+
+    public void setLastSessionRefresh(int lastSessionRefresh) {
+        this.lastSessionRefresh = lastSessionRefresh;
+    }
+
+    public Collection<ClientUserSessionAssociationEntity> getClients() {
+        return clients;
+    }
+
+    public void setClients(Collection<ClientUserSessionAssociationEntity> clients) {
+        this.clients = clients;
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakTransaction.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakTransaction.java
new file mode 100755
index 0000000..095b341
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakTransaction.java
@@ -0,0 +1,53 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.KeycloakTransaction;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaKeycloakTransaction implements KeycloakTransaction {
+
+    protected EntityManager em;
+
+    public JpaKeycloakTransaction(EntityManager em) {
+        this.em = em;
+    }
+
+    @Override
+    public void begin() {
+        em.getTransaction().begin();
+    }
+
+    @Override
+    public void commit() {
+        try {
+            em.getTransaction().commit();
+        } catch (PersistenceException e) {
+            throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
+        }
+    }
+
+    @Override
+    public void rollback() {
+        em.getTransaction().rollback();
+    }
+
+    @Override
+    public void setRollbackOnly() {
+        em.getTransaction().setRollbackOnly();
+    }
+
+    @Override
+    public boolean getRollbackOnly() {
+        return  em.getTransaction().getRollbackOnly();
+    }
+
+    @Override
+    public boolean isActive() {
+        return em.getTransaction().isActive();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java
new file mode 100755
index 0000000..940e067
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java
@@ -0,0 +1,400 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.*;
+import org.keycloak.models.jpa.entities.*;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.util.Time;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaModelProvider implements ModelProvider {
+    private final KeycloakSession session;
+    protected EntityManager em;
+
+    public JpaModelProvider(KeycloakSession session, EntityManager em) {
+        this.session = session;
+        this.em = em;
+        this.em = PersistenceExceptionConverter.create(em);
+    }
+
+    @Override
+    public KeycloakTransaction getTransaction() {
+        return new JpaKeycloakTransaction(em);
+    }
+
+    @Override
+    public RealmModel createRealm(String name) {
+        return createRealm(KeycloakModelUtils.generateId(), name);
+    }
+
+    @Override
+    public RealmModel createRealm(String id, String name) {
+        RealmEntity realm = new RealmEntity();
+        realm.setName(name);
+        realm.setId(id);
+        em.persist(realm);
+        em.flush();
+        return new RealmAdapter(session, em, realm);
+    }
+
+    @Override
+    public RealmModel getRealm(String id) {
+        RealmEntity realm = em.find(RealmEntity.class, id);
+        if (realm == null) return null;
+        return new RealmAdapter(session, em, realm);
+    }
+
+    @Override
+    public List<RealmModel> getRealms() {
+        TypedQuery<RealmEntity> query = em.createNamedQuery("getAllRealms", RealmEntity.class);
+        List<RealmEntity> entities = query.getResultList();
+        List<RealmModel> realms = new ArrayList<RealmModel>();
+        for (RealmEntity entity : entities) {
+            realms.add(new RealmAdapter(session, em, entity));
+        }
+        return realms;
+    }
+
+    @Override
+    public RealmModel getRealmByName(String name) {
+        TypedQuery<RealmEntity> query = em.createNamedQuery("getRealmByName", RealmEntity.class);
+        query.setParameter("name", name);
+        List<RealmEntity> entities = query.getResultList();
+        if (entities.size() == 0) return null;
+        if (entities.size() > 1) throw new IllegalStateException("Should not be more than one realm with same name");
+        RealmEntity realm = query.getResultList().get(0);
+        if (realm == null) return null;
+        return new RealmAdapter(session, em, realm);
+    }
+
+    @Override
+    public UserModel getUserById(String id, RealmModel realmModel) {
+        TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserById", UserEntity.class);
+        query.setParameter("id", id);
+        RealmEntity realm = em.getReference(RealmEntity.class, realmModel.getId());
+        query.setParameter("realm", realm);
+        List<UserEntity> entities = query.getResultList();
+        if (entities.size() == 0) return null;
+        return new UserAdapter(realmModel, em, entities.get(0));
+    }
+
+    @Override
+    public UserModel getUserByUsername(String username, RealmModel realmModel) {
+        TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByUsername", UserEntity.class);
+        query.setParameter("username", username);
+        RealmEntity realm = em.getReference(RealmEntity.class, realmModel.getId());
+        query.setParameter("realm", realm);
+        List<UserEntity> results = query.getResultList();
+        if (results.size() == 0) return null;
+        return new UserAdapter(realmModel, em, results.get(0));
+    }
+
+    @Override
+    public UserModel getUserByEmail(String email, RealmModel realmModel) {
+        TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByEmail", UserEntity.class);
+        query.setParameter("email", email);
+        RealmEntity realm = em.getReference(RealmEntity.class, realmModel.getId());
+        query.setParameter("realm", realm);
+        List<UserEntity> results = query.getResultList();
+        return results.isEmpty() ? null : new UserAdapter(realmModel, em, results.get(0));
+    }
+
+    @Override
+    public boolean removeRealm(String id) {
+        RealmEntity realm = em.find(RealmEntity.class, id);
+        if (realm == null) {
+            return false;
+        }
+
+        RealmAdapter adapter = new RealmAdapter(session, em, realm);
+        adapter.removeUserSessions();
+        for (ApplicationEntity a : new LinkedList<ApplicationEntity>(realm.getApplications())) {
+            adapter.removeApplication(a.getId());
+        }
+
+        for (OAuthClientModel oauth : adapter.getOAuthClients()) {
+            adapter.removeOAuthClient(oauth.getId());
+        }
+
+        for (UserEntity u : em.createQuery("from UserEntity u where u.realm = :realm", UserEntity.class).setParameter("realm", realm).getResultList()) {
+            adapter.removeUser(u.getUsername());
+        }
+
+        em.remove(realm);
+
+        return true;
+    }
+
+    @Override
+    public void close() {
+        if (em.getTransaction().isActive()) em.getTransaction().rollback();
+        if (em.isOpen()) em.close();
+    }
+
+    @Override
+    public void removeAllData() {
+        // Should be sufficient to delete all realms. Rest data should be removed in cascade
+        List<RealmModel> realms = getRealms();
+        for (RealmModel realm : realms) {
+            removeRealm(realm.getId());
+        }
+    }
+
+    @Override
+    public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
+        TypedQuery<UserEntity> query = em.createNamedQuery("findUserByLinkAndRealm", UserEntity.class);
+        RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+        query.setParameter("realm", realmEntity);
+        query.setParameter("socialProvider", socialLink.getSocialProvider());
+        query.setParameter("socialUserId", socialLink.getSocialUserId());
+        List<UserEntity> results = query.getResultList();
+        if (results.isEmpty()) {
+            return null;
+        } else if (results.size() > 1) {
+            throw new IllegalStateException("More results found for socialProvider=" + socialLink.getSocialProvider() +
+                    ", socialUserId=" + socialLink.getSocialUserId() + ", results=" + results);
+        } else {
+            UserEntity user = results.get(0);
+            return new UserAdapter(realm, em, user);
+        }
+    }
+
+    @Override
+    public List<UserModel> getUsers(RealmModel realm) {
+        TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm", UserEntity.class);
+        RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+        query.setParameter("realm", realmEntity);
+        List<UserEntity> results = query.getResultList();
+        List<UserModel> users = new ArrayList<UserModel>();
+        for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+        return users;
+    }
+
+    @Override
+    public List<UserModel> searchForUser(String search, RealmModel realm) {
+        TypedQuery<UserEntity> query = em.createQuery("select u from UserEntity u where u.realm = :realm and ( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search )", UserEntity.class);
+        RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+        query.setParameter("realm", realmEntity);
+        query.setParameter("search", "%" + search.toLowerCase() + "%");
+        List<UserEntity> results = query.getResultList();
+        List<UserModel> users = new ArrayList<UserModel>();
+        for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+        return users;
+    }
+
+    @Override
+    public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
+        StringBuilder builder = new StringBuilder("select u from UserEntity u");
+        boolean first = true;
+        for (Map.Entry<String, String> entry : attributes.entrySet()) {
+            String attribute = null;
+            if (entry.getKey().equals(UserModel.LOGIN_NAME)) {
+                attribute = "lower(username)";
+            } else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
+                attribute = "lower(firstName)";
+            } else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
+                attribute = "lower(lastName)";
+            } else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
+                attribute = "lower(email)";
+            }
+            if (attribute == null) continue;
+            if (first) {
+                first = false;
+                builder.append(" where realm = :realm");
+            } else {
+                builder.append(" and ");
+            }
+            builder.append(attribute).append(" like '%").append(entry.getValue().toLowerCase()).append("%'");
+        }
+        String q = builder.toString();
+        TypedQuery<UserEntity> query = em.createQuery(q, UserEntity.class);
+        RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+        query.setParameter("realm", realmEntity);
+        List<UserEntity> results = query.getResultList();
+        List<UserModel> users = new ArrayList<UserModel>();
+        for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+        return users;
+    }
+
+    private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
+        TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUserAndProvider", SocialLinkEntity.class);
+        UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+        query.setParameter("user", userEntity);
+        query.setParameter("socialProvider", socialProvider);
+        List<SocialLinkEntity> results = query.getResultList();
+        return results.size() > 0 ? results.get(0) : null;
+    }
+
+
+    @Override
+    public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
+        TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUser", SocialLinkEntity.class);
+        UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+        query.setParameter("user", userEntity);
+        List<SocialLinkEntity> results = query.getResultList();
+        Set<SocialLinkModel> set = new HashSet<SocialLinkModel>();
+        for (SocialLinkEntity entity : results) {
+            set.add(new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()));
+        }
+        return set;
+    }
+
+    @Override
+    public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
+        SocialLinkEntity entity = findSocialLink(user, socialProvider);
+        return (entity != null) ? new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()) : null;
+    }
+
+    @Override
+    public RoleModel getRoleById(String id, RealmModel realm) {
+        RoleEntity entity = em.find(RoleEntity.class, id);
+        if (entity == null) return null;
+        if (!realm.getId().equals(entity.getRealmId())) return null;
+        return new RoleAdapter(realm, em, entity);
+    }
+
+    @Override
+    public ApplicationModel getApplicationById(String id, RealmModel realm) {
+        ApplicationEntity app = em.find(ApplicationEntity.class, id);
+
+        // Check if application belongs to this realm
+        if (app == null || !realm.getId().equals(app.getRealm().getId())) return null;
+        return new ApplicationAdapter(realm, em, app);
+    }
+
+    @Override
+    public OAuthClientModel getOAuthClientById(String id, RealmModel realm) {
+        OAuthClientEntity client = em.find(OAuthClientEntity.class, id);
+
+        // Check if client belongs to this realm
+        if (client == null || !realm.getId().equals(client.getRealm().getId())) return null;
+        return new OAuthClientAdapter(realm, client, em);
+    }
+
+    @Override
+    public UsernameLoginFailureModel getUserLoginFailure(String username, RealmModel realm) {
+        String id = username + "-" + realm.getId();
+        UsernameLoginFailureEntity entity = em.find(UsernameLoginFailureEntity.class, id);
+        if (entity == null) return null;
+        return new UsernameLoginFailureAdapter(entity);
+    }
+
+    @Override
+    public UsernameLoginFailureModel addUserLoginFailure(String username, RealmModel realm) {
+        UsernameLoginFailureModel model = getUserLoginFailure(username, realm);
+        if (model != null) return model;
+        String id = username + "-" + realm.getId();
+        UsernameLoginFailureEntity entity = new UsernameLoginFailureEntity();
+        entity.setId(id);
+        entity.setUsername(username);
+        RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+        entity.setRealm(realmEntity);
+        em.persist(entity);
+        return new UsernameLoginFailureAdapter(entity);
+    }
+
+    @Override
+    public List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
+        TypedQuery<UsernameLoginFailureEntity> query = em.createNamedQuery("getAllFailures", UsernameLoginFailureEntity.class);
+        List<UsernameLoginFailureEntity> entities = query.getResultList();
+        List<UsernameLoginFailureModel> models = new ArrayList<UsernameLoginFailureModel>();
+        for (UsernameLoginFailureEntity entity : entities) {
+            models.add(new UsernameLoginFailureAdapter(entity));
+        }
+        return models;
+    }
+
+    @Override
+    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String ipAddress) {
+        UserSessionEntity entity = new UserSessionEntity();
+        entity.setRealmId(realm.getId());
+        entity.setUserId(user.getId());
+        entity.setIpAddress(ipAddress);
+
+        int currentTime = Time.currentTime();
+
+        entity.setStarted(currentTime);
+        entity.setLastSessionRefresh(currentTime);
+
+        em.persist(entity);
+        return new UserSessionAdapter(em, realm, entity);
+    }
+
+    @Override
+    public UserSessionModel getUserSession(String id, RealmModel realm) {
+        UserSessionEntity entity = em.find(UserSessionEntity.class, id);
+        return entity != null ? new UserSessionAdapter(em, realm, entity) : null;
+    }
+
+    @Override
+    public List<UserSessionModel> getUserSessions(UserModel user, RealmModel realm) {
+        List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+        for (UserSessionEntity e : em.createNamedQuery("getUserSessionByUser", UserSessionEntity.class)
+                .setParameter("userId", user.getId()).getResultList()) {
+            sessions.add(new UserSessionAdapter(em, realm, e));
+        }
+        return sessions;
+    }
+
+    @Override
+    public Set<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
+        Set<UserSessionModel> list = new HashSet<UserSessionModel>();
+        TypedQuery<ClientUserSessionAssociationEntity> query = em.createNamedQuery("getClientUserSessionByClient", ClientUserSessionAssociationEntity.class);
+        String id = client.getId();
+        query.setParameter("clientId", id);
+        List<ClientUserSessionAssociationEntity> results = query.getResultList();
+        for (ClientUserSessionAssociationEntity entity : results) {
+            list.add(new UserSessionAdapter(em, realm, entity.getSession()));
+        }
+        return list;
+    }
+
+    @Override
+    public int getActiveUserSessions(RealmModel realm, ClientModel client) {
+        Query query = em.createNamedQuery("getActiveClientSessions");
+        query.setParameter("clientId", client.getId());
+        Object count = query.getSingleResult();
+        return ((Number)count).intValue();
+    }
+
+    @Override
+    public void removeUserSession(UserSessionModel session) {
+        em.remove(((UserSessionAdapter) session).getEntity());
+    }
+
+    @Override
+    public void removeUserSessions(RealmModel realm, UserModel user) {
+        em.createNamedQuery("removeClientUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
+        em.createNamedQuery("removeUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
+    }
+
+    @Override
+    public void removeExpiredUserSessions(RealmModel realm) {
+        TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionExpired", UserSessionEntity.class)
+                .setParameter("maxTime", Time.currentTime() - realm.getSsoSessionMaxLifespan())
+                .setParameter("idleTime", Time.currentTime() - realm.getSsoSessionIdleTimeout());
+        List<UserSessionEntity> results = query.getResultList();
+        for (UserSessionEntity entity : results) {
+            em.remove(entity);
+        }
+    }
+
+    @Override
+    public void removeUserSessions(RealmModel realm) {
+        em.createNamedQuery("removeClientUserSessionByRealm").setParameter("realmId", realm.getId()).executeUpdate();
+        em.createNamedQuery("removeRealmUserSessions").setParameter("realmId", realm.getId()).executeUpdate();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProviderFactory.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProviderFactory.java
new file mode 100755
index 0000000..ad4e6dc
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProviderFactory.java
@@ -0,0 +1,40 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelProvider;
+import org.keycloak.models.ModelProviderFactory;
+import org.keycloak.util.JpaUtils;
+
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JpaModelProviderFactory implements ModelProviderFactory {
+
+    protected EntityManagerFactory emf;
+
+    @Override
+    public void init(Config.Scope config) {
+        emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store", JpaUtils.getHibernateProperties());
+    }
+
+    @Override
+    public String getId() {
+        return "jpa";
+    }
+
+    @Override
+    public ModelProvider create(KeycloakSession session) {
+        return new JpaModelProvider(session, emf.createEntityManager());
+    }
+
+    @Override
+    public void close() {
+        emf.close();
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
new file mode 100755
index 0000000..c890c9f
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
@@ -0,0 +1,56 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.jpa.entities.OAuthClientEntity;
+
+import javax.persistence.EntityManager;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OAuthClientAdapter extends ClientAdapter implements OAuthClientModel {
+
+    protected final OAuthClientEntity oAuthClientEntity;
+
+    public OAuthClientAdapter(RealmModel realm, OAuthClientEntity entity, EntityManager em) {
+        super(realm, entity, em);
+        oAuthClientEntity = entity;
+    }
+
+    @Override
+    public void setClientId(String id) {
+        entity.setName(id);
+
+    }
+
+    @Override
+    public boolean isDirectGrantsOnly() {
+        return oAuthClientEntity.isDirectGrantsOnly();
+    }
+
+    @Override
+    public void setDirectGrantsOnly(boolean flag) {
+        oAuthClientEntity.setDirectGrantsOnly(flag);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof OAuthClientModel)) return false;
+
+        OAuthClientModel that = (OAuthClientModel) o;
+        return that.getId().equals(getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java
new file mode 100644
index 0000000..dfa5053
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java
@@ -0,0 +1,49 @@
+package org.keycloak.models.jpa;
+
+import org.hibernate.exception.ConstraintViolationException;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.ModelDuplicateException;
+
+import javax.persistence.EntityExistsException;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class PersistenceExceptionConverter implements InvocationHandler {
+
+    private EntityManager em;
+
+    public static EntityManager create(EntityManager em) {
+        return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
+    }
+
+    private PersistenceExceptionConverter(EntityManager em) {
+        this.em = em;
+    }
+
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        try {
+            return method.invoke(em, args);
+        } catch (InvocationTargetException e) {
+            throw convert(e.getCause());
+        }
+    }
+
+    public static ModelException convert(Throwable t) {
+        if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
+            throw new ModelDuplicateException(t);
+        } if (t instanceof EntityExistsException) {
+            throw new ModelDuplicateException(t);
+        } else {
+            throw new ModelException(t);
+        }
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
new file mode 100755
index 0000000..23602ec
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -0,0 +1,1142 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.AuthenticationProviderModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.CredentialValidation;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.jpa.entities.ApplicationEntity;
+import org.keycloak.models.jpa.entities.AuthenticationLinkEntity;
+import org.keycloak.models.jpa.entities.AuthenticationProviderEntity;
+import org.keycloak.models.jpa.entities.OAuthClientEntity;
+import org.keycloak.models.jpa.entities.RealmEntity;
+import org.keycloak.models.jpa.entities.RequiredCredentialEntity;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.jpa.entities.ScopeMappingEntity;
+import org.keycloak.models.jpa.entities.SocialLinkEntity;
+import org.keycloak.models.jpa.entities.UserEntity;
+import org.keycloak.models.jpa.entities.UserSessionEntity;
+import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
+import org.keycloak.models.jpa.entities.UsernameLoginFailureEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.TimeBasedOTP;
+import org.keycloak.util.Time;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.*;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RealmAdapter implements RealmModel {
+    protected RealmEntity realm;
+    protected EntityManager em;
+    protected volatile transient PublicKey publicKey;
+    protected volatile transient PrivateKey privateKey;
+    protected KeycloakSession session;
+    private PasswordPolicy passwordPolicy;
+
+    public RealmAdapter(KeycloakSession session, EntityManager em, RealmEntity realm) {
+        this.session = session;
+        this.em = em;
+        this.realm = realm;
+    }
+
+    public RealmEntity getEntity() {
+        return realm;
+    }
+
+    @Override
+    public String getId() {
+        return realm.getId();
+    }
+
+    @Override
+    public String getName() {
+        return realm.getName();
+    }
+
+    @Override
+    public void setName(String name) {
+        realm.setName(name);
+        em.flush();
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return realm.isEnabled();
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        realm.setEnabled(enabled);
+        em.flush();
+    }
+
+    @Override
+    public boolean isSslNotRequired() {
+        return realm.isSslNotRequired();
+    }
+
+    @Override
+    public void setSslNotRequired(boolean sslNotRequired) {
+        realm.setSslNotRequired(sslNotRequired);
+        em.flush();
+    }
+
+    @Override
+    public boolean isPasswordCredentialGrantAllowed() {
+        return realm.isPasswordCredentialGrantAllowed();
+    }
+
+    @Override
+    public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+        realm.setPasswordCredentialGrantAllowed(passwordCredentialGrantAllowed);
+        em.flush();
+    }
+
+    @Override
+    public boolean isRegistrationAllowed() {
+        return realm.isRegistrationAllowed();
+    }
+
+    @Override
+    public void setRegistrationAllowed(boolean registrationAllowed) {
+        realm.setRegistrationAllowed(registrationAllowed);
+        em.flush();
+    }
+
+    @Override
+    public boolean isRememberMe() {
+        return realm.isRememberMe();
+    }
+
+    @Override
+    public void setRememberMe(boolean rememberMe) {
+        realm.setRememberMe(rememberMe);
+        em.flush();
+    }
+
+    @Override
+    public boolean isBruteForceProtected() {
+        return realm.isBruteForceProtected();
+    }
+
+    @Override
+    public void setBruteForceProtected(boolean value) {
+        realm.setBruteForceProtected(value);
+    }
+
+    @Override
+    public int getMaxFailureWaitSeconds() {
+        return realm.getMaxFailureWaitSeconds();
+    }
+
+    @Override
+    public void setMaxFailureWaitSeconds(int val) {
+        realm.setMaxFailureWaitSeconds(val);
+    }
+
+    @Override
+    public int getWaitIncrementSeconds() {
+        return realm.getWaitIncrementSeconds();
+    }
+
+    @Override
+    public void setWaitIncrementSeconds(int val) {
+        realm.setWaitIncrementSeconds(val);
+    }
+
+    @Override
+    public long getQuickLoginCheckMilliSeconds() {
+        return realm.getQuickLoginCheckMilliSeconds();
+    }
+
+    @Override
+    public void setQuickLoginCheckMilliSeconds(long val) {
+        realm.setQuickLoginCheckMilliSeconds(val);
+    }
+
+    @Override
+    public int getMinimumQuickLoginWaitSeconds() {
+        return realm.getMinimumQuickLoginWaitSeconds();
+    }
+
+    @Override
+    public void setMinimumQuickLoginWaitSeconds(int val) {
+        realm.setMinimumQuickLoginWaitSeconds(val);
+    }
+
+    @Override
+    public int getMaxDeltaTimeSeconds() {
+        return realm.getMaxDeltaTimeSeconds();
+    }
+
+    @Override
+    public void setMaxDeltaTimeSeconds(int val) {
+        realm.setMaxDeltaTimeSeconds(val);
+    }
+
+    @Override
+    public int getFailureFactor() {
+        return realm.getFailureFactor();
+    }
+
+    @Override
+    public void setFailureFactor(int failureFactor) {
+        realm.setFailureFactor(failureFactor);
+    }
+
+    @Override
+    public boolean isVerifyEmail() {
+        return realm.isVerifyEmail();
+    }
+
+    @Override
+    public void setVerifyEmail(boolean verifyEmail) {
+        realm.setVerifyEmail(verifyEmail);
+        em.flush();
+    }
+
+    @Override
+    public boolean isResetPasswordAllowed() {
+        return realm.isResetPasswordAllowed();
+    }
+
+    @Override
+    public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
+        realm.setResetPasswordAllowed(resetPasswordAllowed);
+        em.flush();
+    }
+
+    @Override
+    public int getNotBefore() {
+        return realm.getNotBefore();
+    }
+
+    @Override
+    public void setNotBefore(int notBefore) {
+        realm.setNotBefore(notBefore);
+    }
+
+    @Override
+    public int getAccessTokenLifespan() {
+        return realm.getAccessTokenLifespan();
+    }
+
+    @Override
+    public void setAccessTokenLifespan(int tokenLifespan) {
+        realm.setAccessTokenLifespan(tokenLifespan);
+        em.flush();
+    }
+
+    @Override
+    public int getSsoSessionIdleTimeout() {
+        return realm.getSsoSessionIdleTimeout();
+    }
+
+    @Override
+    public void setSsoSessionIdleTimeout(int seconds) {
+        realm.setSsoSessionIdleTimeout(seconds);
+    }
+
+    @Override
+    public int getSsoSessionMaxLifespan() {
+        return realm.getSsoSessionMaxLifespan();
+    }
+
+    @Override
+    public void setSsoSessionMaxLifespan(int seconds) {
+        realm.setSsoSessionMaxLifespan(seconds);
+    }
+
+    @Override
+    public int getAccessCodeLifespan() {
+        return realm.getAccessCodeLifespan();
+    }
+
+    @Override
+    public void setAccessCodeLifespan(int accessCodeLifespan) {
+        realm.setAccessCodeLifespan(accessCodeLifespan);
+        em.flush();
+    }
+
+    @Override
+    public int getAccessCodeLifespanUserAction() {
+        return realm.getAccessCodeLifespanUserAction();
+    }
+
+    @Override
+    public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
+        realm.setAccessCodeLifespanUserAction(accessCodeLifespanUserAction);
+        em.flush();
+    }
+
+    @Override
+    public String getPublicKeyPem() {
+        return realm.getPublicKeyPem();
+    }
+
+    @Override
+    public void setPublicKeyPem(String publicKeyPem) {
+        realm.setPublicKeyPem(publicKeyPem);
+        em.flush();
+    }
+
+    @Override
+    public String getPrivateKeyPem() {
+        return realm.getPrivateKeyPem();
+    }
+
+    @Override
+    public void setPrivateKeyPem(String privateKeyPem) {
+        realm.setPrivateKeyPem(privateKeyPem);
+        em.flush();
+    }
+
+    @Override
+    public PublicKey getPublicKey() {
+        if (publicKey != null) return publicKey;
+        publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
+        return publicKey;
+    }
+
+    @Override
+    public void setPublicKey(PublicKey publicKey) {
+        this.publicKey = publicKey;
+        String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
+        setPublicKeyPem(publicKeyPem);
+    }
+
+    @Override
+    public PrivateKey getPrivateKey() {
+        if (privateKey != null) return privateKey;
+        privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
+        return privateKey;
+    }
+
+    @Override
+    public void setPrivateKey(PrivateKey privateKey) {
+        this.privateKey = privateKey;
+        String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
+        setPrivateKeyPem(privateKeyPem);
+    }
+
+    protected RequiredCredentialModel initRequiredCredentialModel(String type) {
+        RequiredCredentialModel model = RequiredCredentialModel.BUILT_IN.get(type);
+        if (model == null) {
+            throw new RuntimeException("Unknown credential type " + type);
+        }
+        return model;
+    }
+
+    @Override
+    public void addRequiredCredential(String type) {
+        RequiredCredentialModel model = initRequiredCredentialModel(type);
+        addRequiredCredential(model);
+        em.flush();
+    }
+
+    public void addRequiredCredential(RequiredCredentialModel model) {
+        RequiredCredentialEntity entity = new RequiredCredentialEntity();
+        entity.setInput(model.isInput());
+        entity.setSecret(model.isSecret());
+        entity.setType(model.getType());
+        entity.setFormLabel(model.getFormLabel());
+        em.persist(entity);
+        realm.getRequiredCredentials().add(entity);
+        em.flush();
+    }
+
+    @Override
+    public void updateRequiredCredentials(Set<String> creds) {
+        Collection<RequiredCredentialEntity> relationships = realm.getRequiredCredentials();
+        if (relationships == null) relationships = new ArrayList<RequiredCredentialEntity>();
+
+        Set<String> already = new HashSet<String>();
+        List<RequiredCredentialEntity> remove = new ArrayList<RequiredCredentialEntity>();
+        for (RequiredCredentialEntity rel : relationships) {
+            if (!creds.contains(rel.getType())) {
+                remove.add(rel);
+            } else {
+                already.add(rel.getType());
+            }
+        }
+        for (RequiredCredentialEntity entity : remove) {
+            relationships.remove(entity);
+            em.remove(entity);
+        }
+        for (String cred : creds) {
+            if (!already.contains(cred)) {
+                addRequiredCredential(cred);
+            }
+        }
+        em.flush();
+    }
+
+
+    @Override
+    public List<RequiredCredentialModel> getRequiredCredentials() {
+        List<RequiredCredentialModel> requiredCredentialModels = new ArrayList<RequiredCredentialModel>();
+        Collection<RequiredCredentialEntity> entities = realm.getRequiredCredentials();
+        if (entities == null) return requiredCredentialModels;
+        for (RequiredCredentialEntity entity : entities) {
+            RequiredCredentialModel model = new RequiredCredentialModel();
+            model.setFormLabel(entity.getFormLabel());
+            model.setType(entity.getType());
+            model.setSecret(entity.isSecret());
+            model.setInput(entity.isInput());
+            requiredCredentialModels.add(model);
+        }
+        return requiredCredentialModels;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+
+    @Override
+    public UserModel getUser(String name) {
+        return session.getUserByUsername(name, this);
+    }
+
+    @Override
+    public UsernameLoginFailureModel getUserLoginFailure(String username) {
+        return session.getUserLoginFailure(username, this);
+    }
+
+    @Override
+    public UsernameLoginFailureModel addUserLoginFailure(String username) {
+        return session.addUserLoginFailure(username, this);
+    }
+
+    @Override
+    public List<UsernameLoginFailureModel> getAllUserLoginFailures() {
+        return session.getAllUserLoginFailures(this);
+    }
+
+    @Override
+    public UserModel getUserByEmail(String email) {
+        return session.getUserByEmail(email, this);
+    }
+
+    @Override
+    public UserModel getUserById(String id) {
+        return session.getUserById(id, this);
+    }
+
+    @Override
+    public UserModel addUser(String username) {
+        return this.addUser(KeycloakModelUtils.generateId(), username, true);
+    }
+
+    @Override
+    public UserModel addUser(String id, String username, boolean addDefaultRoles) {
+        if (id == null) {
+            id = KeycloakModelUtils.generateId();
+        }
+
+        UserEntity entity = new UserEntity();
+        entity.setId(id);
+        entity.setUsername(username);
+        entity.setRealm(realm);
+        em.persist(entity);
+        em.flush();
+        UserModel userModel = new UserAdapter(this, em, entity);
+
+        if (addDefaultRoles) {
+            for (String r : getDefaultRoles()) {
+                userModel.grantRole(getRole(r));
+            }
+
+            for (ApplicationModel application : getApplications()) {
+                for (String r : application.getDefaultRoles()) {
+                    userModel.grantRole(application.getRole(r));
+                }
+            }
+        }
+
+        return userModel;
+    }
+
+    @Override
+    public boolean removeUser(String name) {
+        TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByUsername", UserEntity.class);
+        query.setParameter("username", name);
+        query.setParameter("realm", realm);
+        List<UserEntity> results = query.getResultList();
+        if (results.size() == 0) return false;
+        removeUser(results.get(0));
+        return true;
+    }
+
+    private void removeUser(UserEntity user) {
+        em.createNamedQuery("removeClientUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
+        em.createNamedQuery("removeUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
+
+        em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
+        em.createQuery("delete from " + SocialLinkEntity.class.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
+        if (user.getAuthenticationLink() != null) {
+            em.remove(user.getAuthenticationLink());
+        }
+        em.remove(user);
+    }
+
+    @Override
+    public List<String> getDefaultRoles() {
+        Collection<RoleEntity> entities = realm.getDefaultRoles();
+        List<String> roles = new ArrayList<String>();
+        if (entities == null) return roles;
+        for (RoleEntity entity : entities) {
+            roles.add(entity.getName());
+        }
+        return roles;
+    }
+
+    @Override
+    public void addDefaultRole(String name) {
+        RoleModel role = getRole(name);
+        if (role == null) {
+            role = addRole(name);
+        }
+        Collection<RoleEntity> entities = realm.getDefaultRoles();
+        for (RoleEntity entity : entities) {
+            if (entity.getId().equals(role.getId())) {
+                return;
+            }
+        }
+        RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+        entities.add(roleEntity);
+        em.flush();
+    }
+
+    public static boolean contains(String str, String[] array) {
+        for (String s : array) {
+            if (str.equals(s)) return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void updateDefaultRoles(String[] defaultRoles) {
+        Collection<RoleEntity> entities = realm.getDefaultRoles();
+        Set<String> already = new HashSet<String>();
+        List<RoleEntity> remove = new ArrayList<RoleEntity>();
+        for (RoleEntity rel : entities) {
+            if (!contains(rel.getName(), defaultRoles)) {
+                remove.add(rel);
+            } else {
+                already.add(rel.getName());
+            }
+        }
+        for (RoleEntity entity : remove) {
+            entities.remove(entity);
+        }
+        em.flush();
+        for (String roleName : defaultRoles) {
+            if (!already.contains(roleName)) {
+                addDefaultRole(roleName);
+            }
+        }
+        em.flush();
+    }
+
+    @Override
+    public ClientModel findClient(String clientId) {
+        ClientModel model = getApplicationByName(clientId);
+        if (model != null) return model;
+        return getOAuthClient(clientId);
+    }
+
+    @Override
+    public ClientModel findClientById(String id) {
+        ClientModel model = getApplicationById(id);
+        if (model != null) return model;
+        return getOAuthClientById(id);
+    }
+
+    @Override
+    public Map<String, ApplicationModel> getApplicationNameMap() {
+        Map<String, ApplicationModel> map = new HashMap<String, ApplicationModel>();
+        for (ApplicationModel app : getApplications()) {
+            map.put(app.getName(), app);
+        }
+        return map;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public List<ApplicationModel> getApplications() {
+        List<ApplicationModel> list = new ArrayList<ApplicationModel>();
+        if (realm.getApplications() == null) return list;
+        for (ApplicationEntity entity : realm.getApplications()) {
+            list.add(new ApplicationAdapter(this, em, entity));
+        }
+        return list;
+    }
+
+    @Override
+    public ApplicationModel addApplication(String name) {
+        return this.addApplication(KeycloakModelUtils.generateId(), name);
+    }
+
+    @Override
+    public ApplicationModel addApplication(String id, String name) {
+        ApplicationEntity applicationData = new ApplicationEntity();
+        applicationData.setId(id);
+        applicationData.setName(name);
+        applicationData.setEnabled(true);
+        applicationData.setRealm(realm);
+        realm.getApplications().add(applicationData);
+        em.persist(applicationData);
+        em.flush();
+        ApplicationModel resource = new ApplicationAdapter(this, em, applicationData);
+        em.flush();
+        return resource;
+    }
+
+    @Override
+    public boolean removeApplication(String id) {
+        if (id == null) return false;
+        ApplicationModel application = getApplicationById(id);
+        if (application == null) return false;
+
+        em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", application.getId()).executeUpdate();
+
+        for (RoleModel role : application.getRoles()) {
+            application.removeRole(role);
+        }
+
+        ApplicationEntity applicationEntity = null;
+        Iterator<ApplicationEntity> it = realm.getApplications().iterator();
+        while (it.hasNext()) {
+            ApplicationEntity ae = it.next();
+            if (ae.getId().equals(id)) {
+                applicationEntity = ae;
+                it.remove();
+                break;
+            }
+        }
+        for (ApplicationEntity a : realm.getApplications()) {
+            if (a.getId().equals(id)) {
+                applicationEntity = a;
+            }
+        }
+        if (application == null) {
+            return false;
+        }
+        em.remove(applicationEntity);
+        em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", applicationEntity).executeUpdate();
+
+        return true;
+    }
+
+    @Override
+    public ApplicationModel getApplicationById(String id) {
+        return session.getApplicationById(id, this);
+    }
+
+    @Override
+    public ApplicationModel getApplicationByName(String name) {
+        return getApplicationNameMap().get(name);
+    }
+
+    @Override
+    public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
+        return session.getUserBySocialLink(socialLink, this);
+    }
+
+    @Override
+    public Set<SocialLinkModel> getSocialLinks(UserModel user) {
+        return session.getSocialLinks(user, this);
+    }
+
+    @Override
+    public SocialLinkModel getSocialLink(UserModel user, String socialProvider) {
+        return session.getSocialLink(user, socialProvider, this);
+    }
+
+    @Override
+    public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
+        SocialLinkEntity entity = new SocialLinkEntity();
+        entity.setRealm(realm);
+        entity.setSocialProvider(socialLink.getSocialProvider());
+        entity.setSocialUserId(socialLink.getSocialUserId());
+        entity.setSocialUsername(socialLink.getSocialUsername());
+        UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+        entity.setUser(userEntity);
+        em.persist(entity);
+        em.flush();
+    }
+
+    private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
+        TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUserAndProvider", SocialLinkEntity.class);
+        UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+        query.setParameter("user", userEntity);
+        query.setParameter("socialProvider", socialProvider);
+        List<SocialLinkEntity> results = query.getResultList();
+        return results.size() > 0 ? results.get(0) : null;
+    }
+
+
+    @Override
+    public boolean removeSocialLink(UserModel user, String socialProvider) {
+        SocialLinkEntity entity = findSocialLink(user, socialProvider);
+        if (entity != null) {
+            em.remove(entity);
+            em.flush();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+
+    @Override
+    public boolean isSocial() {
+        return realm.isSocial();
+    }
+
+    @Override
+    public void setSocial(boolean social) {
+        realm.setSocial(social);
+        em.flush();
+    }
+
+    @Override
+    public boolean isUpdateProfileOnInitialSocialLogin() {
+        return realm.isUpdateProfileOnInitialSocialLogin();
+    }
+
+    @Override
+    public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
+        realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
+        em.flush();
+    }
+
+    @Override
+    public List<UserModel> getUsers() {
+        return session.getUsers(this);
+    }
+
+    @Override
+    public List<UserModel> searchForUser(String search) {
+        return session.searchForUser(search, this);
+    }
+
+    @Override
+    public List<UserModel> searchForUserByAttributes(Map<String, String> attributes) {
+        return session.searchForUserByAttributes(attributes, this);
+    }
+
+    @Override
+    public OAuthClientModel addOAuthClient(String name) {
+        return this.addOAuthClient(KeycloakModelUtils.generateId(), name);
+    }
+
+    @Override
+    public OAuthClientModel addOAuthClient(String id, String name) {
+        OAuthClientEntity data = new OAuthClientEntity();
+        data.setId(id);
+        data.setEnabled(true);
+        data.setName(name);
+        data.setRealm(realm);
+        em.persist(data);
+        em.flush();
+        return new OAuthClientAdapter(this, data, em);
+    }
+
+    @Override
+    public boolean removeOAuthClient(String id) {
+        OAuthClientModel oauth = getOAuthClientById(id);
+        if (oauth == null) return false;
+        ((OAuthClientAdapter)oauth).deleteUserSessionAssociation();
+        OAuthClientEntity client = (OAuthClientEntity) ((OAuthClientAdapter) oauth).getEntity();
+        em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", client).executeUpdate();
+        em.remove(client);
+        return true;
+    }
+
+
+    @Override
+    public OAuthClientModel getOAuthClient(String name) {
+        TypedQuery<OAuthClientEntity> query = em.createNamedQuery("findOAuthClientByName", OAuthClientEntity.class);
+        query.setParameter("name", name);
+        query.setParameter("realm", realm);
+        List<OAuthClientEntity> entities = query.getResultList();
+        if (entities.size() == 0) return null;
+        return new OAuthClientAdapter(this, entities.get(0), em);
+    }
+
+    @Override
+    public OAuthClientModel getOAuthClientById(String id) {
+        return session.getOAuthClientById(id, this);
+    }
+
+
+    @Override
+    public List<OAuthClientModel> getOAuthClients() {
+        TypedQuery<OAuthClientEntity> query = em.createNamedQuery("findOAuthClientByRealm", OAuthClientEntity.class);
+        query.setParameter("realm", realm);
+        List<OAuthClientEntity> entities = query.getResultList();
+        List<OAuthClientModel> list = new ArrayList<OAuthClientModel>();
+        for (OAuthClientEntity entity : entities) list.add(new OAuthClientAdapter(this, entity, em));
+        return list;
+    }
+
+    @Override
+    public Map<String, String> getSmtpConfig() {
+        return realm.getSmtpConfig();
+    }
+
+    @Override
+    public void setSmtpConfig(Map<String, String> smtpConfig) {
+        realm.setSmtpConfig(smtpConfig);
+        em.flush();
+    }
+
+    @Override
+    public Map<String, String> getSocialConfig() {
+        return realm.getSocialConfig();
+    }
+
+    @Override
+    public void setSocialConfig(Map<String, String> socialConfig) {
+        realm.setSocialConfig(socialConfig);
+        em.flush();
+    }
+
+    @Override
+    public Map<String, String> getLdapServerConfig() {
+        return realm.getLdapServerConfig();
+    }
+
+    @Override
+    public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
+        realm.setLdapServerConfig(ldapServerConfig);
+        em.flush();
+    }
+
+    @Override
+    public List<AuthenticationProviderModel> getAuthenticationProviders() {
+        List<AuthenticationProviderEntity> entities = realm.getAuthenticationProviders();
+        List<AuthenticationProviderEntity> copy = new ArrayList<AuthenticationProviderEntity>();
+        for (AuthenticationProviderEntity entity : entities) {
+            copy.add(entity);
+
+        }
+        Collections.sort(copy, new Comparator<AuthenticationProviderEntity>() {
+
+            @Override
+            public int compare(AuthenticationProviderEntity o1, AuthenticationProviderEntity o2) {
+                return o1.getPriority() - o2.getPriority();
+            }
+
+        });
+        List<AuthenticationProviderModel> result = new ArrayList<AuthenticationProviderModel>();
+        for (AuthenticationProviderEntity entity : copy) {
+            result.add(new AuthenticationProviderModel(entity.getProviderName(), entity.isPasswordUpdateSupported(), entity.getConfig()));
+        }
+
+        return result;
+    }
+
+    @Override
+    public void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders) {
+        List<AuthenticationProviderEntity> newEntities = new ArrayList<AuthenticationProviderEntity>();
+        int counter = 1;
+        for (AuthenticationProviderModel model : authenticationProviders) {
+            AuthenticationProviderEntity entity = new AuthenticationProviderEntity();
+            entity.setProviderName(model.getProviderName());
+            entity.setPasswordUpdateSupported(model.isPasswordUpdateSupported());
+            entity.setConfig(model.getConfig());
+            entity.setPriority(counter++);
+            newEntities.add(entity);
+        }
+
+        // Remove all existing first
+        Collection<AuthenticationProviderEntity> existing = realm.getAuthenticationProviders();
+        Collection<AuthenticationProviderEntity> copy = new ArrayList<AuthenticationProviderEntity>(existing);
+        for (AuthenticationProviderEntity apToRemove : copy) {
+            existing.remove(apToRemove);
+            em.remove(apToRemove);
+        }
+
+        // Now create all new providers
+        for (AuthenticationProviderEntity apToAdd : newEntities) {
+            existing.add(apToAdd);
+            em.persist(apToAdd);
+        }
+
+        em.flush();
+    }
+
+    @Override
+    public RoleModel getRole(String name) {
+        TypedQuery<RoleEntity> query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class);
+        query.setParameter("name", name);
+        query.setParameter("realm", realm);
+        List<RoleEntity> roles = query.getResultList();
+        if (roles.size() == 0) return null;
+        return new RoleAdapter(this, em, roles.get(0));
+    }
+
+    @Override
+    public RoleModel addRole(String name) {
+        return this.addRole(KeycloakModelUtils.generateId(), name);
+    }
+
+    @Override
+    public RoleModel addRole(String id, String name) {
+        RoleEntity entity = new RoleEntity();
+        entity.setId(id);
+        entity.setName(name);
+        entity.setRealm(realm);
+        entity.setRealmId(realm.getId());
+        realm.getRoles().add(entity);
+        em.persist(entity);
+        em.flush();
+        return new RoleAdapter(this, em, entity);
+    }
+
+    @Override
+    public boolean removeRole(RoleModel role) {
+        if (role == null) {
+            return false;
+        }
+        if (!role.getContainer().equals(this)) return false;
+
+        RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+        realm.getRoles().remove(role);
+        realm.getDefaultRoles().remove(role);
+
+        em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", roleEntity).executeUpdate();
+        em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", roleEntity).executeUpdate();
+        em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", roleEntity).executeUpdate();
+
+        em.remove(roleEntity);
+
+        return true;
+    }
+
+    @Override
+    public Set<RoleModel> getRoles() {
+        Set<RoleModel> list = new HashSet<RoleModel>();
+        Collection<RoleEntity> roles = realm.getRoles();
+        if (roles == null) return list;
+        for (RoleEntity entity : roles) {
+            list.add(new RoleAdapter(this, em, entity));
+        }
+        return list;
+    }
+
+    @Override
+    public RoleModel getRoleById(String id) {
+        return session.getRoleById(id, this);
+    }
+
+    @Override
+    public boolean removeRoleById(String id) {
+        RoleModel role = getRoleById(id);
+        if (role == null) return false;
+        return role.getContainer().removeRole(role);
+    }
+
+    @Override
+    public boolean validatePassword(UserModel user, String password) {
+        return CredentialValidation.validatePassword(this, user, password);
+    }
+
+    @Override
+    public boolean validateTOTP(UserModel user, String password, String token) {
+        if (!validatePassword(user, password)) return false;
+        for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
+            if (cred.getType().equals(UserCredentialModel.TOTP)) {
+                return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public PasswordPolicy getPasswordPolicy() {
+        if (passwordPolicy == null) {
+            passwordPolicy = new PasswordPolicy(realm.getPasswordPolicy());
+        }
+        return passwordPolicy;
+    }
+
+    @Override
+    public void setPasswordPolicy(PasswordPolicy policy) {
+        this.passwordPolicy = policy;
+        realm.setPasswordPolicy(policy.toString());
+        em.flush();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof RealmModel)) return false;
+
+        RealmModel that = (RealmModel) o;
+        return that.getId().equals(getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+
+    @Override
+    public String getLoginTheme() {
+        return realm.getLoginTheme();
+    }
+
+    @Override
+    public void setLoginTheme(String name) {
+        realm.setLoginTheme(name);
+        em.flush();
+    }
+
+    @Override
+    public String getAccountTheme() {
+        return realm.getAccountTheme();
+    }
+
+    @Override
+    public void setAccountTheme(String name) {
+        realm.setAccountTheme(name);
+        em.flush();
+    }
+
+    @Override
+    public String getAdminTheme() {
+        return realm.getAdminTheme();
+    }
+
+    @Override
+    public void setAdminTheme(String name) {
+        realm.setAdminTheme(name);
+        em.flush();
+    }
+
+    @Override
+    public String getEmailTheme() {
+        return realm.getEmailTheme();
+    }
+
+    @Override
+    public void setEmailTheme(String name) {
+        realm.setEmailTheme(name);
+        em.flush();
+    }
+
+    @Override
+    public boolean isAuditEnabled() {
+        return realm.isAuditEnabled();
+    }
+
+    @Override
+    public void setAuditEnabled(boolean enabled) {
+        realm.setAuditEnabled(enabled);
+        em.flush();
+    }
+
+    @Override
+    public long getAuditExpiration() {
+        return realm.getAuditExpiration();
+    }
+
+    @Override
+    public void setAuditExpiration(long expiration) {
+        realm.setAuditExpiration(expiration);
+        em.flush();
+    }
+
+    @Override
+    public Set<String> getAuditListeners() {
+        return realm.getAuditListeners();
+    }
+
+    @Override
+    public void setAuditListeners(Set<String> listeners) {
+        realm.setAuditListeners(listeners);
+        em.flush();
+    }
+
+    @Override
+    public ApplicationModel getMasterAdminApp() {
+        return new ApplicationAdapter(this, em, realm.getMasterAdminApp());
+    }
+
+    @Override
+    public void setMasterAdminApp(ApplicationModel app) {
+        realm.setMasterAdminApp(((ApplicationAdapter) app).getJpaEntity());
+        em.flush();
+    }
+
+    @Override
+    public UserSessionModel createUserSession(UserModel user, String ipAddress) {
+        return session.createUserSession(this, user, ipAddress);
+    }
+
+    @Override
+    public UserSessionModel getUserSession(String id) {
+        return session.getUserSession(id, this);
+    }
+
+    @Override
+    public List<UserSessionModel> getUserSessions(UserModel user) {
+        return session.getUserSessions(user, this);
+    }
+
+    @Override
+    public void removeUserSession(UserSessionModel session) {
+        this.session.removeUserSession(session);
+    }
+
+    @Override
+    public void removeUserSessions() {
+        session.removeUserSessions(this);
+
+    }
+
+    @Override
+    public void removeUserSessions(UserModel user) {
+        session.removeUserSessions(this, user);
+    }
+
+    @Override
+    public void removeExpiredUserSessions() {
+        session.removeExpiredUserSessions(this);
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
new file mode 100755
index 0000000..28a1f47
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
@@ -0,0 +1,135 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RoleAdapter implements RoleModel {
+    protected RoleEntity role;
+    protected EntityManager em;
+    protected RealmModel realm;
+
+    public RoleAdapter(RealmModel realm, EntityManager em, RoleEntity role) {
+        this.em = em;
+        this.realm = realm;
+        this.role = role;
+    }
+
+    public RoleEntity getRole() {
+        return role;
+    }
+
+    public void setRole(RoleEntity role) {
+        this.role = role;
+    }
+
+    @Override
+    public String getName() {
+        return role.getName();
+    }
+
+    @Override
+    public String getDescription() {
+        return role.getDescription();
+    }
+
+    @Override
+    public void setDescription(String description) {
+        role.setDescription(description);
+    }
+
+    @Override
+    public String getId() {
+        return role.getId();
+    }
+
+    @Override
+    public void setName(String name) {
+        role.setName(name);
+    }
+
+    @Override
+    public boolean isComposite() {
+        return getComposites().size() > 0;
+    }
+
+    @Override
+    public void addCompositeRole(RoleModel role) {
+        RoleEntity entity = RoleAdapter.toRoleEntity(role, em);
+        for (RoleEntity composite : getRole().getCompositeRoles()) {
+            if (composite.equals(entity)) return;
+        }
+        getRole().getCompositeRoles().add(entity);
+        em.flush();
+    }
+
+    @Override
+    public void removeCompositeRole(RoleModel role) {
+        RoleEntity entity = RoleAdapter.toRoleEntity(role, em);
+        Iterator<RoleEntity> it = getRole().getCompositeRoles().iterator();
+        while (it.hasNext()) {
+            if (it.next().equals(entity)) it.remove();
+        }
+    }
+
+    @Override
+    public Set<RoleModel> getComposites() {
+        Set<RoleModel> set = new HashSet<RoleModel>();
+
+        for (RoleEntity composite : getRole().getCompositeRoles()) {
+           set.add(new RoleAdapter(realm, em, composite));
+        }
+        return set;
+    }
+
+    @Override
+    public boolean hasRole(RoleModel role) {
+        if (this.equals(role)) return true;
+        if (!isComposite()) return false;
+
+        Set<RoleModel> visited = new HashSet<RoleModel>();
+        return KeycloakModelUtils.searchFor(role, this, visited);
+    }
+
+    @Override
+    public RoleContainerModel getContainer() {
+        if (role.isApplicationRole()) {
+            return realm.getApplicationById(role.getApplication().getId());
+
+        } else {
+            return realm;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof RoleModel)) return false;
+
+        RoleModel that = (RoleModel) o;
+        return that.getId().equals(getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+
+    public static RoleEntity toRoleEntity(RoleModel model, EntityManager em) {
+        if (model instanceof RoleAdapter) {
+            return ((RoleAdapter)model).getRole();
+        }
+        return em.getReference(RoleEntity.class, model.getId());
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
new file mode 100755
index 0000000..18219f6
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
@@ -0,0 +1,384 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.jpa.entities.AuthenticationLinkEntity;
+import org.keycloak.models.jpa.entities.CredentialEntity;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.jpa.entities.UserEntity;
+import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserAdapter implements UserModel {
+
+    protected UserEntity user;
+    protected EntityManager em;
+    protected RealmModel realm;
+
+    public UserAdapter(RealmModel realm, EntityManager em, UserEntity user) {
+        this.em = em;
+        this.user = user;
+        this.realm = realm;
+    }
+
+    public UserEntity getUser() {
+        return user;
+    }
+
+    @Override
+    public String getId() {
+        return user.getId();
+    }
+
+    @Override
+    public String getUsername() {
+        return user.getUsername();
+    }
+
+    @Override
+    public void setUsername(String username) {
+        user.setUsername(username);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return user.isEnabled();
+    }
+
+    @Override
+    public boolean isTotp() {
+        return user.isTotp();
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        user.setEnabled(enabled);
+    }
+
+    @Override
+    public void setAttribute(String name, String value) {
+        Map<String, String> attributes = user.getAttributes();
+        if (attributes == null) {
+            attributes = new HashMap<String, String>();
+        }
+        attributes.put(name, value);
+        user.setAttributes(attributes);
+    }
+
+    @Override
+    public void removeAttribute(String name) {
+        Map<String, String> attributes = user.getAttributes();
+        if (attributes == null) {
+            attributes = new HashMap<String, String>();
+        }
+        attributes.remove(name);
+        user.setAttributes(attributes);
+    }
+
+    @Override
+    public String getAttribute(String name) {
+        if (user.getAttributes() == null) return null;
+        return user.getAttributes().get(name);
+    }
+
+    @Override
+    public Map<String, String> getAttributes() {
+        Map<String, String> result = new HashMap<String, String>();
+        result.putAll(user.getAttributes());
+        return result;
+    }
+
+    @Override
+    public Set<RequiredAction> getRequiredActions() {
+        Set<RequiredAction> result = new HashSet<RequiredAction>();
+        result.addAll(user.getRequiredActions());
+        return result;
+    }
+
+    @Override
+    public void addRequiredAction(RequiredAction action) {
+        user.getRequiredActions().add(action);
+    }
+
+    @Override
+    public void removeRequiredAction(RequiredAction action) {
+        user.getRequiredActions().remove(action);
+    }
+
+
+    @Override
+    public String getFirstName() {
+        return user.getFirstName();
+    }
+
+    @Override
+    public void setFirstName(String firstName) {
+        user.setFirstName(firstName);
+    }
+
+    @Override
+    public String getLastName() {
+        return user.getLastName();
+    }
+
+    @Override
+    public void setLastName(String lastName) {
+        user.setLastName(lastName);
+    }
+
+    @Override
+    public String getEmail() {
+        return user.getEmail();
+    }
+
+    @Override
+    public void setEmail(String email) {
+        user.setEmail(email);
+    }
+
+    @Override
+    public boolean isEmailVerified() {
+        return user.isEmailVerified();
+    }
+
+    @Override
+    public void setEmailVerified(boolean verified) {
+        user.setEmailVerified(verified);
+    }
+
+    @Override
+    public void setTotp(boolean totp) {
+        user.setTotp(totp);
+    }
+
+    @Override
+    public void updateCredential(UserCredentialModel cred) {
+        CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
+
+        if (credentialEntity == null) {
+            credentialEntity = new CredentialEntity();
+            credentialEntity.setType(cred.getType());
+            credentialEntity.setDevice(cred.getDevice());
+            credentialEntity.setUser(user);
+            em.persist(credentialEntity);
+            user.getCredentials().add(credentialEntity);
+        }
+        if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+            byte[] salt = getSalt();
+            int hashIterations = 1;
+            PasswordPolicy policy = realm.getPasswordPolicy();
+            if (policy != null) {
+                hashIterations = policy.getHashIterations();
+                if (hashIterations == -1) hashIterations = 1;
+            }
+            credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
+            credentialEntity.setSalt(salt);
+            credentialEntity.setHashIterations(hashIterations);
+        } else {
+            credentialEntity.setValue(cred.getValue());
+        }
+        credentialEntity.setDevice(cred.getDevice());
+        em.flush();
+    }
+
+    private CredentialEntity getCredentialEntity(UserEntity userEntity, String credType) {
+        for (CredentialEntity entity : userEntity.getCredentials()) {
+            if (entity.getType().equals(credType)) {
+                return entity;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public List<UserCredentialValueModel> getCredentialsDirectly() {
+        List<CredentialEntity> credentials = new ArrayList<CredentialEntity>(user.getCredentials());
+        List<UserCredentialValueModel> result = new ArrayList<UserCredentialValueModel>();
+
+        if (credentials != null) {
+            for (CredentialEntity credEntity : credentials) {
+                UserCredentialValueModel credModel = new UserCredentialValueModel();
+                credModel.setType(credEntity.getType());
+                credModel.setDevice(credEntity.getDevice());
+                credModel.setValue(credEntity.getValue());
+                credModel.setSalt(credEntity.getSalt());
+                credModel.setHashIterations(credEntity.getHashIterations());
+
+                result.add(credModel);
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public void updateCredentialDirectly(UserCredentialValueModel credModel) {
+        CredentialEntity credentialEntity = getCredentialEntity(user, credModel.getType());
+
+        if (credentialEntity == null) {
+            credentialEntity = new CredentialEntity();
+            credentialEntity.setType(credModel.getType());
+            credentialEntity.setUser(user);
+            em.persist(credentialEntity);
+            user.getCredentials().add(credentialEntity);
+        }
+
+        credentialEntity.setValue(credModel.getValue());
+        credentialEntity.setSalt(credModel.getSalt());
+        credentialEntity.setDevice(credModel.getDevice());
+        credentialEntity.setHashIterations(credModel.getHashIterations());
+
+        em.flush();
+    }
+
+    @Override
+    public boolean hasRole(RoleModel role) {
+        Set<RoleModel> roles = getRoleMappings();
+        if (roles.contains(role)) return true;
+
+        for (RoleModel mapping : roles) {
+            if (mapping.hasRole(role)) return true;
+        }
+        return false;
+    }
+
+    protected TypedQuery<UserRoleMappingEntity> getUserRoleMappingEntityTypedQuery(RoleModel role) {
+        TypedQuery<UserRoleMappingEntity> query = em.createNamedQuery("userHasRole", UserRoleMappingEntity.class);
+        query.setParameter("user", getUser());
+        RoleEntity roleEntity = em.getReference(RoleEntity.class, role.getId());
+        query.setParameter("role", roleEntity);
+        return query;
+    }
+
+    @Override
+    public void grantRole(RoleModel role) {
+        if (hasRole(role)) return;
+        UserRoleMappingEntity entity = new UserRoleMappingEntity();
+        entity.setUser(getUser());
+        RoleEntity roleEntity = em.getReference(RoleEntity.class, role.getId());
+        entity.setRole(roleEntity);
+        em.persist(entity);
+        em.flush();
+        em.detach(entity);
+    }
+
+    @Override
+    public Set<RoleModel> getRealmRoleMappings() {
+        Set<RoleModel> roleMappings = getRoleMappings();
+
+        Set<RoleModel> realmRoles = new HashSet<RoleModel>();
+        for (RoleModel role : roleMappings) {
+            RoleContainerModel container = role.getContainer();
+            if (container instanceof RealmModel) {
+                realmRoles.add(role);
+            }
+        }
+        return realmRoles;
+    }
+
+
+    @Override
+    public Set<RoleModel> getRoleMappings() {
+        // we query ids only as the role might be cached and following the @ManyToOne will result in a load
+        // even if we're getting just the id.
+        TypedQuery<String> query = em.createNamedQuery("userRoleMappingIds", String.class);
+        query.setParameter("user", getUser());
+        List<String> ids = query.getResultList();
+        Set<RoleModel> roles = new HashSet<RoleModel>();
+        for (String roleId : ids) {
+            RoleModel roleById = realm.getRoleById(roleId);
+            if (roleById == null) continue;
+            roles.add(roleById);
+        }
+        return roles;
+    }
+
+    @Override
+    public void deleteRoleMapping(RoleModel role) {
+        if (user == null || role == null) return;
+
+        TypedQuery<UserRoleMappingEntity> query = getUserRoleMappingEntityTypedQuery(role);
+        List<UserRoleMappingEntity> results = query.getResultList();
+        if (results.size() == 0) return;
+        for (UserRoleMappingEntity entity : results) {
+            em.remove(entity);
+        }
+        em.flush();
+    }
+
+    @Override
+    public Set<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
+        Set<RoleModel> roleMappings = getRoleMappings();
+
+        Set<RoleModel> roles = new HashSet<RoleModel>();
+        for (RoleModel role : roleMappings) {
+            RoleContainerModel container = role.getContainer();
+            if (container instanceof ApplicationModel) {
+                ApplicationModel appModel = (ApplicationModel)container;
+                if (appModel.getId().equals(app.getId())) {
+                   roles.add(role);
+                }
+            }
+        }
+        return roles;
+    }
+
+    @Override
+    public AuthenticationLinkModel getAuthenticationLink() {
+        AuthenticationLinkEntity authLinkEntity = user.getAuthenticationLink();
+        return authLinkEntity == null ? null : new AuthenticationLinkModel(authLinkEntity.getAuthProvider(), authLinkEntity.getAuthUserId());
+    }
+
+    @Override
+    public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) {
+        AuthenticationLinkEntity entity = new AuthenticationLinkEntity();
+        entity.setAuthProvider(authenticationLink.getAuthProvider());
+        entity.setAuthUserId(authenticationLink.getAuthUserId());
+
+        user.setAuthenticationLink(entity);
+        em.persist(entity);
+        em.persist(user);
+        em.flush();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof UserModel)) return false;
+
+        UserModel that = (UserModel) o;
+        return that.getId().equals(getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+
+
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UsernameLoginFailureAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UsernameLoginFailureAdapter.java
new file mode 100755
index 0000000..ba43c82
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UsernameLoginFailureAdapter.java
@@ -0,0 +1,70 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.jpa.entities.UsernameLoginFailureEntity;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel
+{
+   protected UsernameLoginFailureEntity user;
+
+   public UsernameLoginFailureAdapter(UsernameLoginFailureEntity user)
+   {
+      this.user = user;
+   }
+
+   @Override
+   public String getUsername()
+   {
+      return user.getUsername();
+   }
+
+   @Override
+   public int getFailedLoginNotBefore() {
+      return user.getFailedLoginNotBefore();
+   }
+
+   @Override
+   public void setFailedLoginNotBefore(int notBefore) {
+      user.setFailedLoginNotBefore(notBefore);
+   }
+
+   @Override
+   public int getNumFailures() {
+      return user.getNumFailures();
+   }
+
+   @Override
+   public void incrementFailures() {
+      user.setNumFailures(getNumFailures() + 1);
+   }
+
+   @Override
+   public void clearFailures() {
+      user.setNumFailures(0);
+   }
+
+   @Override
+   public long getLastFailure() {
+      return user.getLastFailure();
+   }
+
+   @Override
+   public void setLastFailure(long lastFailure) {
+      user.setLastFailure(lastFailure);
+   }
+
+   @Override
+   public String getLastIPFailure() {
+      return user.getLastIPFailure();
+   }
+
+   @Override
+   public void setLastIPFailure(String ip) {
+      user.setLastIPFailure(ip);
+   }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java
new file mode 100755
index 0000000..333f7d1
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java
@@ -0,0 +1,130 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity;
+import org.keycloak.models.jpa.entities.UserSessionEntity;
+
+import javax.persistence.EntityManager;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSessionAdapter implements UserSessionModel {
+
+    private RealmModel realm;
+    private UserSessionEntity entity;
+    private EntityManager em;
+
+    public UserSessionAdapter(EntityManager em, RealmModel realm, UserSessionEntity entity) {
+        this.entity = entity;
+        this.em = em;
+        this.realm = realm;
+    }
+
+    public UserSessionEntity getEntity() {
+        return entity;
+    }
+
+    @Override
+    public String getId() {
+        return entity.getId();
+    }
+
+    @Override
+    public void setId(String id) {
+        entity.setId(id);
+    }
+
+    @Override
+    public UserModel getUser() {
+        return realm.getUserById(entity.getUserId());
+    }
+
+    @Override
+    public void setUser(UserModel user) {
+        entity.setUserId(user.getId());
+    }
+
+    @Override
+    public String getIpAddress() {
+        return entity.getIpAddress();
+    }
+
+    @Override
+    public void setIpAddress(String ipAddress) {
+        entity.setIpAddress(ipAddress);
+    }
+
+    @Override
+    public int getStarted() {
+        return entity.getStarted();
+    }
+
+    @Override
+    public void setStarted(int started) {
+        entity.setStarted(started);
+    }
+
+    @Override
+    public int getLastSessionRefresh() {
+        return entity.getLastSessionRefresh();
+    }
+
+    @Override
+    public void setLastSessionRefresh(int seconds) {
+        entity.setLastSessionRefresh(seconds);
+    }
+
+    @Override
+    public void associateClient(ClientModel client) {
+        for (ClientUserSessionAssociationEntity ass : entity.getClients()) {
+            if (ass.getClientId().equals(client.getId())) return;
+        }
+        ClientUserSessionAssociationEntity association = new ClientUserSessionAssociationEntity();
+        association.setClientId(client.getId());
+        association.setSession(entity);
+        association.setUserId(entity.getUserId());
+        association.setRealmId(realm.getId());
+        em.persist(association);
+        entity.getClients().add(association);
+    }
+
+    @Override
+    public void removeAssociatedClient(ClientModel client) {
+        em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", client.getId()).executeUpdate();
+
+    }
+
+    @Override
+    public List<ClientModel> getClientAssociations() {
+        List<ClientModel> clients = new ArrayList<ClientModel>();
+        for (ClientUserSessionAssociationEntity association : entity.getClients()) {
+            ClientModel client = realm.findClientById(association.getClientId());
+            if (client == null) {
+                throw new ModelException("couldnt find client");
+            }
+            clients.add(client);
+        }
+        return clients;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof UserSessionModel)) return false;
+
+        UserSessionModel that = (UserSessionModel) o;
+        return that.getId().equals(getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/utils/JpaIdGenerator.java b/model/jpa/src/main/java/org/keycloak/models/jpa/utils/JpaIdGenerator.java
new file mode 100644
index 0000000..6fc10e8
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/utils/JpaIdGenerator.java
@@ -0,0 +1,19 @@
+package org.keycloak.models.jpa.utils;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.spi.SessionImplementor;
+import org.hibernate.id.IdentifierGenerator;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class JpaIdGenerator implements IdentifierGenerator {
+
+    @Override
+    public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
+        return KeycloakModelUtils.generateId();
+    }
+}
diff --git a/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProviderFactory b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProviderFactory
new file mode 100644
index 0000000..d981d8c
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.jpa.JpaModelProviderFactory
\ No newline at end of file
diff --git a/model/jpa/src/test/resources/META-INF/persistence.xml b/model/jpa/src/test/resources/META-INF/persistence.xml
new file mode 100755
index 0000000..a150d05
--- /dev/null
+++ b/model/jpa/src/test/resources/META-INF/persistence.xml
@@ -0,0 +1,70 @@
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+    version="1.0">
+    <persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
+        <provider>org.hibernate.ejb.HibernatePersistence</provider>
+
+        <class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
+        <class>org.keycloak.models.jpa.entities.CredentialEntity</class>
+        <class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
+        <class>org.keycloak.models.jpa.entities.RealmEntity</class>
+        <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
+        <class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
+        <class>org.keycloak.models.jpa.entities.RoleEntity</class>
+        <class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
+        <class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
+        <class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
+        <class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
+        <class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
+
+        <exclude-unlisted-classes>true</exclude-unlisted-classes>
+
+        <properties>
+            <property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
+            <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
+            <property name="hibernate.connection.username" value="sa"/>
+            <property name="hibernate.connection.password" value=""/>
+            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
+            <property name="hibernate.show_sql" value="false" />
+            <property name="hibernate.format_sql" value="true" />
+        </properties>
+    </persistence-unit>
+
+    <!--
+    <persistence-unit name="picketlink-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
+        <provider>org.hibernate.ejb.HibernatePersistence</provider>
+
+        <class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
+        <class>org.keycloak.models.picketlink.mappings.RealmEntity</class>
+        <class>org.keycloak.models.picketlink.mappings.ApplicationEntity</class>
+
+        <exclude-unlisted-classes>true</exclude-unlisted-classes>
+
+        <properties>
+            <property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
+            <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
+            <property name="hibernate.connection.username" value="sa"/>
+            <property name="hibernate.connection.password" value=""/>
+            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
+            <property name="hibernate.show_sql" value="false" />
+            <property name="hibernate.format_sql" value="true" />
+        </properties>
+    </persistence-unit>
+    -->
+</persistence>

server/pom.xml 22(+1 -21)

diff --git a/server/pom.xml b/server/pom.xml
index d0318f4..2d8e61d 100755
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -49,27 +49,7 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-hybrid</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-realms-jpa</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-users-jpa</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-sessions-mem</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-sessions-jpa</artifactId>
+            <artifactId>keycloak-model-jpa</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>
diff --git a/server/src/main/resources/META-INF/keycloak-server.json b/server/src/main/resources/META-INF/keycloak-server.json
index 6133aa4..7e3f246 100755
--- a/server/src/main/resources/META-INF/keycloak-server.json
+++ b/server/src/main/resources/META-INF/keycloak-server.json
@@ -11,25 +11,13 @@
     },
 
     "model": {
-        "provider": "hybrid"
+        "provider": "jpa"
     },
 
     "modelCache": {
         "provider": "${keycloak.model.cache.provider:}"
     },
 
-    "modelRealms": {
-        "provider": "jpa"
-    },
-
-    "modelUsers": {
-        "provider": "jpa"
-    },
-
-    "modelSessions": {
-        "provider": "mem"
-    },
-
     "timer": {
         "provider": "basic"
     },
diff --git a/server/src/main/resources/META-INF/persistence.xml b/server/src/main/resources/META-INF/persistence.xml
index bba0831..a704014 100755
--- a/server/src/main/resources/META-INF/persistence.xml
+++ b/server/src/main/resources/META-INF/persistence.xml
@@ -4,23 +4,21 @@
     version="1.0">
     <persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
         <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
-
-        <class>org.keycloak.models.realms.jpa.entities.ApplicationEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.OAuthClientEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.RealmEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.RequiredCredentialEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.AuthenticationProviderEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.RoleEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.ScopeMappingEntity</class>
-
-        <class>org.keycloak.models.users.jpa.entities.UserAttributeEntity</class>
-        <class>org.keycloak.models.users.jpa.entities.UserCredentialEntity</class>
-        <class>org.keycloak.models.users.jpa.entities.UserRoleMappingEntity</class>
-        <class>org.keycloak.models.users.jpa.entities.UserEntity</class>
-
-        <class>org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity</class>
-        <class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
-        <class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
+        <class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
+        <class>org.keycloak.models.jpa.entities.CredentialEntity</class>
+        <class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
+        <class>org.keycloak.models.jpa.entities.RealmEntity</class>
+        <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
+        <class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
+        <class>org.keycloak.models.jpa.entities.RoleEntity</class>
+        <class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
+        <class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
+        <class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
+        <class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
+        <class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
 
         <exclude-unlisted-classes>true</exclude-unlisted-classes>
 
@@ -28,7 +26,7 @@
             <property name="hibernate.hbm2ddl.auto" value="update" />
         </properties>
     </persistence-unit>
-
+	
     <persistence-unit name="jpa-keycloak-audit-store" transaction-type="RESOURCE_LOCAL">
         <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
         <class>org.keycloak.audit.jpa.EventEntity</class>
@@ -39,4 +37,5 @@
             <property name="hibernate.hbm2ddl.auto" value="update" />
         </properties>
     </persistence-unit>
+	
 </persistence>
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 50466c8..052f6b1 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -122,27 +122,7 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-hybrid</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-realms-jpa</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-users-jpa</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-sessions-mem</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-sessions-jpa</artifactId>
+            <artifactId>keycloak-model-jpa</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>
diff --git a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
index c39a794..326c697 100755
--- a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
@@ -14,7 +14,7 @@
     },
 
     "model": {
-        "provider": "${keycloak.model.provider:hybrid}",
+        "provider": "${keycloak.model.provider:jpa}",
         "mongo": {
           "host": "${keycloak.model.mongo.host:127.0.0.1}",
           "port": "${keycloak.model.mongo.port:27017}",
@@ -27,18 +27,6 @@
         "provider": "${keycloak.model.cache.provider:simple}"
     },
 
-    "modelRealms": {
-        "provider": "jpa"
-    },
-
-    "modelUsers": {
-        "provider": "jpa"
-    },
-
-    "modelSessions": {
-        "provider": "mem"
-    },
-
     "timer": {
         "provider": "basic"
     },
diff --git a/testsuite/integration/src/main/resources/META-INF/persistence.xml b/testsuite/integration/src/main/resources/META-INF/persistence.xml
index 5a261d7..39dd390 100755
--- a/testsuite/integration/src/main/resources/META-INF/persistence.xml
+++ b/testsuite/integration/src/main/resources/META-INF/persistence.xml
@@ -5,22 +5,21 @@
     <persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
         <provider>org.hibernate.ejb.HibernatePersistence</provider>
 
-        <class>org.keycloak.models.realms.jpa.entities.ApplicationEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.OAuthClientEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.RealmEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.RequiredCredentialEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.AuthenticationProviderEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.RoleEntity</class>
-        <class>org.keycloak.models.realms.jpa.entities.ScopeMappingEntity</class>
-
-        <class>org.keycloak.models.users.jpa.entities.UserAttributeEntity</class>
-        <class>org.keycloak.models.users.jpa.entities.UserCredentialEntity</class>
-        <class>org.keycloak.models.users.jpa.entities.UserRoleMappingEntity</class>
-        <class>org.keycloak.models.users.jpa.entities.UserEntity</class>
-
-        <class>org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity</class>
-        <class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
-        <class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
+        <class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
+        <class>org.keycloak.models.jpa.entities.CredentialEntity</class>
+        <class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
+        <class>org.keycloak.models.jpa.entities.RealmEntity</class>
+        <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
+        <class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
+        <class>org.keycloak.models.jpa.entities.RoleEntity</class>
+        <class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
+        <class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserEntity</class>
+        <class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
+        <class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
+        <class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
 
         <exclude-unlisted-classes>true</exclude-unlisted-classes>
 
@@ -52,4 +51,38 @@
             <property name="hibernate.format_sql" value="true" />
         </properties>
     </persistence-unit>
+
+    <!--
+    <persistence-unit name="picketlink-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
+        <provider>org.hibernate.ejb.HibernatePersistence</provider>
+
+        <class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
+        <class>org.keycloak.models.picketlink.mappings.RealmEntity</class>
+        <class>org.keycloak.models.picketlink.mappings.ApplicationEntity</class>
+
+        <exclude-unlisted-classes>true</exclude-unlisted-classes>
+
+        <properties>
+            <property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
+            <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
+            <property name="hibernate.connection.username" value="sa"/>
+            <property name="hibernate.connection.password" value=""/>
+            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
+            <property name="hibernate.show_sql" value="false" />
+            <property name="hibernate.format_sql" value="true" />
+        </properties>
+    </persistence-unit>
+    -->
 </persistence>
diff --git a/testsuite/performance/pom.xml b/testsuite/performance/pom.xml
index 201abf3..1ba9c0f 100755
--- a/testsuite/performance/pom.xml
+++ b/testsuite/performance/pom.xml
@@ -42,31 +42,6 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-hybrid</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-realms-jpa</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-users-jpa</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-sessions-mem</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-sessions-jpa</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-model-mongo</artifactId>
             <version>${project.version}</version>
         </dependency>
diff --git a/testsuite/tools/pom.xml b/testsuite/tools/pom.xml
index 31d1492..c630624 100755
--- a/testsuite/tools/pom.xml
+++ b/testsuite/tools/pom.xml
@@ -68,26 +68,6 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-hybrid</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-realms-jpa</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-users-jpa</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-sessions-mem</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-audit-api</artifactId>
             <version>${project.version}</version>
         </dependency>