You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/11/15 16:50:13 UTC

[syncope] branch 2_1_X updated: [SYNCOPE-1395] integration tests working

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/2_1_X by this push:
     new a98edb1  [SYNCOPE-1395] integration tests working
a98edb1 is described below

commit a98edb15f9412fe0157b4020ffa08c5f2f257c2a
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Thu Nov 15 17:47:23 2018 +0100

    [SYNCOPE-1395] integration tests working
---
 .../validation/InvalidEntityException.java         |  19 +--
 core/persistence-jpa-pgjsonb/pom.xml               |  14 +-
 .../persistence/jpa/dao/PGJPAAnyObjectDAO.java     |   7 -
 .../core/persistence/jpa/dao/PGJPAConfDAO.java     |  51 +++++++
 .../core/persistence/jpa/dao/PGJPAGroupDAO.java    |  17 +--
 .../jpa/dao/PGJPAPlainAttrValueDAO.java            |  12 +-
 .../core/persistence/jpa/dao/PGJPAUserDAO.java     |   7 -
 .../jpa/entity/PGJPAEntityListener.java            |   7 +-
 .../jpa/entity/anyobject/PGJPAAnyObject.java       |  27 +++-
 .../entity/anyobject/PGJPAAnyObjectListener.java   |  12 +-
 .../jpa/entity/conf/PGJPAConfListener.java         |  12 +-
 .../persistence/jpa/entity/group/PGJPAGroup.java   |   4 +-
 .../jpa/entity/group/PGJPAGroupListener.java       |  12 +-
 .../persistence/jpa/entity/user/PGJPAUser.java     |  32 +++-
 .../jpa/entity/user/PGJPAUserListener.java         |  12 +-
 .../main/resources/META-INF/spring-orm-pgjsonb.xml |  64 ++++++++
 .../src/main/resources/persistence.properties      |   1 +
 .../src/test/resources/domains/MasterContent.xml   |   2 -
 .../persistence/jpa/dao/AbstractAnySearchDAO.java  |  12 +-
 .../core/persistence/jpa/dao/JPAConfDAO.java       |   2 -
 .../core/persistence/jpa/dao/JPAUserDAO.java       |   3 +
 .../entity/anyobject/JPAADynGroupMembership.java   |   4 +-
 .../jpa/entity/anyobject/JPAAMembership.java       |  12 +-
 .../jpa/entity/anyobject/JPAARelationship.java     |  10 +-
 .../persistence/jpa/entity/group/JPAGroup.java     |   9 +-
 .../jpa/entity/group/JPATypeExtension.java         |   3 +-
 .../jpa/entity/user/JPAUDynGroupMembership.java    |  11 +-
 .../jpa/entity/user/JPAUMembership.java            |  14 +-
 .../jpa/entity/user/JPAURelationship.java          |  12 +-
 .../main/resources/META-INF/spring-orm-oracle.xml  |  65 ++++++++
 .../resources/META-INF/spring-orm-sqlserver.xml    |  65 ++++++++
 .../src/main/resources/META-INF/spring-orm.xml     |  65 ++++++++
 .../src/main/resources/persistence.properties      |   1 +
 .../src/main/resources/persistenceContext.xml      |   1 +
 .../core/persistence/jpa/inner/DomainTest.java     |   7 +-
 .../java/data/AnyObjectDataBinderImpl.java         |   2 +
 .../java/data/ConfigurationDataBinderImpl.java     |   5 +-
 .../java/data/GroupDataBinderImpl.java             |   2 +
 .../provisioning/java/data/UserDataBinderImpl.java |   2 +
 .../core/provisioning/java/jexl/JexlUtils.java     |  90 ++++++-----
 .../core/rest/cxf/RestServiceExceptionMapper.java  |  42 +++---
 .../core/src/main/resources/persistence.properties |   1 +
 .../resources/docker-compose/docker-compose-ha.yml |  14 +-
 .../docker-compose/docker-compose-postgresql.yml   |  14 +-
 .../src/main/resources/persistence.properties      |   1 +
 fit/core-reference/pom.xml                         | 165 +++++++++++++++++++--
 .../resources/elasticsearch/persistence.properties |   1 +
 .../resources/pgjsonb/domains/Master.properties    |  22 +--
 .../src/main/resources/pgjsonb/indexes.xml         |  63 ++++++++
 .../main/resources/pgjsonb}/persistence.properties |   1 +
 .../main/resources/pgjsonb/provisioning.properties |  32 ++++
 .../src/main/resources/pgjsonb/views.xml           | 154 +++++++++++++++++++
 .../java/org/apache/syncope/fit/CamelDetector.java |  36 -----
 .../java/org/apache/syncope/fit/SCIMDetector.java  |  46 ------
 .../apache/syncope/fit/core/BpmnProcessITCase.java |   8 +-
 .../apache/syncope/fit/core/CamelRouteITCase.java  |  19 +--
 .../org/apache/syncope/fit/core/DomainITCase.java  |  11 +-
 .../org/apache/syncope/fit/core/GroupITCase.java   |  20 +--
 .../apache/syncope/fit/core/MembershipITCase.java  |   3 +-
 .../syncope/fit/core/MultitenancyITCase.java       |  21 ++-
 .../org/apache/syncope/fit/core/SCIMITCase.java    |  50 +++----
 .../org/apache/syncope/fit/core/UserITCase.java    |   8 +-
 .../apache/syncope/fit/core/UserIssuesITCase.java  |  23 +--
 pom.xml                                            |   2 +
 src/main/asciidoc/getting-started/obtain.adoc      |  16 +-
 65 files changed, 1073 insertions(+), 409 deletions(-)

diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java
index d6a3fe7..31719d0 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.persistence.api.attrvalue.validation;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import javax.validation.ConstraintViolation;
 import javax.validation.ValidationException;
@@ -45,8 +44,10 @@ public class InvalidEntityException extends ValidationException {
      * @param entityViolationType type of violation found
      * @param message message to be associated to the violation
      */
-    public InvalidEntityException(final Class<?> entityClass,
-            final EntityViolationType entityViolationType, final String message) {
+    public InvalidEntityException(
+            final Class<?> entityClass,
+            final EntityViolationType entityViolationType,
+            final String message) {
 
         super();
 
@@ -64,14 +65,15 @@ public class InvalidEntityException extends ValidationException {
      * @param entityClassSimpleName simple class name of invalid entity
      * @param violations as returned by bean validation
      */
-    public InvalidEntityException(final String entityClassSimpleName,
+    public InvalidEntityException(
+            final String entityClassSimpleName,
             final Set<ConstraintViolation<Object>> violations) {
 
         super();
 
         this.entityClassSimpleName = entityClassSimpleName;
 
-        violations.forEach((violation) -> {
+        violations.forEach(violation -> {
             int firstComma = violation.getMessageTemplate().indexOf(';');
 
             final String key = violation.getMessageTemplate().substring(
@@ -80,7 +82,6 @@ public class InvalidEntityException extends ValidationException {
             final String message = violation.getMessageTemplate().substring(firstComma > 0 ? firstComma + 1 : 0);
 
             EntityViolationType entityViolationType;
-
             try {
                 entityViolationType = EntityViolationType.valueOf(key.trim());
             } catch (IllegalArgumentException e) {
@@ -115,9 +116,9 @@ public class InvalidEntityException extends ValidationException {
     public String getMessage() {
         StringBuilder sb = new StringBuilder();
 
-        for (Entry<Class<?>, Set<EntityViolationType>> entry : violations.entrySet()) {
-            sb.append(entry.getKey().getSimpleName()).append(' ').append(entry.getValue().toString()).append(", ");
-        }
+        violations.forEach((key, value) -> {
+            sb.append(key.getSimpleName()).append(' ').append(value.toString()).append(", ");
+        });
         sb.delete(sb.lastIndexOf(", "), sb.length());
 
         return sb.toString();
diff --git a/core/persistence-jpa-pgjsonb/pom.xml b/core/persistence-jpa-pgjsonb/pom.xml
index de08084..831f9fb 100644
--- a/core/persistence-jpa-pgjsonb/pom.xml
+++ b/core/persistence-jpa-pgjsonb/pom.xml
@@ -187,18 +187,12 @@ under the License.
             <configuration>
               <images>
                 <image>
-                  <name>crunchydata/crunchy-postgres:centos7-10.5-2.1.0</name>
+                  <name>postgres:${docker.postgresql.version}</name>
                   <run>
                     <env>
-                      <PG_ROOT_PASSWORD>postgres</PG_ROOT_PASSWORD>
-                      <PG_MODE>primary</PG_MODE>
-                      <PG_PRIMARY_USER>postgres</PG_PRIMARY_USER>
-                      <PG_PRIMARY_PASSWORD>postgres</PG_PRIMARY_PASSWORD>
-                      <PG_PRIMARY_HOST>localhost</PG_PRIMARY_HOST>
-                      <PG_PRIMARY_PORT>5432</PG_PRIMARY_PORT>
-                      <PG_DATABASE>syncope</PG_DATABASE>
-                      <PG_USER>syncope</PG_USER>
-                      <PG_PASSWORD>syncope</PG_PASSWORD>
+                      <POSTGRES_DB>syncope</POSTGRES_DB>
+                      <POSTGRES_USER>syncope</POSTGRES_USER>
+                      <POSTGRES_PASSWORD>syncope</POSTGRES_PASSWORD>
                     </env>
                     <ports>
                       <port>5432:5432</port>
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAAnyObjectDAO.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAAnyObjectDAO.java
index 4879f26..f821f7f 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAAnyObjectDAO.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAAnyObjectDAO.java
@@ -24,7 +24,6 @@ import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.PGJPAAnyObject;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.springframework.transaction.annotation.Transactional;
 
 public class PGJPAAnyObjectDAO extends JPAAnyObjectDAO {
 
@@ -37,12 +36,6 @@ public class PGJPAAnyObjectDAO extends JPAAnyObjectDAO {
         return anyDAO;
     }
 
-    @Transactional(readOnly = true)
-    @Override
-    public AnyObject find(final String key) {
-        return entityManager().find(PGJPAAnyObject.class, key);
-    }
-
     @Override
     @SuppressWarnings("unchecked")
     public List<AnyObject> findByPlainAttrValue(
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAConfDAO.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAConfDAO.java
new file mode 100644
index 0000000..130d730
--- /dev/null
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAConfDAO.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import org.apache.openjpa.enhance.PersistenceCapable;
+import org.apache.openjpa.util.ImplHelper;
+import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.conf.Conf;
+
+public class PGJPAConfDAO extends JPAConfDAO {
+
+    /**
+     * Marks the {@code plainAttrs} field as dirty, to force OpenJPA generating an update statement on the
+     * SyncopeConf table - otherwise no update on the table itself would be generated when adding an attribute,
+     * as the {@code plainAttrs} JSON field gets updated by the entity listener.
+     */
+    private void dirten() {
+        PersistenceCapable pc = ImplHelper.toPersistenceCapable(get(), null);
+        if (pc != null) {
+            pc.pcGetStateManager().dirty("plainAttrs");
+        }
+    }
+
+    @Override
+    public Conf save(final CPlainAttr attr) {
+        dirten();
+        return super.save(attr);
+    }
+
+    @Override
+    public Conf delete(final String key) {
+        dirten();
+        return super.delete(key);
+    }
+}
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAGroupDAO.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAGroupDAO.java
index 4c020c6..e69252a 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAGroupDAO.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAGroupDAO.java
@@ -22,9 +22,8 @@ import java.util.List;
 import org.apache.syncope.core.persistence.api.dao.PGAnyDAO;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.persistence.jpa.entity.group.PGJPAGroup;
+import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.springframework.transaction.annotation.Transactional;
 
 public class PGJPAGroupDAO extends JPAGroupDAO {
 
@@ -37,12 +36,6 @@ public class PGJPAGroupDAO extends JPAGroupDAO {
         return anyDAO;
     }
 
-    @Transactional(readOnly = true)
-    @Override
-    public Group find(final String key) {
-        return entityManager().find(PGJPAGroup.class, key);
-    }
-
     @Override
     @SuppressWarnings("unchecked")
     public List<Group> findByPlainAttrValue(
@@ -50,7 +43,7 @@ public class PGJPAGroupDAO extends JPAGroupDAO {
             final PlainAttrValue attrValue,
             final boolean ignoreCaseMatch) {
 
-        return anyDAO().findByPlainAttrValue(PGJPAGroup.TABLE, anyUtils(), schemaKey, attrValue, ignoreCaseMatch);
+        return anyDAO().findByPlainAttrValue(JPAGroup.TABLE, anyUtils(), schemaKey, attrValue, ignoreCaseMatch);
     }
 
     @Override
@@ -59,7 +52,7 @@ public class PGJPAGroupDAO extends JPAGroupDAO {
             final PlainAttrValue attrUniqueValue,
             final boolean ignoreCaseMatch) {
 
-        return anyDAO().findByPlainAttrUniqueValue(PGJPAGroup.TABLE, anyUtils(),
+        return anyDAO().findByPlainAttrUniqueValue(JPAGroup.TABLE, anyUtils(),
                 schemaKey, attrUniqueValue, ignoreCaseMatch);
     }
 
@@ -70,12 +63,12 @@ public class PGJPAGroupDAO extends JPAGroupDAO {
             final String value,
             final boolean ignoreCaseMatch) {
 
-        return anyDAO().findByDerAttrValue(PGJPAGroup.TABLE, anyUtils(), schemaKey, value, ignoreCaseMatch);
+        return anyDAO().findByDerAttrValue(JPAGroup.TABLE, anyUtils(), schemaKey, value, ignoreCaseMatch);
     }
 
     @Override
     public Group save(final Group group) {
-        anyDAO().checkBeforeSave(PGJPAGroup.TABLE, anyUtils(), group);
+        anyDAO().checkBeforeSave(JPAGroup.TABLE, anyUtils(), group);
         return super.save(group);
     }
 }
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAPlainAttrValueDAO.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAPlainAttrValueDAO.java
index 0b67612..c1d9c54 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAPlainAttrValueDAO.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAPlainAttrValueDAO.java
@@ -18,12 +18,8 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
-import static org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO.getEntityReference;
-
-import java.util.stream.Collectors;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 
@@ -32,13 +28,7 @@ public class PGJPAPlainAttrValueDAO extends AbstractDAO<PlainAttrValue> implemen
     @Override
     public void deleteAll(final PlainAttr<?> attr, final AnyUtils anyUtils) {
         if (attr.getUniqueValue() == null) {
-            attr.getValues().stream().map(Entity::getKey).collect(Collectors.toSet()).forEach(attrValueKey -> {
-                PlainAttrValue attrValue = anyUtils.plainAttrValueClass().cast(
-                        entityManager().find(getEntityReference(anyUtils.plainAttrValueClass()), attrValueKey));
-                if (attrValue != null) {
-                    attr.getValues().remove(attrValue);
-                }
-            });
+            attr.getValues().clear();
         } else {
             attr.setUniqueValue(null);
         }
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAUserDAO.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAUserDAO.java
index 340ba6e..f1c420f 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAUserDAO.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAUserDAO.java
@@ -26,7 +26,6 @@ import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.user.PGJPAUser;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.springframework.transaction.annotation.Transactional;
 
 public class PGJPAUserDAO extends JPAUserDAO {
 
@@ -39,12 +38,6 @@ public class PGJPAUserDAO extends JPAUserDAO {
         return anyDAO;
     }
 
-    @Transactional(readOnly = true)
-    @Override
-    public User find(final String key) {
-        return entityManager().find(PGJPAUser.class, key);
-    }
-
     @Override
     @SuppressWarnings("unchecked")
     public List<User> findByPlainAttrValue(
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/PGJPAEntityListener.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/PGJPAEntityListener.java
index ddbb13e..2c4c4e4 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/PGJPAEntityListener.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/PGJPAEntityListener.java
@@ -27,7 +27,10 @@ public abstract class PGJPAEntityListener<A extends Any<?>> {
     protected abstract List<? extends PGPlainAttr<A>> getValues(String plainAttrsJSON);
 
     @SuppressWarnings("unchecked")
-    protected void read(final PGJPAAny<A> entity) {
+    protected void json2list(final PGJPAAny<A> entity, final boolean clearFirst) {
+        if (clearFirst) {
+            entity.getPlainAttrList().clear();
+        }
         if (entity.getPlainAttrsJSON() != null) {
             getValues(entity.getPlainAttrsJSON()).stream().filter(attr -> attr.getSchema() != null).
                     map(attr -> {
@@ -41,7 +44,7 @@ public abstract class PGJPAEntityListener<A extends Any<?>> {
         }
     }
 
-    protected void save(final PGJPAAny<A> entity) {
+    protected void list2json(final PGJPAAny<A> entity) {
         entity.setPlainAttrsJSON(entity.getPlainAttrList().isEmpty()
                 ? "[{}]"
                 : POJOHelper.serialize(entity.getPlainAttrList()));
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/PGJPAAnyObject.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/PGJPAAnyObject.java
index dad8536..d98c26a 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/PGJPAAnyObject.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/PGJPAAnyObject.java
@@ -20,12 +20,15 @@ package org.apache.syncope.core.persistence.jpa.entity.anyobject;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.EntityListeners;
 import javax.persistence.Lob;
 import javax.persistence.Table;
 import javax.persistence.Transient;
+import org.apache.syncope.core.persistence.api.entity.Membership;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
 import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
@@ -78,10 +81,7 @@ public class PGJPAAnyObject extends JPAAnyObject implements PGJPAAny<AnyObject>,
     public boolean remove(final APlainAttr attr) {
         return plainAttrList.removeIf(pgattr
                 -> pgattr.getSchemaKey().equals(attr.getSchema().getKey())
-                && attr.getOwner().getKey().equals(getKey())
-                && attr.getMembership() == null
-                ? true
-                : pgattr.getMembership() != null && pgattr.getMembershipKey().equals(attr.getMembership().getKey()));
+                && Objects.equals(pgattr.getMembershipKey(), ((PGAPlainAttr) attr).getMembershipKey()));
     }
 
     @Override
@@ -90,8 +90,25 @@ public class PGJPAAnyObject extends JPAAnyObject implements PGJPAAny<AnyObject>,
     }
 
     @Override
+    public Optional<? extends APlainAttr> getPlainAttr(final String plainSchema) {
+        return plainAttrList.stream().
+                filter(pgattr -> pgattr.getSchemaKey() != null && pgattr.getSchemaKey().equals(plainSchema)
+                && pgattr.getMembershipKey() == null).
+                findFirst();
+    }
+
+    @Override
+    public Optional<? extends APlainAttr> getPlainAttr(final String plainSchema, final Membership<?> membership) {
+        return plainAttrList.stream().
+                filter(pgattr -> pgattr.getSchemaKey() != null && pgattr.getSchemaKey().equals(plainSchema)
+                && pgattr.getMembershipKey() != null && pgattr.getMembershipKey().equals(membership.getKey())).
+                findFirst();
+    }
+
+    @Override
     public boolean remove(final AMembership membership) {
-        plainAttrList.removeIf(attr -> attr.getMembership().getKey().equals(membership.getKey()));
+        plainAttrList.removeIf(pgattr
+                -> pgattr.getMembershipKey() != null && pgattr.getMembershipKey().equals(membership.getKey()));
         return super.remove(membership);
     }
 }
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/PGJPAAnyObjectListener.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/PGJPAAnyObjectListener.java
index 61b201f..112a676 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/PGJPAAnyObjectListener.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/PGJPAAnyObjectListener.java
@@ -21,6 +21,8 @@ package org.apache.syncope.core.persistence.jpa.entity.anyobject;
 import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.List;
 import javax.persistence.PostLoad;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
 import javax.persistence.PrePersist;
 import javax.persistence.PreUpdate;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
@@ -38,12 +40,18 @@ public class PGJPAAnyObjectListener extends PGJPAEntityListener<AnyObject> {
 
     @PostLoad
     public void read(final PGJPAAnyObject anyObject) {
-        super.read(anyObject);
+        super.json2list(anyObject, false);
     }
 
     @PrePersist
     @PreUpdate
     public void save(final PGJPAAnyObject anyObject) {
-        super.save(anyObject);
+        super.list2json(anyObject);
+    }
+
+    @PostPersist
+    @PostUpdate
+    public void readAfterSave(final PGJPAAnyObject anyObject) {
+        super.json2list(anyObject, true);
     }
 }
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/PGJPAConfListener.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/PGJPAConfListener.java
index f1a7198..00ad5db 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/PGJPAConfListener.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/PGJPAConfListener.java
@@ -21,6 +21,8 @@ package org.apache.syncope.core.persistence.jpa.entity.conf;
 import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.List;
 import javax.persistence.PostLoad;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
 import javax.persistence.PrePersist;
 import javax.persistence.PreUpdate;
 import org.apache.syncope.core.persistence.api.entity.conf.Conf;
@@ -38,12 +40,18 @@ public class PGJPAConfListener extends PGJPAEntityListener<Conf> {
 
     @PostLoad
     public void read(final PGJPAConf conf) {
-        super.read(conf);
+        super.json2list(conf, false);
     }
 
     @PrePersist
     @PreUpdate
     public void save(final PGJPAConf conf) {
-        super.save(conf);
+        super.list2json(conf);
+    }
+
+    @PostPersist
+    @PostUpdate
+    public void readAfterSave(final PGJPAConf conf) {
+        super.json2list(conf, true);
     }
 }
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/PGJPAGroup.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/PGJPAGroup.java
index 6207dfc..6f65a43 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/PGJPAGroup.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/PGJPAGroup.java
@@ -75,9 +75,7 @@ public class PGJPAGroup extends JPAGroup implements PGJPAAny<Group>, Group {
 
     @Override
     public boolean remove(final GPlainAttr attr) {
-        return plainAttrList.removeIf(pgattr
-                -> pgattr.getSchemaKey().equals(attr.getSchema().getKey())
-                && attr.getOwner().getKey().equals(getKey()));
+        return plainAttrList.removeIf(pgattr -> pgattr.getSchemaKey().equals(attr.getSchema().getKey()));
     }
 
     @Override
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/PGJPAGroupListener.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/PGJPAGroupListener.java
index b32f471..f8e190f 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/PGJPAGroupListener.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/PGJPAGroupListener.java
@@ -21,6 +21,8 @@ package org.apache.syncope.core.persistence.jpa.entity.group;
 import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.List;
 import javax.persistence.PostLoad;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
 import javax.persistence.PrePersist;
 import javax.persistence.PreUpdate;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -38,12 +40,18 @@ public class PGJPAGroupListener extends PGJPAEntityListener<Group> {
 
     @PostLoad
     public void read(final PGJPAGroup group) {
-        super.read(group);
+        super.json2list(group, false);
     }
 
     @PrePersist
     @PreUpdate
     public void save(final PGJPAGroup group) {
-        super.save(group);
+        super.list2json(group);
+    }
+
+    @PostPersist
+    @PostUpdate
+    public void readAfterSave(final PGJPAGroup group) {
+        super.json2list(group, true);
     }
 }
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/PGJPAUser.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/PGJPAUser.java
index 486ba1c..b3c215d 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/PGJPAUser.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/PGJPAUser.java
@@ -20,12 +20,16 @@ package org.apache.syncope.core.persistence.jpa.entity.user;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.EntityListeners;
 import javax.persistence.Lob;
 import javax.persistence.Table;
 import javax.persistence.Transient;
+import org.apache.syncope.core.persistence.api.entity.Membership;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.PGPlainAttr;
@@ -77,14 +81,34 @@ public class PGJPAUser extends JPAUser implements PGJPAAny<User>, User {
     public boolean remove(final UPlainAttr attr) {
         return plainAttrList.removeIf(pgattr
                 -> pgattr.getSchemaKey().equals(attr.getSchema().getKey())
-                && attr.getOwner().getKey().equals(getKey())
-                && attr.getMembership() == null
-                ? true
-                : pgattr.getMembership() != null && pgattr.getMembershipKey().equals(attr.getMembership().getKey()));
+                && Objects.equals(pgattr.getMembershipKey(), ((PGUPlainAttr) attr).getMembershipKey()));
     }
 
     @Override
     protected List<? extends UPlainAttr> internalGetPlainAttrs() {
         return plainAttrList;
     }
+
+    @Override
+    public Optional<? extends UPlainAttr> getPlainAttr(final String plainSchema) {
+        return plainAttrList.stream().
+                filter(pgattr -> pgattr.getSchemaKey() != null && pgattr.getSchemaKey().equals(plainSchema)
+                && pgattr.getMembershipKey() == null).
+                findFirst();
+    }
+
+    @Override
+    public Optional<? extends UPlainAttr> getPlainAttr(final String plainSchema, final Membership<?> membership) {
+        return plainAttrList.stream().
+                filter(pgattr -> pgattr.getSchemaKey() != null && pgattr.getSchemaKey().equals(plainSchema)
+                && pgattr.getMembershipKey() != null && pgattr.getMembershipKey().equals(membership.getKey())).
+                findFirst();
+    }
+
+    @Override
+    public boolean remove(final UMembership membership) {
+        plainAttrList.removeIf(pgattr
+                -> pgattr.getMembershipKey() != null && pgattr.getMembershipKey().equals(membership.getKey()));
+        return super.remove(membership);
+    }
 }
diff --git a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/PGJPAUserListener.java b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/PGJPAUserListener.java
index 5baf4fc..7ab5d32 100644
--- a/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/PGJPAUserListener.java
+++ b/core/persistence-jpa-pgjsonb/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/PGJPAUserListener.java
@@ -21,6 +21,8 @@ package org.apache.syncope.core.persistence.jpa.entity.user;
 import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.List;
 import javax.persistence.PostLoad;
+import javax.persistence.PostPersist;
+import javax.persistence.PostUpdate;
 import javax.persistence.PrePersist;
 import javax.persistence.PreUpdate;
 import org.apache.syncope.core.persistence.api.entity.user.User;
@@ -38,12 +40,18 @@ public class PGJPAUserListener extends PGJPAEntityListener<User> {
 
     @PostLoad
     public void read(final PGJPAUser user) {
-        super.read(user);
+        super.json2list(user, false);
     }
 
     @PrePersist
     @PreUpdate
     public void save(final PGJPAUser user) {
-        super.save(user);
+        super.list2json(user);
+    }
+
+    @PostPersist
+    @PostUpdate
+    public void readAfterSave(final PGJPAUser user) {
+        super.json2list(user, true);
     }
 }
diff --git a/core/persistence-jpa-pgjsonb/src/main/resources/META-INF/spring-orm-pgjsonb.xml b/core/persistence-jpa-pgjsonb/src/main/resources/META-INF/spring-orm-pgjsonb.xml
index 3e5b103..0118765 100644
--- a/core/persistence-jpa-pgjsonb/src/main/resources/META-INF/spring-orm-pgjsonb.xml
+++ b/core/persistence-jpa-pgjsonb/src/main/resources/META-INF/spring-orm-pgjsonb.xml
@@ -34,4 +34,68 @@ under the License.
     </persistence-unit-defaults>
   </persistence-unit-metadata>
   
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+    <attributes>
+      <many-to-one name="userOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.PGJPAUser"/>
+      <many-to-one name="groupOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.PGJPAGroup"/>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension">
+    <attributes>
+      <many-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.PGJPAGroup"/>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.PGJPAUser">
+        <join-column name="user_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.PGJPAGroup">
+        <join-column name="group_id"/>
+      </many-to-one>        
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership">
+    <attributes>
+      <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.PGJPAGroup"/>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.PGJPAUser">
+        <join-column name="user_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.PGJPAAnyObject">
+        <join-column name="anyObject_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.PGJPAAnyObject">
+        <join-column name="anyObject_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.PGJPAGroup">
+        <join-column name="group_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership">
+    <attributes>
+      <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.PGJPAGroup"/>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.PGJPAAnyObject">
+        <join-column name="left_anyObject_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.PGJPAAnyObject">
+        <join-column name="right_anyObject_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
 </entity-mappings>
diff --git a/core/persistence-jpa-pgjsonb/src/main/resources/persistence.properties b/core/persistence-jpa-pgjsonb/src/main/resources/persistence.properties
index c3196eb..a299527 100644
--- a/core/persistence-jpa-pgjsonb/src/main/resources/persistence.properties
+++ b/core/persistence-jpa-pgjsonb/src/main/resources/persistence.properties
@@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAAnySearchDAO
 user.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAAnyObjectDAO
+conf.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAConfDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/core/persistence-jpa-pgjsonb/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-pgjsonb/src/test/resources/domains/MasterContent.xml
index 32efc96..60834e6 100644
--- a/core/persistence-jpa-pgjsonb/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-pgjsonb/src/test/resources/domains/MasterContent.xml
@@ -18,8 +18,6 @@ specific language governing permissions and limitations
 under the License.
 -->
 <dataset>
-  <SyncopeDomain id="Two" adminCipherAlgorithm="SHA" adminPwd="2AA60A8FF7FCD473D321E0146AFD9E26DF395147"/>  
-  
   <SyncopeSchema id="password.cipher.algorithm"/>
   <PlainSchema id="password.cipher.algorithm" type="String"
                mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
index 0dbc686..e09ff63 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
@@ -26,7 +26,6 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
-import javax.persistence.Entity;
 import javax.validation.ValidationException;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
@@ -56,6 +55,7 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
@@ -145,7 +145,7 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
             AnyTypeKind kind);
 
     protected Pair<PlainSchema, PlainAttrValue> check(final AttributeCond cond, final AnyTypeKind kind) {
-        AnyUtils attrUtils = anyUtilsFactory.getInstance(kind);
+        AnyUtils anyUtils = anyUtilsFactory.getInstance(kind);
 
         PlainSchema schema = schemaDAO.find(cond.getSchema());
         if (schema == null) {
@@ -153,7 +153,9 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
             throw new IllegalArgumentException();
         }
 
-        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
+        PlainAttrValue attrValue = schema.isUniqueConstraint()
+                ? anyUtils.newPlainAttrUniqueValue()
+                : anyUtils.newPlainAttrValue();
         try {
             if (cond.getType() != AttributeCond.Type.LIKE
                     && cond.getType() != AttributeCond.Type.ILIKE
@@ -212,7 +214,7 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
         }
 
         // Deal with any fields representing relationships to other entities
-        if (anyField.getType().getAnnotation(Entity.class) != null) {
+        if (Entity.class.isAssignableFrom(anyField.getType())) {
             Method relMethod = null;
             try {
                 relMethod = ClassUtils.getPublicMethod(anyField.getType(), "getKey", new Class<?>[0]);
@@ -310,7 +312,7 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
         raw.stream().map(anyKey -> anyKey instanceof Object[]
                 ? (String) ((Object[]) anyKey)[0]
                 : ((String) anyKey)).
-                forEachOrdered((actualKey) -> {
+                forEachOrdered(actualKey -> {
                     @SuppressWarnings("unchecked")
                     T any = kind == AnyTypeKind.USER
                             ? (T) userDAO.find(actualKey)
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfDAO.java
index ed065fe..2bdb045 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfDAO.java
@@ -26,10 +26,8 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.conf.Conf;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
-@Repository
 public class JPAConfDAO extends AbstractDAO<Conf> implements ConfDAO {
 
     private static final String KEY = "cd64d66f-6fff-4008-b966-a06b1cc1436d";
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 33bf9f7..e86905f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -362,6 +362,9 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                 propagateSuspension |= policy.isPropagateSuspension();
             }
         } catch (Exception e) {
+            if (e instanceof InvalidEntityException) {
+                throw (InvalidEntityException) e;
+            }
             LOG.error("Invalid username for {}", user, e);
             throw new InvalidEntityException(User.class, EntityViolationType.InvalidUsername, e.getMessage());
         }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAADynGroupMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAADynGroupMembership.java
index b1d55ff..91fc4cb 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAADynGroupMembership.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAADynGroupMembership.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.persistence.jpa.entity.anyobject;
 
 import javax.persistence.Entity;
 import javax.persistence.ManyToOne;
-import javax.persistence.OneToOne;
 import javax.persistence.Table;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
@@ -38,8 +37,7 @@ public class JPAADynGroupMembership extends AbstractDynMembership<AnyObject> imp
 
     public static final String TABLE = "ADynGroupMembership";
 
-    @OneToOne
-    private JPAGroup group;
+    private Group group;
 
     @ManyToOne
     private JPAAnyType anyType;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAMembership.java
index 1561dae..24992db 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAMembership.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAMembership.java
@@ -18,9 +18,7 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.anyobject;
 
-import javax.persistence.Column;
 import javax.persistence.Entity;
-import javax.persistence.ManyToOne;
 import javax.persistence.Table;
 import org.apache.syncope.core.persistence.api.entity.MembershipType;
 import org.apache.syncope.core.persistence.api.entity.RelationshipType;
@@ -38,13 +36,9 @@ public class JPAAMembership extends AbstractGeneratedKeyEntity implements AMembe
 
     public static final String TABLE = "AMembership";
 
-    @ManyToOne
-    @Column(name = "anyObject_id")
-    private JPAAnyObject leftEnd;
+    private AnyObject leftEnd;
 
-    @ManyToOne
-    @Column(name = "group_id")
-    private JPAGroup rightEnd;
+    private Group rightEnd;
 
     @Override
     public MembershipType getType() {
@@ -68,7 +62,7 @@ public class JPAAMembership extends AbstractGeneratedKeyEntity implements AMembe
     }
 
     @Override
-    public JPAGroup getRightEnd() {
+    public Group getRightEnd() {
         return rightEnd;
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAARelationship.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAARelationship.java
index b488538..c926a60 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAARelationship.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAARelationship.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.anyobject;
 
-import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.ManyToOne;
@@ -43,13 +42,9 @@ public class JPAARelationship extends AbstractGeneratedKeyEntity implements ARel
     @ManyToOne(fetch = FetchType.EAGER, optional = false)
     private JPARelationshipType type;
 
-    @ManyToOne
-    @Column(name = "left_anyObject_id")
-    private JPAAnyObject leftEnd;
+    private AnyObject leftEnd;
 
-    @ManyToOne
-    @Column(name = "right_anyObject_id")
-    private JPAAnyObject rightEnd;
+    private AnyObject rightEnd;
 
     @Override
     public RelationshipType getType() {
@@ -86,5 +81,4 @@ public class JPAARelationship extends AbstractGeneratedKeyEntity implements ARel
         checkType(rightEnd, JPAAnyObject.class);
         this.rightEnd = (JPAAnyObject) rightEnd;
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
index d10cb9a..7c6724a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
@@ -29,7 +29,6 @@ import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
-import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
@@ -68,11 +67,9 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     @NotNull
     private String name;
 
-    @ManyToOne
-    private JPAUser userOwner;
+    protected User userOwner;
 
-    @ManyToOne
-    private JPAGroup groupOwner;
+    protected Group groupOwner;
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "owner")
     @Valid
@@ -145,7 +142,7 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     }
 
     @Override
-    public JPAGroup getGroupOwner() {
+    public Group getGroupOwner() {
         return groupOwner;
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java
index 9ddf68a..648436e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java
@@ -45,8 +45,7 @@ public class JPATypeExtension extends AbstractGeneratedKeyEntity implements Type
 
     public static final String TABLE = "TypeExtension";
 
-    @ManyToOne
-    private JPAGroup group;
+    private Group group;
 
     @ManyToOne
     private JPAAnyType anyType;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUDynGroupMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUDynGroupMembership.java
index 875b572..5e3a728 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUDynGroupMembership.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUDynGroupMembership.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.persistence.jpa.entity.user;
 
 import javax.persistence.Entity;
-import javax.persistence.OneToOne;
 import javax.persistence.Table;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership;
@@ -35,8 +34,7 @@ public class JPAUDynGroupMembership extends AbstractDynMembership<User> implemen
 
     public static final String TABLE = "UDynGroupMembership";
 
-    @OneToOne
-    private JPAGroup group;
+    private Group group;
 
     @Override
     public Group getGroup() {
@@ -44,9 +42,8 @@ public class JPAUDynGroupMembership extends AbstractDynMembership<User> implemen
     }
 
     @Override
-    public void setGroup(final Group role) {
-        checkType(role, JPAGroup.class);
-        this.group = (JPAGroup) role;
+    public void setGroup(final Group group) {
+        checkType(group, JPAGroup.class);
+        this.group = (JPAGroup) group;
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUMembership.java
index 866537e..a9d39e9 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUMembership.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUMembership.java
@@ -18,9 +18,7 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.user;
 
-import javax.persistence.Column;
 import javax.persistence.Entity;
-import javax.persistence.ManyToOne;
 import javax.persistence.Table;
 import org.apache.syncope.core.persistence.api.entity.MembershipType;
 import org.apache.syncope.core.persistence.api.entity.RelationshipType;
@@ -38,13 +36,9 @@ public class JPAUMembership extends AbstractGeneratedKeyEntity implements UMembe
 
     public static final String TABLE = "UMembership";
 
-    @ManyToOne
-    @Column(name = "user_id")
-    private JPAUser leftEnd;
+    private User leftEnd;
 
-    @ManyToOne
-    @Column(name = "group_id")
-    private JPAGroup rightEnd;
+    private Group rightEnd;
 
     @Override
     public MembershipType getType() {
@@ -57,7 +51,7 @@ public class JPAUMembership extends AbstractGeneratedKeyEntity implements UMembe
     }
 
     @Override
-    public JPAUser getLeftEnd() {
+    public User getLeftEnd() {
         return leftEnd;
     }
 
@@ -68,7 +62,7 @@ public class JPAUMembership extends AbstractGeneratedKeyEntity implements UMembe
     }
 
     @Override
-    public JPAGroup getRightEnd() {
+    public Group getRightEnd() {
         return rightEnd;
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAURelationship.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAURelationship.java
index 931b4e6..38c5c73 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAURelationship.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAURelationship.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.user;
 
-import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.ManyToOne;
@@ -45,13 +44,9 @@ public class JPAURelationship extends AbstractGeneratedKeyEntity implements URel
     @ManyToOne(fetch = FetchType.EAGER, optional = false)
     private JPARelationshipType type;
 
-    @ManyToOne
-    @Column(name = "user_id")
-    private JPAUser leftEnd;
+    private User leftEnd;
 
-    @ManyToOne
-    @Column(name = "anyObject_id")
-    private JPAAnyObject rightEnd;
+    private AnyObject rightEnd;
 
     @Override
     public RelationshipType getType() {
@@ -68,7 +63,7 @@ public class JPAURelationship extends AbstractGeneratedKeyEntity implements URel
     }
 
     @Override
-    public JPAUser getLeftEnd() {
+    public User getLeftEnd() {
         return leftEnd;
     }
 
@@ -88,5 +83,4 @@ public class JPAURelationship extends AbstractGeneratedKeyEntity implements URel
         checkType(rightEnd, JPAAnyObject.class);
         this.rightEnd = (JPAAnyObject) rightEnd;
     }
-
 }
diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
index 0ac2579..bfdc77a 100644
--- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
+++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
@@ -33,6 +33,71 @@ under the License.
       </entity-listeners>
     </persistence-unit-defaults>
   </persistence-unit-metadata>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+    <attributes>
+      <many-to-one name="userOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser"/>
+      <many-to-one name="groupOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension">
+    <attributes>
+      <many-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser">
+        <join-column name="user_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+        <join-column name="group_id"/>
+      </many-to-one>        
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership">
+    <attributes>
+      <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser">
+        <join-column name="user_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="anyObject_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="anyObject_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+        <join-column name="group_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership">
+    <attributes>
+      <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="left_anyObject_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="right_anyObject_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
   
   <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrUniqueValue">
     <table>
diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
index 8c52f2e..5880423 100644
--- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
+++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
@@ -34,6 +34,71 @@ under the License.
     </persistence-unit-defaults>
   </persistence-unit-metadata>
 
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+    <attributes>
+      <many-to-one name="userOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser"/>
+      <many-to-one name="groupOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension">
+    <attributes>
+      <many-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser">
+        <join-column name="user_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+        <join-column name="group_id"/>
+      </many-to-one>        
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership">
+    <attributes>
+      <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser">
+        <join-column name="user_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="anyObject_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="anyObject_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+        <join-column name="group_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership">
+    <attributes>
+      <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="left_anyObject_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="right_anyObject_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+
   <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrUniqueValue">
     <table>
       <unique-constraint>
diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
index aaf42b3..c477773 100644
--- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
+++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
@@ -34,6 +34,71 @@ under the License.
     </persistence-unit-defaults>
   </persistence-unit-metadata>
 
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+    <attributes>
+      <many-to-one name="userOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser"/>
+      <many-to-one name="groupOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension">
+    <attributes>
+      <many-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser">
+        <join-column name="user_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+        <join-column name="group_id"/>
+      </many-to-one>        
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership">
+    <attributes>
+      <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAUser">
+        <join-column name="user_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="anyObject_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="anyObject_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+        <join-column name="group_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership">
+    <attributes>
+      <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup"/>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship">
+    <attributes>
+      <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="left_anyObject_id"/>
+      </many-to-one>
+      <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject">
+        <join-column name="right_anyObject_id"/>
+      </many-to-one>
+    </attributes>
+  </entity>
+
   <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrUniqueValue">
     <table>
       <unique-constraint>
diff --git a/core/persistence-jpa/src/main/resources/persistence.properties b/core/persistence-jpa/src/main/resources/persistence.properties
index 0841aa0..1ce4a2b 100644
--- a/core/persistence-jpa/src/main/resources/persistence.properties
+++ b/core/persistence-jpa/src/main/resources/persistence.properties
@@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
+conf.dao=org.apache.syncope.core.persistence.jpa.dao.JPAConfDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/core/persistence-jpa/src/main/resources/persistenceContext.xml b/core/persistence-jpa/src/main/resources/persistenceContext.xml
index c7c54c2..577ab78 100644
--- a/core/persistence-jpa/src/main/resources/persistenceContext.xml
+++ b/core/persistence-jpa/src/main/resources/persistenceContext.xml
@@ -38,6 +38,7 @@ under the License.
   <bean class="${user.dao}"/>
   <bean class="${group.dao}"/>
   <bean class="${anyObject.dao}"/>
+  <bean class="${conf.dao}"/>
 
   <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
 
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/DomainTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/DomainTest.java
index 468c8e5..52b421e 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/DomainTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/DomainTest.java
@@ -29,6 +29,7 @@ import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.persistence.api.dao.DomainDAO;
 import org.apache.syncope.core.persistence.api.entity.Domain;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
@@ -39,6 +40,7 @@ public class DomainTest extends AbstractTest {
     @Autowired
     private DomainDAO domainDAO;
 
+    @Tag("multitenancy")
     @Test
     public void find() {
         Domain two = domainDAO.find("Two");
@@ -48,14 +50,15 @@ public class DomainTest extends AbstractTest {
         assertNull(domainDAO.find("none"));
     }
 
+    @Tag("multitenancy")
     @Test
     public void findAll() {
         List<Domain> list = domainDAO.findAll();
         assertNotNull(list);
         assertFalse(list.isEmpty());
-        for (Domain domain : list) {
+        list.forEach(domain -> {
             assertNotNull(domain);
-        }
+        });
     }
 
     @Test
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index f772503..e4ec82f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@ -468,6 +468,8 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
             throw scce;
         }
 
+        // Re-merge any pending change from above
+        anyObjectDAO.save(anyObject);
         return propByRes;
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
index f207ae6..fbe700d 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
@@ -86,7 +86,7 @@ public class ConfigurationDataBinderImpl extends AbstractAnyDataBinder implement
             }
         }
 
-        for (String value : valuesProvided) {
+        valuesProvided.forEach(value -> {
             if (value == null || value.isEmpty()) {
                 LOG.debug("Null value for {}, ignoring", schema.getKey());
             } else {
@@ -106,7 +106,7 @@ public class ConfigurationDataBinderImpl extends AbstractAnyDataBinder implement
                     invalidValues.getElements().add(schema.getKey() + ": " + value + " - " + e.getMessage());
                 }
             }
-        }
+        });
     }
 
     @Override
@@ -127,5 +127,4 @@ public class ConfigurationDataBinderImpl extends AbstractAnyDataBinder implement
             return attr;
         }
     }
-
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
index e2ec7ee..f18470a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
@@ -305,6 +305,8 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
             throw scce;
         }
 
+        // Re-merge any pending change from above
+        groupDAO.save(group);
         return propByRes;
     }
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 055f7aa..3ff0f2e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -554,6 +554,8 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
             throw scce;
         }
 
+        // Re-merge any pending change from above
+        userDAO.save(user);
         return propByRes;
     }
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java
index b2eaebb..fc3c9e9 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java
@@ -38,7 +38,9 @@ import org.apache.commons.jexl3.JexlExpression;
 import org.apache.commons.jexl3.JxltEngine;
 import org.apache.commons.jexl3.MapContext;
 import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.RealmTO;
@@ -61,9 +63,9 @@ public final class JexlUtils {
 
     private static final String[] IGNORE_FIELDS = { "password", "clearPassword", "serialVersionUID", "class" };
 
-    private static final Map<Class<?>, Set<PropertyDescriptor>> FIELD_CACHE =
-            Collections.<Class<?>, Set<PropertyDescriptor>>synchronizedMap(
-                    new HashMap<Class<?>, Set<PropertyDescriptor>>());
+    private static final Map<Class<?>, Set<Pair<PropertyDescriptor, Field>>> FIELD_CACHE =
+            Collections.<Class<?>, Set<Pair<PropertyDescriptor, Field>>>synchronizedMap(
+                    new HashMap<Class<?>, Set<Pair<PropertyDescriptor, Field>>>());
 
     private static JexlEngine JEXL_ENGINE;
 
@@ -122,38 +124,52 @@ public final class JexlUtils {
     }
 
     public static void addFieldsToContext(final Object object, final JexlContext jexlContext) {
-        Set<PropertyDescriptor> cached = FIELD_CACHE.get(object.getClass());
+        Set<Pair<PropertyDescriptor, Field>> cached = FIELD_CACHE.get(object.getClass());
         if (cached == null) {
-            cached = new HashSet<>();
-            FIELD_CACHE.put(object.getClass(), cached);
-
-            try {
-                for (PropertyDescriptor desc : Introspector.getBeanInfo(object.getClass()).getPropertyDescriptors()) {
-                    if ((!desc.getName().startsWith("pc"))
-                            && (!ArrayUtils.contains(IGNORE_FIELDS, desc.getName()))
-                            && (!Iterable.class.isAssignableFrom(desc.getPropertyType()))
-                            && (!desc.getPropertyType().isArray())) {
-
-                        cached.add(desc);
+            FIELD_CACHE.put(object.getClass(), new HashSet<>());
+
+            List<Class<?>> classes = ClassUtils.getAllSuperclasses(object.getClass());
+            classes.add(object.getClass());
+            classes.forEach(clazz -> {
+                try {
+                    for (PropertyDescriptor desc : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) {
+                        if (!desc.getName().startsWith("pc")
+                                && !ArrayUtils.contains(IGNORE_FIELDS, desc.getName())
+                                && !Collection.class.isAssignableFrom(desc.getPropertyType())
+                                && !Map.class.isAssignableFrom(desc.getPropertyType())
+                                && !desc.getPropertyType().isArray()) {
+
+                            Field field = null;
+                            try {
+                                field = clazz.getDeclaredField(desc.getName());
+                            } catch (NoSuchFieldException | SecurityException e) {
+                                LOG.debug("Could not get field {} from {}", desc.getName(), clazz.getName(), e);
+                            }
+
+                            FIELD_CACHE.get(object.getClass()).add(Pair.of(desc, field));
+                        }
                     }
+                } catch (IntrospectionException e) {
+                    LOG.warn("Could not introspect {}", clazz.getName(), e);
                 }
-            } catch (IntrospectionException ie) {
-                LOG.error("Reading class attributes error", ie);
-            }
+            });
+
+            cached = FIELD_CACHE.get(object.getClass());
         }
 
-        for (PropertyDescriptor desc : cached) {
-            String fieldName = desc.getName();
-            Class<?> fieldType = desc.getPropertyType();
+        cached.forEach(fd -> {
+            String fieldName = fd.getLeft().getName();
+            Class<?> fieldType = fd.getLeft().getPropertyType();
 
             try {
-                Object fieldValue;
-                if (desc.getReadMethod() == null) {
-                    final Field field = object.getClass().getDeclaredField(fieldName);
-                    field.setAccessible(true);
-                    fieldValue = field.get(object);
+                Object fieldValue = null;
+                if (fd.getLeft().getReadMethod() == null) {
+                    if (fd.getRight() != null) {
+                        fd.getRight().setAccessible(true);
+                        fieldValue = fd.getRight().get(object);
+                    }
                 } else {
-                    fieldValue = desc.getReadMethod().invoke(object);
+                    fieldValue = fd.getLeft().getReadMethod().invoke(object);
                 }
                 fieldValue = fieldValue == null
                         ? StringUtils.EMPTY
@@ -167,7 +183,7 @@ public final class JexlUtils {
             } catch (Exception iae) {
                 LOG.error("Reading '{}' value error", fieldName, iae);
             }
-        }
+        });
 
         if (object instanceof Any && ((Any<?>) object).getRealm() != null) {
             jexlContext.set("realm", ((Any<?>) object).getRealm().getFullPath());
@@ -181,23 +197,21 @@ public final class JexlUtils {
     }
 
     public static void addAttrTOsToContext(final Collection<AttrTO> attrs, final JexlContext jexlContext) {
-        for (AttrTO attr : attrs) {
-            if (attr.getSchema() != null) {
-                String expressionValue = attr.getValues().isEmpty()
-                        ? StringUtils.EMPTY
-                        : attr.getValues().get(0);
+        attrs.stream().filter(attr -> attr.getSchema() != null).forEach(attr -> {
+            String expressionValue = attr.getValues().isEmpty()
+                    ? StringUtils.EMPTY
+                    : attr.getValues().get(0);
 
-                LOG.debug("Add attribute {} with value {}", attr.getSchema(), expressionValue);
+            LOG.debug("Add attribute {} with value {}", attr.getSchema(), expressionValue);
 
-                jexlContext.set(attr.getSchema(), expressionValue);
-            }
-        }
+            jexlContext.set(attr.getSchema(), expressionValue);
+        });
     }
 
     public static void addPlainAttrsToContext(
             final Collection<? extends PlainAttr<?>> attrs, final JexlContext jexlContext) {
 
-        attrs.stream().filter(attr -> attr.getSchema() != null).forEachOrdered((attr) -> {
+        attrs.stream().filter(attr -> attr.getSchema() != null).forEach(attr -> {
             List<String> attrValues = attr.getValuesAsStrings();
             String expressionValue = attrValues.isEmpty()
                     ? StringUtils.EMPTY
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
index db834e3..acf5c3b 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
@@ -24,6 +24,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 import javax.persistence.EntityExistsException;
 import javax.persistence.PersistenceException;
 import javax.persistence.RollbackException;
@@ -75,13 +76,15 @@ public class RestServiceExceptionMapper implements ExceptionMapper<Exception> {
     @Autowired
     private Environment env;
 
+    private static final String UNIQUE_MSG_KEY = "UniqueConstraintViolation";
+
     private static final Map<String, String> EXCEPTION_CODE_MAP = new HashMap<String, String>() {
 
         private static final long serialVersionUID = -7688359318035249200L;
 
         {
-            put("23000", "UniqueConstraintViolation");
-            put("23505", "UniqueConstraintViolation");
+            put("23000", UNIQUE_MSG_KEY);
+            put("23505", UNIQUE_MSG_KEY);
         }
     };
 
@@ -107,9 +110,9 @@ public class RestServiceExceptionMapper implements ExceptionMapper<Exception> {
                 || ex instanceof PersistenceException && ex.getCause() instanceof EntityExistsException) {
 
             builder = builder(ClientExceptionType.EntityExists,
-                    getJPAMessage(ex instanceof PersistenceException ? ex.getCause() : ex));
+                    getPersistenceErrorMessage(ex instanceof PersistenceException ? ex.getCause() : ex));
         } else if (ex instanceof DataIntegrityViolationException || ex instanceof JpaSystemException) {
-            builder = builder(ClientExceptionType.DataIntegrityViolation, getJPAMessage(ex));
+            builder = builder(ClientExceptionType.DataIntegrityViolation, getPersistenceErrorMessage(ex));
         } else if (ex instanceof ConnectorException) {
             builder = builder(ClientExceptionType.ConnectorException, ExceptionUtils.getRootCauseMessage(ex));
         } else if (ex instanceof NotFoundException) {
@@ -159,10 +162,10 @@ public class RestServiceExceptionMapper implements ExceptionMapper<Exception> {
         error.setStatus(ex.getType().getResponseStatus().getStatusCode());
         error.setType(ex.getType());
 
-        for (String element : ex.getElements()) {
+        ex.getElements().forEach(element -> {
             builder.header(RESTHeaders.ERROR_INFO, ex.getType().getInfoHeaderValue(element));
             error.getElements().add(element);
-        }
+        });
 
         return builder.entity(error);
     }
@@ -175,20 +178,20 @@ public class RestServiceExceptionMapper implements ExceptionMapper<Exception> {
         ResponseBuilder builder = Response.status(Response.Status.BAD_REQUEST);
 
         List<ErrorTO> errors = new ArrayList<>();
-        for (SyncopeClientException sce : ex.getExceptions()) {
+        ex.getExceptions().stream().map(sce -> {
             builder.header(RESTHeaders.ERROR_CODE, sce.getType().name());
 
             ErrorTO error = new ErrorTO();
             error.setStatus(sce.getType().getResponseStatus().getStatusCode());
             error.setType(sce.getType());
 
-            for (String element : sce.getElements()) {
+            sce.getElements().forEach(element -> {
                 builder.header(RESTHeaders.ERROR_INFO, sce.getType().getInfoHeaderValue(element));
                 error.getElements().add(element);
-            }
+            });
 
-            errors.add(error);
-        }
+            return error;
+        }).collect(Collectors.toList());
 
         return builder.entity(errors);
     }
@@ -292,25 +295,28 @@ public class RestServiceExceptionMapper implements ExceptionMapper<Exception> {
     private ResponseBuilder builder(final Response response) {
         ResponseBuilder builder = JAXRSUtils.toResponseBuilder(response.getStatus());
         builder.entity(response.getEntity());
-        for (Map.Entry<String, List<Object>> entry : response.getMetadata().entrySet()) {
-            if (!HttpHeaders.CONTENT_TYPE.equals(entry.getKey())) {
-                for (Object value : entry.getValue()) {
-                    builder.header(entry.getKey(), value);
-                }
+        response.getMetadata().forEach((key, value) -> {
+            if (!HttpHeaders.CONTENT_TYPE.equals(key)) {
+                value.forEach(headerValue -> {
+                    builder.header(key, headerValue);
+                });
             }
-        }
+        });
 
         return builder;
     }
 
-    private String getJPAMessage(final Throwable ex) {
+    private String getPersistenceErrorMessage(final Throwable ex) {
         Throwable throwable = ExceptionUtils.getRootCause(ex);
+
         String message = null;
         if (throwable instanceof SQLException) {
             String messageKey = EXCEPTION_CODE_MAP.get(((SQLException) throwable).getSQLState());
             if (messageKey != null) {
                 message = env.getProperty("errMessage." + messageKey);
             }
+        } else if (throwable instanceof EntityExistsException || throwable instanceof DuplicateException) {
+            message = env.getProperty("errMessage." + UNIQUE_MSG_KEY);
         }
 
         return message == null
diff --git a/docker/core/src/main/resources/persistence.properties b/docker/core/src/main/resources/persistence.properties
index b656eb2..b4e2c81 100644
--- a/docker/core/src/main/resources/persistence.properties
+++ b/docker/core/src/main/resources/persistence.properties
@@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
+conf.dao=org.apache.syncope.core.persistence.jpa.dao.JPAConfDAO
 openjpa.RemoteCommitProvider=${OPENJPA_REMOTE_COMMIT}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-ha.yml b/docker/src/main/resources/docker-compose/docker-compose-ha.yml
index 0eb26f5..8f9aa0c 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-ha.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-ha.yml
@@ -22,18 +22,12 @@ version: '3.3'
 
 services:
    db:
-     image: crunchydata/crunchy-postgres:centos7-10.3-1.8.2
+     image: postgres:latest
      restart: always
      environment:
-       PG_ROOT_PASSWORD: postgres
-       PG_MODE: primary
-       PG_PRIMARY_USER: postgres
-       PG_PRIMARY_PASSWORD: postgres
-       PG_PRIMARY_HOST: localhost
-       PG_PRIMARY_PORT: 5432
-       PG_DATABASE: syncope
-       PG_USER: syncope
-       PG_PASSWORD: syncope
+       POSTGRES_DB: syncope
+       POSTGRES_USER: syncope
+       POSTGRES_PASSWORD: syncope
 
    syncope1:
      depends_on:
diff --git a/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml b/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml
index 0c42ede..9eb7ba7 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml
@@ -21,18 +21,12 @@ version: '3.3'
 
 services:
    db:
-     image: crunchydata/crunchy-postgres:centos7-10.5-2.1.0
+     image: postgres:latest
      restart: always
      environment:
-       PG_ROOT_PASSWORD: postgres
-       PG_MODE: primary
-       PG_PRIMARY_USER: postgres
-       PG_PRIMARY_PASSWORD: postgres
-       PG_PRIMARY_HOST: localhost
-       PG_PRIMARY_PORT: 5432
-       PG_DATABASE: syncope
-       PG_USER: syncope
-       PG_PASSWORD: syncope
+       POSTGRES_DB: syncope
+       POSTGRES_USER: syncope
+       POSTGRES_PASSWORD: syncope
 
    syncope:
      depends_on:
diff --git a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
index 194c758..dd4b730 100644
--- a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
+++ b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
@@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearc
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
+conf.dao=org.apache.syncope.core.persistence.jpa.dao.JPAConfDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 6748264..754640c 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -591,6 +591,157 @@ under the License.
     </profile>
   
     <profile>
+      <id>pgjsonb-it</id>
+
+      <properties>
+        <jdbcdriver.groupId>org.postgresql</jdbcdriver.groupId>
+        <jdbcdriver.artifactId>postgresql</jdbcdriver.artifactId>
+      </properties>
+
+      <dependencies>
+        <dependency>
+          <groupId>org.apache.syncope.core</groupId>
+          <artifactId>syncope-core-persistence-jpa-pgjsonb</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+    
+        <dependency>
+          <groupId>org.postgresql</groupId>
+          <artifactId>postgresql</artifactId>
+          <version>${jdbc.postgresql.version}</version>
+          <scope>test</scope>
+        </dependency>
+      </dependencies>
+
+      <build>
+        <defaultGoal>clean verify</defaultGoal>
+
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-war-plugin</artifactId>
+            <configuration>
+              <packagingExcludes>WEB-INF/classes/domains/Two*</packagingExcludes>
+            </configuration>
+          </plugin>
+
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <inherited>true</inherited>
+            <executions>
+              <execution>
+                <id>remove-domain-Two</id>
+                <phase>prepare-package</phase>
+                <configuration>
+                  <target>
+                    <delete>
+                      <fileset dir="${project.build.directory}/classes/domains" includes="Two*"/>
+                    </delete>
+                  </target>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+
+          <plugin>
+            <groupId>io.fabric8</groupId>
+            <artifactId>docker-maven-plugin</artifactId>
+            <configuration>
+              <images>
+                <image>
+                  <name>postgres:${docker.postgresql.version}</name>
+                  <run>
+                    <env>
+                      <POSTGRES_DB>syncope</POSTGRES_DB>
+                      <POSTGRES_USER>syncope</POSTGRES_USER>
+                      <POSTGRES_PASSWORD>syncope</POSTGRES_PASSWORD>
+                    </env>
+                    <ports>
+                      <port>5432:5432</port>
+                    </ports>
+                  </run>
+                </image>
+              </images>
+            </configuration>
+            <executions>
+              <execution>
+                <id>start-postgres</id>
+                <phase>pre-integration-test</phase>
+                <goals>
+                  <goal>start</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>stop-postgres</id>
+                <phase>post-integration-test</phase>
+                <goals>
+                  <goal>stop</goal>
+                  <goal>remove</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+
+          <plugin>
+            <groupId>org.codehaus.cargo</groupId>
+            <artifactId>cargo-maven2-plugin</artifactId>
+            <inherited>true</inherited>
+            <executions>
+              <execution>
+                <id>start-container</id>
+                <phase>pre-integration-test</phase>
+                <goals>
+                  <goal>start</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>stop-container</id>
+                <phase>post-integration-test</phase>
+                <goals>
+                  <goal>stop</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+
+        <resources>
+          <resource>
+            <directory>src/main/resources</directory>
+            <filtering>true</filtering>
+            <excludes>
+              <exclude>provisioning.properties</exclude>
+              <exclude>indexes.xml</exclude>
+              <exclude>views.xml</exclude>
+            </excludes>
+          </resource>
+          <resource>
+            <directory>src/main/resources/pgjsonb</directory>
+            <filtering>true</filtering>
+          </resource>
+          <resource>
+            <directory>${basedir}/../../core/persistence-jpa-pgjsonb/src/test/resources/domains</directory>
+            <targetPath>${project.build.directory}/classes/domains</targetPath>
+            <filtering>true</filtering>
+          </resource>
+        </resources>
+        <testResources>
+          <testResource>
+            <directory>src/main/resources/pgjsonb</directory>
+            <includes>
+              <include>indexes.xml</include>
+              <include>views.xml</include>
+            </includes>            
+          </testResource>
+        </testResources>
+      </build>
+    </profile>
+
+    <profile>
       <id>postgres-it</id>
 
       <properties>
@@ -617,18 +768,12 @@ under the License.
             <configuration>
               <images>
                 <image>
-                  <name>crunchydata/crunchy-postgres:centos7-10.5-2.1.0</name>
+                  <name>postgres:${docker.postgresql.version}</name>
                   <run>
                     <env>
-                      <PG_ROOT_PASSWORD>postgres</PG_ROOT_PASSWORD>
-                      <PG_MODE>primary</PG_MODE>
-                      <PG_PRIMARY_USER>postgres</PG_PRIMARY_USER>
-                      <PG_PRIMARY_PASSWORD>postgres</PG_PRIMARY_PASSWORD>
-                      <PG_PRIMARY_HOST>localhost</PG_PRIMARY_HOST>
-                      <PG_PRIMARY_PORT>5432</PG_PRIMARY_PORT>
-                      <PG_DATABASE>syncope</PG_DATABASE>
-                      <PG_USER>syncope</PG_USER>
-                      <PG_PASSWORD>syncope</PG_PASSWORD>
+                      <POSTGRES_DB>syncope</POSTGRES_DB>
+                      <POSTGRES_USER>syncope</POSTGRES_USER>
+                      <POSTGRES_PASSWORD>syncope</POSTGRES_PASSWORD>
                     </env>
                     <ports>
                       <port>5432:5432</port>
diff --git a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
index 194c758..dd4b730 100644
--- a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
+++ b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
@@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearc
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
+conf.dao=org.apache.syncope.core.persistence.jpa.dao.JPAConfDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/core/persistence-jpa/src/main/resources/persistence.properties b/fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties
similarity index 54%
copy from core/persistence-jpa/src/main/resources/persistence.properties
copy to fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties
index 0841aa0..d9a648f 100644
--- a/core/persistence-jpa/src/main/resources/persistence.properties
+++ b/fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties
@@ -14,13 +14,15 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-content.directory=${conf.directory}
-entity.factory=org.apache.syncope.core.persistence.jpa.entity.JPAEntityFactory
-plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainSchemaDAO
-plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrDAO
-plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO
-any.search.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO
-user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
-group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
-anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
-openjpa.RemoteCommitProvider=sjvm
+Master.driverClassName=org.postgresql.Driver
+Master.url=jdbc:postgresql://localhost:5432/syncope?stringtype=unspecified
+Master.schema=
+Master.username=syncope
+Master.password=syncope
+Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
+Master.orm=META-INF/spring-orm-pgjsonb.xml
+
+Master.pool.maxActive=10
+Master.pool.minIdle=2
+
+Master.audit.sql=audit.sql
diff --git a/fit/core-reference/src/main/resources/pgjsonb/indexes.xml b/fit/core-reference/src/main/resources/pgjsonb/indexes.xml
new file mode 100644
index 0000000..3e5b92b
--- /dev/null
+++ b/fit/core-reference/src/main/resources/pgjsonb/indexes.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+  <comment>Additional indexes (in respect to JPA's)</comment>
+
+  <entry key="SyncopeUser_plainAttrs_idx">CREATE INDEX SyncopeUser_plainAttrs_idx ON SyncopeUser USING gin ((plainAttrs) jsonb_path_ops)</entry>
+  <entry key="SyncopeGroup_plainAttrs_idx">CREATE INDEX SyncopeGroup_plainAttrs_idx ON SyncopeGroup USING gin ((plainAttrs) jsonb_path_ops)</entry>
+  <entry key="AnyObject_plainAttrs_idx">CREATE INDEX AnyObject_plainAttrs_idx ON AnyObject USING gin ((plainAttrs) jsonb_path_ops)</entry>
+  <entry key="SyncopeConf_plainAttrs_idx">CREATE INDEX SyncopeConf_plainAttrs_idx ON SyncopeConf USING gin ((plainAttrs) jsonb_path_ops)</entry>
+
+  <entry key="UDynGroupMembers_any_id">CREATE INDEX UDynGroupMembers_any_id ON UDynGroupMembers(any_id)</entry>
+  <entry key="UDynGroupMembers_group_id">CREATE INDEX UDynGroupMembers_group_id ON UDynGroupMembers(group_id)</entry>
+  <entry key="ADynGroupMembers_any_id">CREATE INDEX ADynGroupMembers_any_id ON ADynGroupMembers(any_id)</entry>
+  <entry key="ADynGroupMembers_group_id">CREATE INDEX ADynGroupMembers_group_id ON ADynGroupMembers(group_id)</entry>
+
+  <entry key="DynRoleMembers_any_id">CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id)</entry>
+  <entry key="DynRoleMembers_role_id">CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id)</entry>
+
+  <entry key="DynRealmMembers_any_id">CREATE INDEX DynRealmMembers_any_id ON DynRealmMembers(any_id)</entry>
+  <entry key="DynRealmMembers_realm_id">CREATE INDEX DynRealmMembers_dynRealm_id ON DynRealmMembers(dynRealm_id)</entry>
+
+  <entry key="CPlainAttrValue_stringvalueIndex">CREATE INDEX CAttrValue_stringvalueIndex ON CPlainAttrValue(stringvalue)</entry>
+  <entry key="CPlainAttrValue_datevalueIndex">CREATE INDEX CAttrValue_datevalueIndex ON CPlainAttrValue(datevalue)</entry>
+  <entry key="CPlainAttrValue_longvalueIndex">CREATE INDEX CAttrValue_longvalueIndex ON CPlainAttrValue(longvalue)</entry>
+  <entry key="CPlainAttrValue_doublevalueIndex">CREATE INDEX CAttrValue_doublevalueIndex ON CPlainAttrValue(doublevalue)</entry>
+  <entry key="CPlainAttrValue_booleanvalueIndex">CREATE INDEX CAttrValue_booleanvalueIndex ON CPlainAttrValue(booleanvalue)</entry>
+
+  <entry key="UMembership_GroupIndex">CREATE INDEX UMembership_GroupIndex ON UMembership(group_id)</entry>
+  <entry key="UMembership_UserIndex">CREATE INDEX UMembership_UserIndex ON UMembership(user_id)</entry>
+  <entry key="AMembership_GroupIndex">CREATE INDEX AMembership_GroupIndex ON AMembership(group_id)</entry>
+  <entry key="AMembership_AnyObjectIndex">CREATE INDEX AMembership_AnyObjectIndex ON AMembership(anyObject_id)</entry>
+
+  <entry key="URelationship_RightIndex">CREATE INDEX URelationship_RightIndex ON URelationship(anyObject_id)</entry>
+  <entry key="URelationship_LeftIndex">CREATE INDEX URelationship_LeftIndex ON URelationship(user_id)</entry>
+  <entry key="ARelationship_RightIndex">CREATE INDEX ARelationship_RightIndex ON ARelationship(right_anyObject_id)</entry>
+  <entry key="ARelationship_AnyObjectIndex">CREATE INDEX ARelationship_AnyObjectIndex ON ARelationship(left_anyObject_id)</entry>
+
+  <entry key="CPlainAttrValue_attrIndex">CREATE INDEX CPlainAttrValue_attrIndex on CPlainAttrValue(attribute_id)</entry>
+  <entry key="CPAttrUniqueValue_attrIndex">CREATE INDEX CPAttrUniqueValue_attrIndex on CPlainAttrUniqueValue(attribute_id)</entry>
+
+  <entry key="CPlainAttr_owner_Index">CREATE INDEX CPlainAttr_owner_Index on CPlainAttr(owner_id)</entry>
+  <entry key="CPlainAttr_schema_Index">CREATE INDEX CPlainAttr_schema_Index on CPlainAttr(schema_id)</entry>
+
+  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
+</properties>
diff --git a/core/persistence-jpa-pgjsonb/src/main/resources/persistence.properties b/fit/core-reference/src/main/resources/pgjsonb/persistence.properties
similarity index 95%
copy from core/persistence-jpa-pgjsonb/src/main/resources/persistence.properties
copy to fit/core-reference/src/main/resources/pgjsonb/persistence.properties
index c3196eb..a299527 100644
--- a/core/persistence-jpa-pgjsonb/src/main/resources/persistence.properties
+++ b/fit/core-reference/src/main/resources/pgjsonb/persistence.properties
@@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAAnySearchDAO
 user.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAAnyObjectDAO
+conf.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAConfDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/fit/core-reference/src/main/resources/pgjsonb/provisioning.properties b/fit/core-reference/src/main/resources/pgjsonb/provisioning.properties
new file mode 100644
index 0000000..378e75e
--- /dev/null
+++ b/fit/core-reference/src/main/resources/pgjsonb/provisioning.properties
@@ -0,0 +1,32 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+asyncConnectorFacadeExecutor.poolSize=10
+
+# see http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-task-namespace-executor
+propagationTaskExecutorAsyncExecutor.poolSize=5-25
+propagationTaskExecutorAsyncExecutor.queueCapacity=100
+propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
+
+userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
+groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
+anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
+virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
+
+quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
+quartz.sql=tables_postgres.sql
+quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/pgjsonb/views.xml b/fit/core-reference/src/main/resources/pgjsonb/views.xml
new file mode 100644
index 0000000..eb450be
--- /dev/null
+++ b/fit/core-reference/src/main/resources/pgjsonb/views.xml
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+  
+  <entry key="UDynGroupMembers">
+    CREATE TABLE UDynGroupMembers(
+    any_id CHAR(36),
+    group_id CHAR(36),
+    UNIQUE(any_id, group_id))
+  </entry>
+  <entry key="ADynGroupMembers">
+    CREATE TABLE ADynGroupMembers(
+    anyType_id VARCHAR(255),
+    any_id CHAR(36),
+    group_id CHAR(36),
+    UNIQUE(anyType_id, any_id, group_id))
+  </entry>
+  <entry key="DynRoleMembers">
+    CREATE TABLE DynRoleMembers(
+    any_id CHAR(36),
+    role_id VARCHAR(255),
+    UNIQUE(any_id, role_id))
+  </entry>
+  <entry key="DynRealmMembers">
+    CREATE TABLE DynRealmMembers(
+    any_id CHAR(36),
+    dynRealm_id VARCHAR(255),
+    UNIQUE(any_id, dynRealm_id))
+  </entry>
+
+  <!-- user -->
+  <entry key="user_search">
+    CREATE VIEW user_search AS
+
+    SELECT u.id as any_id, u.*,attrs,attrValues
+    FROM SyncopeUser u, jsonb_array_elements(COALESCE(u.plainAttrs, '[{}]'::jsonb)) attrs,
+    jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues
+  </entry>
+  <entry key="user_search_urelationship">
+    CREATE VIEW user_search_urelationship AS
+
+    SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type
+    FROM URelationship m
+  </entry>
+  <entry key="user_search_umembership">
+    CREATE VIEW user_search_umembership AS
+
+    SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name
+    FROM UMembership m, SyncopeGroup g
+    WHERE m.group_id = g.id
+  </entry>
+  <entry key="user_search_role">
+    CREATE VIEW user_search_role AS
+
+    SELECT ss.user_id AS any_id, ss.role_id AS role_id
+    FROM SyncopeUser_SyncopeRole ss
+  </entry>
+  <entry key="user_search_priv">
+    CREATE VIEW user_search_priv AS
+
+    SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
+    FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
+    WHERE ss.role_id = sp.role_id
+  </entry>
+  <entry key="user_search_dynpriv">
+    CREATE VIEW user_search_dynpriv AS
+
+    SELECT any_id, privilege_id
+    FROM DynRoleMembers drm, SyncopeRole_Privilege rp
+    WHERE drm.role_id = rp.role_id
+  </entry>
+  <entry key="user_search_resource">
+    CREATE VIEW user_search_resource AS
+
+    SELECT st.user_id AS any_id, st.resource_id AS resource_id
+    FROM SyncopeUser_ExternalResource st
+  </entry>
+  <entry key="user_search_group_res">
+    CREATE VIEW user_search_group_res AS
+
+    SELECT m.user_id AS any_id, st.resource_id AS resource_id
+    FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
+    WHERE m.group_id = r.id AND st.group_id = r.id
+  </entry>
+
+  <!-- anyObject -->
+  <entry key="anyObject_search">
+    CREATE VIEW anyObject_search AS
+
+    SELECT a.id as any_id, a.*,attrs,attrValues
+    FROM AnyObject a, jsonb_array_elements(COALESCE(a.plainAttrs, '[{}]'::jsonb)) attrs,
+    jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues
+  </entry>
+  <entry key="anyObject_search_arelationship">
+    CREATE VIEW anyObject_search_arelationship AS
+
+    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type
+    FROM ARelationship m
+  </entry>
+  <entry key="anyObject_search_amembership">
+    CREATE VIEW anyObject_search_amembership AS
+
+    SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name
+    FROM AMembership m, SyncopeGroup g
+    WHERE m.group_id = g.id
+  </entry>
+  <entry key="anyObject_search_resource">
+    CREATE VIEW anyObject_search_resource AS
+
+    SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id
+    FROM AnyObject_ExternalResource st
+  </entry>
+  <entry key="anyObject_search_group_res">
+    CREATE VIEW anyObject_search_group_res AS
+
+    SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id
+    FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
+    WHERE m.group_id = r.id AND st.group_id = r.id
+  </entry>
+
+  <!-- group -->
+  <entry key="group_search">
+    CREATE VIEW group_search AS
+
+    SELECT g.id as any_id, g.*,attrs,attrValues
+    FROM SyncopeGroup g, jsonb_array_elements(COALESCE(g.plainAttrs, '[{}]'::jsonb)) attrs,
+    jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues
+  </entry>
+  <entry key="group_search_resource">
+    CREATE VIEW group_search_resource AS
+
+    SELECT st.group_id AS any_id, st.resource_id AS resource_id
+    FROM SyncopeGroup_ExternalResource st
+  </entry>
+
+</properties>
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/CamelDetector.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/CamelDetector.java
deleted file mode 100644
index 8ac7871..0000000
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/CamelDetector.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.fit;
-
-import org.apache.syncope.common.rest.api.service.SyncopeService;
-
-public class CamelDetector {
-
-    public static boolean isCamelEnabledForUsers(final SyncopeService syncopeService) {
-        return syncopeService.platform().getUserProvisioningManager().contains("Camel");
-    }
-
-    public static boolean isCamelEnabledForGroups(final SyncopeService syncopeService) {
-        return syncopeService.platform().getGroupProvisioningManager().contains("Camel");
-    }
-
-    public static boolean isCamelEnabledForAnyObjects(final SyncopeService syncopeService) {
-        return syncopeService.platform().getAnyObjectProvisioningManager().contains("Camel");
-    }
-}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/SCIMDetector.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/SCIMDetector.java
deleted file mode 100644
index 265e292..0000000
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/SCIMDetector.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.fit;
-
-import javax.ws.rs.core.Response;
-import org.apache.cxf.jaxrs.client.WebClient;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class SCIMDetector {
-
-    private static final Logger LOG = LoggerFactory.getLogger(SCIMDetector.class);
-
-    private static Boolean ENABLED;
-
-    public static boolean isSCIMAvailable(final WebClient webClient) {
-        synchronized (LOG) {
-            if (ENABLED == null) {
-                try {
-                    Response response = webClient.path("ServiceProviderConfig").get();
-                    ENABLED = response.getStatus() == 200;
-                } catch (Exception e) {
-                    // ignore
-                    ENABLED = false;
-                }
-            }
-        }
-        return ENABLED;
-    }
-}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
index 21fb4fd..bc9711a 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
@@ -32,6 +32,7 @@ import org.apache.syncope.common.lib.to.BpmnProcess;
 import org.apache.syncope.fit.AbstractITCase;
 import org.apache.syncope.fit.FlowableDetector;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 public class BpmnProcessITCase extends AbstractITCase {
@@ -49,9 +50,13 @@ public class BpmnProcessITCase extends AbstractITCase {
         assertNotNull(userWorkflowKey);
     }
 
+    @BeforeEach
+    public void check() {
+        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
+    }
+
     @Test
     public void exportUserWorkflowProcess() throws IOException {
-        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
         Response response = bpmnProcessService.get(userWorkflowKey);
         assertTrue(response.getMediaType().toString().
                 startsWith(clientFactory.getContentType().getMediaType().toString()));
@@ -63,7 +68,6 @@ public class BpmnProcessITCase extends AbstractITCase {
 
     @Test
     public void updateUserWorkflowProcess() throws IOException {
-        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
         Response response = bpmnProcessService.get(userWorkflowKey);
         String definition = IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8);
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CamelRouteITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CamelRouteITCase.java
index 9316ad2..eb5a3a7 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CamelRouteITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/CamelRouteITCase.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.fit.core;
 
-import org.apache.syncope.fit.CamelDetector;
-
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.fail;
@@ -35,14 +33,18 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 public class CamelRouteITCase extends AbstractITCase {
 
+    @BeforeEach
+    public void check() {
+        assumeTrue(syncopeService.platform().getUserProvisioningManager().contains("Camel"));
+    }
+
     @Test
     public void userRoutes() {
-        assumeTrue(CamelDetector.isCamelEnabledForUsers(syncopeService));
-
         List<CamelRouteTO> userRoutes = camelRouteService.list(AnyTypeKind.USER);
         assertNotNull(userRoutes);
         assertEquals(16, userRoutes.size());
@@ -51,8 +53,6 @@ public class CamelRouteITCase extends AbstractITCase {
 
     @Test
     public void groupRoutes() {
-        assumeTrue(CamelDetector.isCamelEnabledForGroups(syncopeService));
-
         List<CamelRouteTO> groupRoutes = camelRouteService.list(AnyTypeKind.GROUP);
         assertNotNull(groupRoutes);
         assertEquals(8, groupRoutes.size());
@@ -69,8 +69,6 @@ public class CamelRouteITCase extends AbstractITCase {
 
     @Test
     public void update() {
-        assumeTrue(CamelDetector.isCamelEnabledForUsers(syncopeService));
-
         CamelRouteTO oldRoute = camelRouteService.read(AnyTypeKind.USER, "createUser");
         assertNotNull(oldRoute);
         String routeContent = "<route id=\"createUser\">\n"
@@ -103,8 +101,6 @@ public class CamelRouteITCase extends AbstractITCase {
 
     @Test
     public void scriptingUpdate() {
-        assumeTrue(CamelDetector.isCamelEnabledForUsers(syncopeService));
-
         CamelRouteTO oldRoute = camelRouteService.read(AnyTypeKind.USER, "createUser");
         // updating route content including new attribute management
 
@@ -169,8 +165,6 @@ public class CamelRouteITCase extends AbstractITCase {
 
     @Test
     public void issueSYNCOPE931() {
-        assumeTrue(CamelDetector.isCamelEnabledForUsers(syncopeService));
-
         CamelRouteTO oldRoute = camelRouteService.read(AnyTypeKind.USER, "createUser");
         assertNotNull(oldRoute);
         String routeContent = "<route id=\"createUser\">\n"
@@ -211,5 +205,4 @@ public class CamelRouteITCase extends AbstractITCase {
             doUpdate(AnyTypeKind.USER, oldRoute.getKey(), oldRoute.getContent());
         }
     }
-
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DomainITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DomainITCase.java
index a24d22e..096e37b 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DomainITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DomainITCase.java
@@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
 
 import java.security.AccessControlException;
 import java.util.List;
@@ -37,12 +38,14 @@ public class DomainITCase extends AbstractITCase {
 
     @Test
     public void list() {
+        assumeFalse(domainService.list().isEmpty());
+
         List<DomainTO> domains = domainService.list();
         assertNotNull(domains);
         assertFalse(domains.isEmpty());
-        for (DomainTO domain : domains) {
+        domains.forEach(domain -> {
             assertNotNull(domain);
-        }
+        });
     }
 
     @Test
@@ -70,6 +73,8 @@ public class DomainITCase extends AbstractITCase {
 
     @Test
     public void update() {
+        assumeFalse(domainService.list().isEmpty());
+
         DomainTO two = domainService.read("Two");
         assertNotNull(two);
 
@@ -99,6 +104,8 @@ public class DomainITCase extends AbstractITCase {
 
     @Test
     public void delete() {
+        assumeFalse(domainService.list().isEmpty());
+
         DomainTO two = domainService.read("Two");
         assertNotNull(two);
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
index aaa26fc..de39e53 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
@@ -249,20 +249,20 @@ public class GroupITCase extends AbstractITCase {
                 "PRINTER",
                 "(($groups==7;cool==ss);$resources==ws-target-resource-2);$type==PRINTER");
 
-        GroupTO updated = createGroup(original).getEntity();
+        GroupTO created = createGroup(original).getEntity();
 
-        updated.getPlainAttrs().add(new AttrTO.Builder().schema("icon").build());
-        updated.getPlainAttrs().add(new AttrTO.Builder().schema("show").build());
-        updated.getPlainAttrs().add(new AttrTO.Builder().schema("rderived_sx").value("sx").build());
-        updated.getPlainAttrs().add(new AttrTO.Builder().schema("rderived_dx").value("dx").build());
-        updated.getPlainAttrs().add(new AttrTO.Builder().schema("title").value("mr").build());
+        created.getPlainAttrs().add(new AttrTO.Builder().schema("icon").build());
+        created.getPlainAttrs().add(new AttrTO.Builder().schema("show").build());
+        created.getPlainAttrs().add(new AttrTO.Builder().schema("rderived_sx").value("sx").build());
+        created.getPlainAttrs().add(new AttrTO.Builder().schema("rderived_dx").value("dx").build());
+        created.getPlainAttrs().add(new AttrTO.Builder().schema("title").value("mr").build());
 
-        original = groupService.read(updated.getKey());
+        original = groupService.read(created.getKey());
 
-        GroupPatch patch = AnyOperations.diff(updated, original, true);
-        GroupTO group = updateGroup(patch).getEntity();
+        GroupPatch patch = AnyOperations.diff(created, original, true);
+        GroupTO updated = updateGroup(patch).getEntity();
 
-        Map<String, AttrTO> attrs = EntityTOUtils.buildAttrMap(group.getPlainAttrs());
+        Map<String, AttrTO> attrs = EntityTOUtils.buildAttrMap(updated.getPlainAttrs());
         assertFalse(attrs.containsKey("icon"));
         assertFalse(attrs.containsKey("show"));
         assertEquals(Collections.singletonList("sx"), attrs.get("rderived_sx").getValues());
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java
index d51dca1..9725be1 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java
@@ -153,7 +153,8 @@ public class MembershipITCase extends AbstractITCase {
             assertTrue(user.getMemberships().isEmpty());
         } finally {
             if (user.getKey() != null) {
-                userService.delete(user.getKey());
+                System.out.println("ZZZZZZZZZZZZZZZZZ " + user.getKey());
+//                userService.delete(user.getKey());
             }
         }
     }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MultitenancyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MultitenancyITCase.java
index c088bd6..8703d22 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MultitenancyITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MultitenancyITCase.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
 
 import java.util.List;
 import java.util.Locale;
@@ -71,21 +72,29 @@ import org.apache.syncope.common.rest.api.service.UserService;
 import org.apache.syncope.fit.AbstractITCase;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 public class MultitenancyITCase extends AbstractITCase {
 
     @BeforeAll
     public static void restSetup() {
-        clientFactory = new SyncopeClientFactoryBean().setAddress(ADDRESS).setDomain("Two");
+        if (!domainService.list().isEmpty()) {
+            clientFactory = new SyncopeClientFactoryBean().setAddress(ADDRESS).setDomain("Two");
 
-        String envContentType = System.getProperty(ENV_KEY_CONTENT_TYPE);
-        if (StringUtils.isNotBlank(envContentType)) {
-            clientFactory.setContentType(envContentType);
+            String envContentType = System.getProperty(ENV_KEY_CONTENT_TYPE);
+            if (StringUtils.isNotBlank(envContentType)) {
+                clientFactory.setContentType(envContentType);
+            }
+            LOG.info("Performing IT with content type {}", clientFactory.getContentType().getMediaType());
+
+            adminClient = clientFactory.create(ADMIN_UNAME, "password2");
         }
-        LOG.info("Performing IT with content type {}", clientFactory.getContentType().getMediaType());
+    }
 
-        adminClient = clientFactory.create(ADMIN_UNAME, "password2");
+    @BeforeEach
+    public void multitenancyCheck() {
+        assumeFalse(domainService.list().isEmpty());
     }
 
     @Test
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
index deacadc..3994005 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
@@ -65,7 +65,7 @@ import org.apache.syncope.ext.scimv2.api.type.ErrorType;
 import org.apache.syncope.ext.scimv2.api.type.Resource;
 import org.apache.syncope.ext.scimv2.cxf.JacksonSCIMJsonProvider;
 import org.apache.syncope.fit.AbstractITCase;
-import org.apache.syncope.fit.SCIMDetector;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 public class SCIMITCase extends AbstractITCase {
@@ -74,6 +74,8 @@ public class SCIMITCase extends AbstractITCase {
 
     private static final SCIMConf CONF;
 
+    private static Boolean ENABLED;
+
     private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
 
         @Override
@@ -105,6 +107,21 @@ public class SCIMITCase extends AbstractITCase {
         CONF.getUserConf().getEmails().add(email);
     }
 
+    private static boolean isSCIMAvailable(final WebClient webClient) {
+        synchronized (LOG) {
+            if (ENABLED == null) {
+                try {
+                    Response response = webClient.path("ServiceProviderConfig").get();
+                    ENABLED = response.getStatus() == 200;
+                } catch (Exception e) {
+                    // ignore
+                    ENABLED = false;
+                }
+            }
+        }
+        return ENABLED;
+    }
+
     private WebClient webClient() {
         return WebClient.create(SCIM_ADDRESS, Arrays.asList(new JacksonSCIMJsonProvider())).
                 accept(SCIMConstants.APPLICATION_SCIM_JSON_TYPE).
@@ -112,10 +129,13 @@ public class SCIMITCase extends AbstractITCase {
                 header(HttpHeaders.AUTHORIZATION, "Bearer " + adminClient.getJWT());
     }
 
+    @BeforeEach
+    public void check() {
+        assumeTrue(isSCIMAvailable(webClient()));
+    }
+
     @Test
     public void serviceProviderConfig() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         Response response = webClient().path("ServiceProviderConfig").get();
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals(
@@ -133,8 +153,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void resourceTypes() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         Response response = webClient().path("ResourceTypes").get();
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals(
@@ -157,8 +175,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void schemas() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         Response response = webClient().path("Schemas").get();
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals(
@@ -182,8 +198,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void read() throws IOException {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         Response response = webClient().path("Users").path("missing").get();
         assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
 
@@ -220,8 +234,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void conf() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         SCIMConf conf = scimConfService.get();
         assertNotNull(conf);
 
@@ -240,8 +252,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void list() throws IOException {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         Response response = webClient().path("Groups").query("count", 1100000).get();
         assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
         SCIMError error = response.readEntity(SCIMError.class);
@@ -271,8 +281,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void search() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         // invalid filter
         Response response = webClient().path("Groups").query("filter", "invalid").get();
         assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
@@ -359,8 +367,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void createUser() throws JsonProcessingException {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         scimConfService.set(CONF);
 
         SCIMUser user = getSampleUser(UUID.randomUUID().toString());
@@ -389,8 +395,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void replaceUser() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         scimConfService.set(CONF);
 
         SCIMUser user = getSampleUser(UUID.randomUUID().toString());
@@ -412,8 +416,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void deleteUser() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         scimConfService.set(CONF);
 
         SCIMUser user = getSampleUser(UUID.randomUUID().toString());
@@ -436,8 +438,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void createGroup() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         String displayName = UUID.randomUUID().toString();
 
         SCIMGroup group = new SCIMGroup(null, null, displayName);
@@ -470,8 +470,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void replaceGroup() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         SCIMGroup group = new SCIMGroup(null, null, UUID.randomUUID().toString());
         group.getMembers().add(new Member("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee", null, null));
         Response response = webClient().path("Groups").post(group);
@@ -505,8 +503,6 @@ public class SCIMITCase extends AbstractITCase {
 
     @Test
     public void deleteGroup() {
-        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
-
         SCIMGroup group = new SCIMGroup(null, null, UUID.randomUUID().toString());
         Response response = webClient().path("Groups").post(group);
         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
index 6358b08..def056d 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
@@ -932,12 +932,12 @@ public class UserITCase extends AbstractITCase {
 
         userTO.getResources().add(RESOURCE_NAME_CSV);
 
-        UserTO actual = createUser(userTO).getEntity();
-        assertNotNull(actual);
-        assertNotNull(actual.getDerAttr("csvuserid"));
+        userTO = createUser(userTO).getEntity();
+        assertNotNull(userTO);
+        assertNotNull(userTO.getDerAttr("csvuserid"));
 
         ConnObjectTO connObjectTO =
-                resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
+                resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), userTO.getKey());
         assertNotNull(connObjectTO);
         assertEquals("sx-dx", connObjectTO.getAttr("THEIRGROUP").get().getValues().get(0));
     }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
index c6bfcad..30f3444 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
@@ -799,15 +799,20 @@ public class UserIssuesITCase extends AbstractITCase {
 
     @Test
     public void issueSYNCOPE420() throws IOException {
-        ImplementationTO logicActions = new ImplementationTO();
-        logicActions.setKey("DoubleValueLogicActions");
-        logicActions.setEngine(ImplementationEngine.GROOVY);
-        logicActions.setType(ImplementationType.LOGIC_ACTIONS);
-        logicActions.setBody(org.apache.commons.io.IOUtils.toString(
-                getClass().getResourceAsStream("/DoubleValueLogicActions.groovy"), StandardCharsets.UTF_8));
-        Response response = implementationService.create(logicActions);
-        logicActions = implementationService.read(
-                logicActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+        ImplementationTO logicActions;
+        try {
+            logicActions = implementationService.read(ImplementationType.LOGIC_ACTIONS, "DoubleValueLogicActions");
+        } catch (SyncopeClientException e) {
+            logicActions = new ImplementationTO();
+            logicActions.setKey("DoubleValueLogicActions");
+            logicActions.setEngine(ImplementationEngine.GROOVY);
+            logicActions.setType(ImplementationType.LOGIC_ACTIONS);
+            logicActions.setBody(org.apache.commons.io.IOUtils.toString(
+                    getClass().getResourceAsStream("/DoubleValueLogicActions.groovy"), StandardCharsets.UTF_8));
+            Response response = implementationService.create(logicActions);
+            logicActions = implementationService.read(
+                    logicActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+        }
         assertNotNull(logicActions);
 
         RealmTO realm = realmService.list("/even/two").iterator().next();
diff --git a/pom.xml b/pom.xml
index c6a0682..8e7bc0d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -517,6 +517,8 @@ under the License.
     <nodejs.version>v8.11.4</nodejs.version>    
     <protractor.version>5.4.0</protractor.version>    
 
+    <docker.postgresql.version>11.1</docker.postgresql.version>
+
     <jdbc.postgresql.version>42.2.5</jdbc.postgresql.version>
     <jdbc.mysql.version>5.1.47</jdbc.mysql.version>
     <jdbc.mariadb.version>2.2.6</jdbc.mariadb.version>
diff --git a/src/main/asciidoc/getting-started/obtain.adoc b/src/main/asciidoc/getting-started/obtain.adoc
index 473b608..75f9f2c 100644
--- a/src/main/asciidoc/getting-started/obtain.adoc
+++ b/src/main/asciidoc/getting-started/obtain.adoc
@@ -363,18 +363,12 @@ version: '3.3'
 
 services:
    db: // <1>
-     image: crunchydata/crunchy-postgres:centos7-10.3-1.8.2
+     image: postgres:latest
      restart: always
      environment:
-       PG_ROOT_PASSWORD: postgres
-       PG_MODE: primary
-       PG_PRIMARY_USER: postgres
-       PG_PRIMARY_PASSWORD: postgres
-       PG_PRIMARY_HOST: localhost
-       PG_PRIMARY_PORT: 5432
-       PG_DATABASE: syncope
-       PG_USER: syncope
-       PG_PASSWORD: syncope
+       POSTGRES_DB: syncope
+       POSTGRES_USER: syncope
+       POSTGRES_PASSWORD: syncope
 
    syncope: // <2>
      depends_on:
@@ -417,7 +411,7 @@ services:
        CORE_PORT: 8080
        DOMAIN: Master
 ----
-<1> Database container for usage as internal storage, based on PostgreSQL 10.3
+<1> Database container for usage as internal storage, based on latest PostgreSQL image available
 <2> Apache Syncope Core, single instance, port `18080` exposed
 <3> Apache Syncope Admin UI, port `28080` exposed
 <4> Apache Syncope Enduser UI, port `38080` exposed, working with `Master` domain