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/09 14:04:39 UTC

[syncope] 01/02: [SYNCOPE-1395] Preliminary changes

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

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

commit 1f54d793fdec207774a53f2c8372de470defdad7
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Thu Nov 8 10:30:38 2018 +0100

    [SYNCOPE-1395] Preliminary changes
---
 .../core/persistence/api/dao/AllowedSchemas.java   |  1 -
 .../core/persistence/api/dao/AnyObjectDAO.java     |  3 ++
 .../core/persistence/api/dao/PlainAttrDAO.java     | 10 ++--
 .../persistence/api/dao/PlainAttrValueDAO.java     | 17 +++----
 .../syncope/core/persistence/api/dao/UserDAO.java  |  3 ++
 .../core/persistence/api/entity/EntityFactory.java | 16 +++++++
 .../persistence/api/entity/GroupableRelatable.java |  3 +-
 .../core/persistence/jpa/dao/AbstractAnyDAO.java   |  8 +++-
 .../persistence/jpa/dao/AbstractAnySearchDAO.java  | 12 +----
 .../core/persistence/jpa/dao/JPAAnyObjectDAO.java  | 23 +++++----
 .../core/persistence/jpa/dao/JPAAnySearchDAO.java  |  2 +-
 .../core/persistence/jpa/dao/JPAConfDAO.java       | 10 ++--
 .../core/persistence/jpa/dao/JPADynRealmDAO.java   | 10 ++--
 .../core/persistence/jpa/dao/JPAGroupDAO.java      | 31 ++++++------
 .../core/persistence/jpa/dao/JPAPlainAttrDAO.java  | 19 +-------
 .../persistence/jpa/dao/JPAPlainAttrValueDAO.java  | 55 +++++++---------------
 .../persistence/jpa/dao/JPAPlainSchemaDAO.java     | 29 +++++++-----
 .../jpa/dao/JPARelationshipTypeDAO.java            |  5 +-
 .../core/persistence/jpa/dao/JPAUserDAO.java       | 29 +++++++-----
 .../core/persistence/jpa/entity/JPAAnyUtils.java   |  6 +--
 .../persistence/jpa/entity/JPAAnyUtilsFactory.java |  1 -
 .../persistence/jpa/entity/JPAEntityFactory.java   | 29 +++++++++++-
 .../jpa/entity/anyobject/JPAAnyObject.java         |  6 +++
 .../core/persistence/jpa/entity/conf/JPAConf.java  |  4 +-
 .../persistence/jpa/entity/group/JPAGroup.java     |  3 +-
 .../jpa/entity/resource/AbstractAnyTemplate.java   |  7 +--
 .../core/persistence/jpa/entity/user/JPAUser.java  |  6 +++
 .../jpa/validation/entity/AnyValidator.java        |  8 ++--
 .../jpa/validation/entity/PlainAttrValidator.java  | 27 +++++------
 .../validation/entity/PlainAttrValueValidator.java | 37 +++++++--------
 .../src/main/resources/persistence.properties      |  7 +++
 .../src/main/resources/persistenceContext.xml      |  7 +++
 .../syncope/core/persistence/jpa/AbstractTest.java | 12 +++++
 .../core/persistence/jpa/inner/ConfTest.java       | 11 +++--
 .../persistence/jpa/inner/MultitenancyTest.java    |  2 +
 .../core/persistence/jpa/inner/PlainAttrTest.java  | 50 ++++++++++++++++----
 .../persistence/jpa/inner/PlainSchemaTest.java     |  2 +
 .../core/persistence/jpa/inner/UserTest.java       | 31 +++++++++++-
 .../core/persistence/jpa/outer/ConfTest.java       | 11 +++--
 .../core/persistence/jpa/outer/GroupTest.java      | 14 +-----
 .../core/persistence/jpa/outer/PlainAttrTest.java  | 40 +++++++++-------
 .../persistence/jpa/outer/PlainSchemaTest.java     | 10 ++--
 .../core/persistence/jpa/outer/UserTest.java       | 16 ++-----
 .../jpa/outer/XMLContentExporterTest.java          | 10 ++--
 .../java/data/AbstractAnyDataBinder.java           | 10 ++--
 .../java/data/AnyObjectDataBinderImpl.java         |  2 +-
 .../provisioning/java/data/UserDataBinderImpl.java |  2 +-
 .../core/src/main/resources/persistence.properties |  7 +++
 .../src/main/resources/persistence.properties      |  7 +++
 .../resources/elasticsearch/persistence.properties |  7 +++
 50 files changed, 406 insertions(+), 272 deletions(-)

diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AllowedSchemas.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AllowedSchemas.java
index 54f5e51..aa3eee1 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AllowedSchemas.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AllowedSchemas.java
@@ -99,6 +99,5 @@ public class AllowedSchemas<S extends Schema> {
         public boolean test(final S object) {
             return object.getKey().equals(schema);
         }
-
     }
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
index f8f05f9..6fb8d79 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
@@ -26,6 +26,7 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.Relationship;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
@@ -44,6 +45,8 @@ public interface AnyObjectDAO extends AnyDAO<AnyObject> {
 
     AnyObject findByName(String name);
 
+    AMembership findMembership(String key);
+
     List<Group> findDynGroups(String key);
 
     List<Relationship<Any<?>, AnyObject>> findAllRelationships(AnyObject anyObject);
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainAttrDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainAttrDAO.java
index 204aade..bce54f2 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainAttrDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainAttrDAO.java
@@ -22,9 +22,11 @@ import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 
 public interface PlainAttrDAO extends DAO<PlainAttr<?>> {
 
-    <T extends PlainAttr<?>> T find(String key, Class<T> reference);
-
-    <T extends PlainAttr<?>> void delete(String key, Class<T> reference);
-
+    /**
+     * Deletes the given plain attribute and removes it from its owner.
+     *
+     * @param <T> actual plain attr class
+     * @param attr plain attribute
+     */
     <T extends PlainAttr<?>> void delete(T attr);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainAttrValueDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainAttrValueDAO.java
index 26bfeaa..ec980d8 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainAttrValueDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainAttrValueDAO.java
@@ -18,16 +18,17 @@
  */
 package org.apache.syncope.core.persistence.api.dao;
 
-import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 
 public interface PlainAttrValueDAO extends DAO<PlainAttrValue> {
 
-    <T extends PlainAttrValue> T find(String key, Class<T> reference);
-
-    <T extends PlainAttrValue> List<T> findAll(Class<T> reference);
-
-    <T extends PlainAttrValue> T save(T attributeValue);
-
-    <T extends PlainAttrValue> void delete(String key, Class<T> reference);
+    /**
+     * Deletes and remove all values of the given attribute.
+     *
+     * @param attr plain attribute
+     * @param anyUtils utility
+     */
+    void deleteAll(PlainAttr<?> attr, AnyUtils anyUtils);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
index 13d7c77..bda2a8d 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
@@ -27,6 +27,7 @@ import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 
 public interface UserDAO extends AnyDAO<User> {
@@ -41,6 +42,8 @@ public interface UserDAO extends AnyDAO<User> {
 
     List<User> findBySecurityQuestion(SecurityQuestion securityQuestion);
 
+    UMembership findMembership(String key);
+
     List<Role> findDynRoles(String key);
 
     Collection<Role> findAllRoles(User user);
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java
index 29be9ed..a756028 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java
@@ -18,9 +18,25 @@
  */
 package org.apache.syncope.core.persistence.api.entity;
 
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.conf.Conf;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+
 public interface EntityFactory {
 
     <E extends Entity> E newEntity(Class<E> reference);
 
     ConnPoolConf newConnPoolConf();
+
+    Class<? extends User> userClass();
+
+    Class<? extends Group> groupClass();
+
+    Class<? extends AnyObject> anyObjectClass();
+
+    Class<? extends Conf> confClass();
+
+    Class<? extends AnySearchDAO> anySearchDAOClass();
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/GroupableRelatable.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/GroupableRelatable.java
index dd13b4a..daa8a4c 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/GroupableRelatable.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/GroupableRelatable.java
@@ -83,6 +83,8 @@ public interface GroupableRelatable<
 
     boolean add(M membership);
 
+    boolean remove(M membership);
+
     Optional<? extends M> getMembership(String groupKey);
 
     List<? extends M> getMemberships();
@@ -96,5 +98,4 @@ public interface GroupableRelatable<
     Collection<? extends REL> getRelationships(RelationshipType relationshipType);
 
     List<? extends REL> getRelationships();
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
index 568b007..531607b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
@@ -47,6 +47,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.AnyTypeClass;
 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.DerSchema;
 import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
@@ -66,13 +67,16 @@ import org.springframework.transaction.annotation.Transactional;
 public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> implements AnyDAO<A> {
 
     @Autowired
+    protected AnyUtilsFactory anyUtilsFactory;
+
+    @Autowired
     protected ApplicationEventPublisher publisher;
 
     @Autowired
-    private PlainSchemaDAO plainSchemaDAO;
+    protected PlainSchemaDAO plainSchemaDAO;
 
     @Autowired
-    private DerSchemaDAO derSchemaDAO;
+    protected DerSchemaDAO derSchemaDAO;
 
     @Autowired
     protected DynRealmDAO dynRealmDAO;
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 0d35ccb..2a204cc 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
@@ -353,17 +353,7 @@ public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implement
             return Collections.<T>emptyList();
         }
 
-        List<OrderByClause> effectiveOrderBy;
-        if (orderBy.isEmpty()) {
-            OrderByClause keyClause = new OrderByClause();
-            keyClause.setField("key");
-            keyClause.setDirection(OrderByClause.Direction.ASC);
-            effectiveOrderBy = Collections.singletonList(keyClause);
-        } else {
-            effectiveOrderBy = orderBy;
-        }
-
-        return doSearch(adminRealms, cond, page, itemsPerPage, effectiveOrderBy, kind);
+        return doSearch(adminRealms, cond, page, itemsPerPage, orderBy, kind);
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
index 278434f..f4aacd2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
@@ -44,23 +44,22 @@ import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Relationship;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
 import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.user.URelationship;
-import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
+import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship;
 import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
 import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
-@Repository
 public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObjectDAO {
 
     @Autowired
@@ -71,7 +70,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
 
     @Override
     protected AnyUtils init() {
-        return new JPAAnyUtilsFactory().getInstance(AnyTypeKind.ANY_OBJECT);
+        return anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT);
     }
 
     @Transactional(readOnly = true)
@@ -89,7 +88,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     @Override
     public Map<AnyType, Integer> countByType() {
         Query query = entityManager().createQuery(
-                "SELECT e.type, COUNT(e) AS countByType FROM  " + JPAAnyObject.class.getSimpleName() + " e "
+                "SELECT e.type, COUNT(e) AS countByType FROM  " + anyUtils().anyClass().getSimpleName() + " e "
                 + "GROUP BY e.type ORDER BY countByType DESC");
         @SuppressWarnings("unchecked")
         List<Object[]> results = query.getResultList();
@@ -105,7 +104,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     @Override
     public Map<String, Integer> countByRealm(final AnyType anyType) {
         Query query = entityManager().createQuery(
-                "SELECT e.realm, COUNT(e) FROM  " + JPAAnyObject.class.getSimpleName() + " e "
+                "SELECT e.realm, COUNT(e) FROM  " + anyUtils().anyClass().getSimpleName() + " e "
                 + "WHERE e.type=:type GROUP BY e.realm");
         query.setParameter("type", anyType);
 
@@ -138,7 +137,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     @Override
     public AnyObject findByName(final String name) {
         TypedQuery<AnyObject> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAAnyObject.class.getSimpleName() + " e WHERE e.name = :name", AnyObject.class);
+                "SELECT e FROM " + anyUtils().anyClass().getSimpleName() + " e WHERE e.name = :name", AnyObject.class);
         query.setParameter("name", name);
 
         AnyObject result = null;
@@ -152,6 +151,11 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     }
 
     @Override
+    public AMembership findMembership(final String key) {
+        return entityManager().find(JPAAMembership.class, key);
+    }
+
+    @Override
     public List<Relationship<Any<?>, AnyObject>> findAllRelationships(final AnyObject anyObject) {
         List<Relationship<Any<?>, AnyObject>> result = new ArrayList<>();
 
@@ -177,14 +181,14 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     @Override
     public int count() {
         Query query = entityManager().createQuery(
-                "SELECT COUNT(e) FROM  " + JPAAnyObject.class.getSimpleName() + " e");
+                "SELECT COUNT(e) FROM  " + anyUtils().anyClass().getSimpleName() + " e");
         return ((Number) query.getSingleResult()).intValue();
     }
 
     @Override
     public List<AnyObject> findAll(final int page, final int itemsPerPage) {
         TypedQuery<AnyObject> query = entityManager().createQuery(
-                "SELECT e FROM  " + JPAAnyObject.class.getSimpleName() + " e ORDER BY e.id", AnyObject.class);
+                "SELECT e FROM  " + anyUtils().anyClass().getSimpleName() + " e ORDER BY e.id", AnyObject.class);
         query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
         query.setMaxResults(itemsPerPage);
 
@@ -308,5 +312,4 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     public Collection<String> findAllResourceKeys(final String key) {
         return findAllResources(authFind(key)).stream().map(resource -> resource.getKey()).collect(Collectors.toList());
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 0a04ac0..53c52d9 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -223,7 +223,7 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
     }
 
     private StringBuilder buildSelect(final OrderBySupport obs) {
-        StringBuilder select = new StringBuilder("SELECT u.any_id");
+        StringBuilder select = new StringBuilder("SELECT DISTINCT u.any_id");
 
         obs.items.forEach(item -> {
             select.append(',').append(item.select);
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 35d07d2..ed065fe 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
@@ -22,9 +22,10 @@ import java.util.Optional;
 import java.util.Collections;
 import java.util.List;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
+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.apache.syncope.core.persistence.jpa.entity.conf.JPAConf;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -33,11 +34,14 @@ public class JPAConfDAO extends AbstractDAO<Conf> implements ConfDAO {
 
     private static final String KEY = "cd64d66f-6fff-4008-b966-a06b1cc1436d";
 
+    @Autowired
+    private EntityFactory entityFactory;
+
     @Override
     public Conf get() {
-        Conf instance = entityManager().find(JPAConf.class, KEY);
+        Conf instance = entityManager().find(entityFactory.confClass(), KEY);
         if (instance == null) {
-            instance = new JPAConf();
+            instance = entityFactory.newEntity(Conf.class);
             instance.setKey(KEY);
 
             instance = entityManager().merge(instance);
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java
index 7fc9585..d9c1775 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADynRealmDAO.java
@@ -27,6 +27,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.DynRealmMembership;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
 import org.apache.syncope.core.persistence.jpa.entity.JPADynRealm;
 import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
@@ -48,6 +49,9 @@ public class JPADynRealmDAO extends AbstractDAO<DynRealm> implements DynRealmDAO
     private ApplicationEventPublisher publisher;
 
     @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
     private AnySearchDAO searchDAO;
 
     private AnySearchDAO jpaAnySearchDAO;
@@ -55,11 +59,11 @@ public class JPADynRealmDAO extends AbstractDAO<DynRealm> implements DynRealmDAO
     private AnySearchDAO jpaAnySearchDAO() {
         synchronized (this) {
             if (jpaAnySearchDAO == null) {
-                if (AopUtils.getTargetClass(searchDAO).equals(JPAAnySearchDAO.class)) {
+                if (AopUtils.getTargetClass(searchDAO).equals(entityFactory.anySearchDAOClass())) {
                     jpaAnySearchDAO = searchDAO;
                 } else {
-                    jpaAnySearchDAO = (AnySearchDAO) ApplicationContextProvider.getBeanFactory().
-                            createBean(JPAAnySearchDAO.class, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
+                    jpaAnySearchDAO = (AnySearchDAO) ApplicationContextProvider.getBeanFactory().createBean(
+                            entityFactory.anySearchDAOClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
                 }
             }
         }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
index b8125ef..a343ebc 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
@@ -54,6 +54,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 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.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
@@ -61,7 +62,6 @@ import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.TypeExtension;
 import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership;
 import org.apache.syncope.core.persistence.api.entity.user.UMembership;
-import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension;
@@ -73,10 +73,8 @@ import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.springframework.aop.support.AopUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
-@Repository
 public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
 
     public static final String UDYNMEMB_TABLE = "UDynGroupMembers";
@@ -84,6 +82,9 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     public static final String ADYNMEMB_TABLE = "ADynGroupMembers";
 
     @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
     private PlainAttrDAO plainAttrDAO;
 
     @Autowired
@@ -100,11 +101,11 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     private AnySearchDAO jpaAnySearchDAO() {
         synchronized (this) {
             if (jpaAnySearchDAO == null) {
-                if (AopUtils.getTargetClass(searchDAO).equals(JPAAnySearchDAO.class)) {
+                if (AopUtils.getTargetClass(searchDAO).equals(entityFactory.anySearchDAOClass())) {
                     jpaAnySearchDAO = searchDAO;
                 } else {
-                    jpaAnySearchDAO = (AnySearchDAO) ApplicationContextProvider.getBeanFactory().
-                            createBean(JPAAnySearchDAO.class, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
+                    jpaAnySearchDAO = (AnySearchDAO) ApplicationContextProvider.getBeanFactory().createBean(
+                            entityFactory.anySearchDAOClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
                 }
             }
         }
@@ -113,7 +114,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
 
     @Override
     protected AnyUtils init() {
-        return new JPAAnyUtilsFactory().getInstance(AnyTypeKind.GROUP);
+        return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
     }
 
     @Transactional(readOnly = true)
@@ -131,14 +132,14 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public int count() {
         Query query = entityManager().createQuery(
-                "SELECT COUNT(e) FROM  " + JPAGroup.class.getSimpleName() + " e");
+                "SELECT COUNT(e) FROM  " + anyUtils().anyClass().getSimpleName() + " e");
         return ((Number) query.getSingleResult()).intValue();
     }
 
     @Override
     public Map<String, Integer> countByRealm() {
         Query query = entityManager().createQuery(
-                "SELECT e.realm, COUNT(e) FROM  " + JPAGroup.class.getSimpleName() + " e GROUP BY e.realm");
+                "SELECT e.realm, COUNT(e) FROM  " + anyUtils().anyClass().getSimpleName() + " e GROUP BY e.realm");
 
         @SuppressWarnings("unchecked")
         List<Object[]> results = query.getResultList();
@@ -170,7 +171,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public Group findByName(final String name) {
         TypedQuery<Group> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAGroup.class.getSimpleName() + " e WHERE e.name = :name", Group.class);
+                "SELECT e FROM " + anyUtils().anyClass().getSimpleName() + " e WHERE e.name = :name", Group.class);
         query.setParameter("name", name);
 
         Group result = null;
@@ -191,7 +192,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             return Collections.<Group>emptyList();
         }
 
-        StringBuilder queryString = new StringBuilder("SELECT e FROM ").append(JPAGroup.class.getSimpleName()).
+        StringBuilder queryString = new StringBuilder("SELECT e FROM ").append(anyUtils().anyClass().getSimpleName()).
                 append(" e WHERE e.userOwner=:owner ");
         userDAO.findAllGroupKeys(owner).forEach(groupKey -> {
             queryString.append("OR e.groupOwner.id='").append(groupKey).append("' ");
@@ -212,7 +213,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
         }
 
         TypedQuery<Group> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAGroup.class.getSimpleName() + " e WHERE e.groupOwner=:owner", Group.class);
+                "SELECT e FROM " + anyUtils().anyClass().getSimpleName() + " e WHERE e.groupOwner=:owner", Group.class);
         query.setParameter("owner", owner);
 
         return query.getResultList();
@@ -241,7 +242,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public List<Group> findAll(final int page, final int itemsPerPage) {
         TypedQuery<Group> query = entityManager().createQuery(
-                "SELECT e FROM  " + JPAGroup.class.getSimpleName() + " e ORDER BY e.id", Group.class);
+                "SELECT e FROM  " + anyUtils().anyClass().getSimpleName() + " e ORDER BY e.id", Group.class);
         query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
         query.setMaxResults(itemsPerPage);
 
@@ -324,7 +325,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
 
         findAMemberships(group).forEach(membership -> {
             AnyObject leftEnd = membership.getLeftEnd();
-            leftEnd.getMemberships().remove(membership);
+            leftEnd.remove(membership);
             membership.setRightEnd(null);
             leftEnd.getPlainAttrs(membership).stream().map(attr -> {
                 leftEnd.remove(attr);
@@ -339,7 +340,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
 
         findUMemberships(group).forEach(membership -> {
             User leftEnd = membership.getLeftEnd();
-            leftEnd.getMemberships().remove(membership);
+            leftEnd.remove(membership);
             membership.setRightEnd(null);
             leftEnd.getPlainAttrs(membership).stream().map(attr -> {
                 leftEnd.remove(attr);
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainAttrDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainAttrDAO.java
index d4ba926..5ca294c 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainAttrDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainAttrDAO.java
@@ -30,12 +30,10 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttr;
 import org.apache.syncope.core.persistence.jpa.entity.conf.JPACPlainAttr;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttr;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttr;
-import org.springframework.stereotype.Repository;
 
-@Repository
 public class JPAPlainAttrDAO extends AbstractDAO<PlainAttr<?>> implements PlainAttrDAO {
 
-    public <T extends PlainAttr<?>> Class<? extends AbstractPlainAttr<?>> getEntityReference(
+    public static <T extends PlainAttr<?>> Class<? extends AbstractPlainAttr<?>> getEntityReference(
             final Class<T> reference) {
 
         return CPlainAttr.class.isAssignableFrom(reference)
@@ -50,21 +48,6 @@ public class JPAPlainAttrDAO extends AbstractDAO<PlainAttr<?>> implements PlainA
     }
 
     @Override
-    public <T extends PlainAttr<?>> T find(final String key, final Class<T> reference) {
-        return reference.cast(entityManager().find(getEntityReference(reference), key));
-    }
-
-    @Override
-    public <T extends PlainAttr<?>> void delete(final String key, final Class<T> reference) {
-        T attribute = find(key, reference);
-        if (attribute == null) {
-            return;
-        }
-
-        delete(attribute);
-    }
-
-    @Override
     @SuppressWarnings("unchecked")
     public <T extends PlainAttr<?>> void delete(final T plainAttr) {
         if (plainAttr.getOwner() != null) {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainAttrValueDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainAttrValueDAO.java
index 9e24017..c0ede1d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainAttrValueDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainAttrValueDAO.java
@@ -18,10 +18,11 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
-import java.util.List;
-import javax.persistence.TypedQuery;
+import java.util.stream.Collectors;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
-import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue;
+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;
 import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrValue;
@@ -40,13 +41,11 @@ import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrUniqueV
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrValue;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue;
-import org.springframework.stereotype.Repository;
 
-@Repository
 public class JPAPlainAttrValueDAO extends AbstractDAO<PlainAttrValue> implements PlainAttrValueDAO {
 
     @SuppressWarnings("unchecked")
-    private <T extends PlainAttrValue> Class<? extends AbstractPlainAttrValue> getEntityReference(
+    public static <T extends PlainAttrValue> Class<? extends AbstractPlainAttrValue> getEntityReference(
             final Class<T> reference) {
 
         return AbstractPlainAttrValue.class.isAssignableFrom(reference)
@@ -71,37 +70,19 @@ public class JPAPlainAttrValueDAO extends AbstractDAO<PlainAttrValue> implements
     }
 
     @Override
-    public <T extends PlainAttrValue> T find(final String key, final Class<T> reference) {
-        return reference.cast(entityManager().find(getEntityReference(reference), key));
-    }
-
-    @Override
-    public <T extends PlainAttrValue> List<T> findAll(final Class<T> reference) {
-        TypedQuery<T> query = entityManager().createQuery(
-                "SELECT e FROM " + getEntityReference(reference).getSimpleName() + " e", reference);
-        return query.getResultList();
-    }
-
-    @Override
-    public <T extends PlainAttrValue> T save(final T attributeValue) {
-        return entityManager().merge(attributeValue);
-    }
-
-    @Override
-    public <T extends PlainAttrValue> void delete(final String key, final Class<T> reference) {
-        T attrValue = find(key, reference);
-        if (attrValue == null) {
-            return;
+    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) {
+                    entityManager().remove(attrValue);
+                    attr.getValues().remove(attrValue);
+                }
+            });
+        } else {
+            entityManager().remove(attr.getUniqueValue());
+            attr.setUniqueValue(null);
         }
-
-        if (attrValue.getAttr() != null) {
-            if (attrValue instanceof PlainAttrUniqueValue) {
-                attrValue.getAttr().setUniqueValue(null);
-            } else {
-                attrValue.getAttr().getValues().remove(attrValue);
-            }
-        }
-
-        entityManager().remove(attrValue);
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainSchemaDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainSchemaDAO.java
index ef569d7..46df742 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainSchemaDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainSchemaDAO.java
@@ -31,15 +31,15 @@ import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
-import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.JPAPlainSchema;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Repository;
 
-@Repository
 public class JPAPlainSchemaDAO extends AbstractDAO<PlainSchema> implements PlainSchemaDAO {
 
     @Autowired
+    private AnyUtilsFactory anyUtilsFactory;
+
+    @Autowired
     private PlainAttrDAO plainAttrDAO;
 
     @Autowired
@@ -94,7 +94,7 @@ public class JPAPlainSchemaDAO extends AbstractDAO<PlainSchema> implements Plain
     @Override
     public <T extends PlainAttr<?>> List<T> findAttrs(final PlainSchema schema, final Class<T> reference) {
         TypedQuery<T> query = entityManager().createQuery(
-                "SELECT e FROM " + ((JPAPlainAttrDAO) plainAttrDAO).getEntityReference(reference).getSimpleName()
+                "SELECT e FROM " + JPAPlainAttrDAO.getEntityReference(reference).getSimpleName()
                 + " e WHERE e.schema=:schema", reference);
         query.setParameter("schema", schema);
 
@@ -106,6 +106,16 @@ public class JPAPlainSchemaDAO extends AbstractDAO<PlainSchema> implements Plain
         return entityManager().merge(schema);
     }
 
+    protected void deleteAttrs(final PlainSchema schema) {
+        for (AnyTypeKind anyTypeKind : AnyTypeKind.values()) {
+            AnyUtils anyUtils = anyUtilsFactory.getInstance(anyTypeKind);
+
+            findAttrs(schema, anyUtils.plainAttrClass()).forEach(attr -> {
+                plainAttrDAO.delete(attr);
+            });
+        }
+    }
+
     @Override
     public void delete(final String key) {
         PlainSchema schema = find(key);
@@ -115,16 +125,9 @@ public class JPAPlainSchemaDAO extends AbstractDAO<PlainSchema> implements Plain
 
         schema.getLabels().forEach(label -> label.setSchema(null));
 
-        AnyUtilsFactory anyUtilsFactory = new JPAAnyUtilsFactory();
-        for (AnyTypeKind anyTypeKind : AnyTypeKind.values()) {
-            AnyUtils anyUtils = anyUtilsFactory.getInstance(anyTypeKind);
-
-            findAttrs(schema, anyUtils.plainAttrClass()).forEach(attr -> {
-                plainAttrDAO.delete(attr.getKey(), anyUtils.plainAttrClass());
-            });
+        deleteAttrs(schema);
 
-            resourceDAO.deleteMapping(key);
-        }
+        resourceDAO.deleteMapping(key);
 
         if (schema.getAnyTypeClass() != null) {
             schema.getAnyTypeClass().getPlainSchemas().remove(schema);
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARelationshipTypeDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARelationshipTypeDAO.java
index fd1f731..4d7f64e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARelationshipTypeDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARelationshipTypeDAO.java
@@ -82,11 +82,11 @@ public class JPARelationshipTypeDAO extends AbstractDAO<RelationshipType> implem
             if (relationship instanceof URelationship) {
                 ((URelationship) relationship).getLeftEnd().getRelationships().remove((URelationship) relationship);
             } else if (relationship instanceof UMembership) {
-                ((UMembership) relationship).getLeftEnd().getMemberships().remove((UMembership) relationship);
+                ((UMembership) relationship).getLeftEnd().remove((UMembership) relationship);
             } else if (relationship instanceof ARelationship) {
                 ((ARelationship) relationship).getLeftEnd().getRelationships().remove((ARelationship) relationship);
             } else if (relationship instanceof AMembership) {
-                ((AMembership) relationship).getLeftEnd().getMemberships().remove((AMembership) relationship);
+                ((AMembership) relationship).getLeftEnd().remove((AMembership) relationship);
             }
             relationship.setLeftEnd(null);
             return relationship;
@@ -94,5 +94,4 @@ public class JPARelationshipTypeDAO extends AbstractDAO<RelationshipType> implem
 
         entityManager().remove(type);
     }
-
 }
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 a86ee8e..33bf9f7 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
@@ -61,19 +61,18 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
 import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
 import org.apache.syncope.core.spring.ImplementationManager;
 import org.apache.syncope.core.spring.security.Encryptor;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
-@Repository
 public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     private static final Pattern USERNAME_PATTERN =
@@ -101,7 +100,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Override
     protected AnyUtils init() {
-        return new JPAAnyUtilsFactory().getInstance(AnyTypeKind.USER);
+        return anyUtilsFactory.getInstance(AnyTypeKind.USER);
     }
 
     @Transactional(readOnly = true)
@@ -119,14 +118,14 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Override
     public int count() {
         Query query = entityManager().createQuery(
-                "SELECT COUNT(e) FROM  " + JPAUser.class.getSimpleName() + " e");
+                "SELECT COUNT(e) FROM  " + anyUtils().anyClass().getSimpleName() + " e");
         return ((Number) query.getSingleResult()).intValue();
     }
 
     @Override
     public Map<String, Integer> countByRealm() {
         Query query = entityManager().createQuery(
-                "SELECT e.realm, COUNT(e) FROM  " + JPAUser.class.getSimpleName() + " e GROUP BY e.realm");
+                "SELECT e.realm, COUNT(e) FROM  " + anyUtils().anyClass().getSimpleName() + " e GROUP BY e.realm");
 
         @SuppressWarnings("unchecked")
         List<Object[]> results = query.getResultList();
@@ -138,7 +137,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Override
     public Map<String, Integer> countByStatus() {
         Query query = entityManager().createQuery(
-                "SELECT e.status, COUNT(e) FROM  " + JPAUser.class.getSimpleName() + " e GROUP BY e.status");
+                "SELECT e.status, COUNT(e) FROM  " + anyUtils().anyClass().getSimpleName() + " e GROUP BY e.status");
 
         @SuppressWarnings("unchecked")
         List<Object[]> results = query.getResultList();
@@ -174,7 +173,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Override
     public User findByUsername(final String username) {
-        TypedQuery<User> query = entityManager().createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+        TypedQuery<User> query = entityManager().createQuery(
+                "SELECT e FROM " + anyUtils().anyClass().getSimpleName()
                 + " e WHERE e.username = :username", User.class);
         query.setParameter("username", username);
 
@@ -190,7 +190,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Override
     public User findByToken(final String token) {
-        TypedQuery<User> query = entityManager().createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+        TypedQuery<User> query = entityManager().createQuery(
+                "SELECT e FROM " + anyUtils().anyClass().getSimpleName()
                 + " e WHERE e.token LIKE :token", User.class);
         query.setParameter("token", token);
 
@@ -206,13 +207,19 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Override
     public List<User> findBySecurityQuestion(final SecurityQuestion securityQuestion) {
-        TypedQuery<User> query = entityManager().createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+        TypedQuery<User> query = entityManager().createQuery(
+                "SELECT e FROM " + anyUtils().anyClass().getSimpleName()
                 + " e WHERE e.securityQuestion = :securityQuestion", User.class);
         query.setParameter("securityQuestion", securityQuestion);
 
         return query.getResultList();
     }
 
+    @Override
+    public UMembership findMembership(final String key) {
+        return entityManager().find(JPAUMembership.class, key);
+    }
+
     private List<PasswordPolicy> getPasswordPolicies(final User user) {
         List<PasswordPolicy> policies = new ArrayList<>();
 
@@ -240,7 +247,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Override
     public List<User> findAll(final int page, final int itemsPerPage) {
         TypedQuery<User> query = entityManager().createQuery(
-                "SELECT e FROM  " + JPAUser.class.getSimpleName() + " e ORDER BY e.id", User.class);
+                "SELECT e FROM  " + anyUtils().anyClass().getSimpleName() + " e ORDER BY e.id", User.class);
         query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
         query.setMaxResults(itemsPerPage);
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java
index aa86519..4cc3058 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java
@@ -141,16 +141,16 @@ public class JPAAnyUtils implements AnyUtils {
 
         switch (anyTypeKind) {
             case GROUP:
-                result = entityFactory.newEntity(Group.class).getClass();
+                result = entityFactory.groupClass();
                 break;
 
             case ANY_OBJECT:
-                result = entityFactory.newEntity(AnyObject.class).getClass();
+                result = entityFactory.anyObjectClass();
                 break;
 
             case USER:
             default:
-                result = entityFactory.newEntity(User.class).getClass();
+                result = entityFactory.userClass();
         }
 
         return result;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtilsFactory.java
index 1fa004b..7e5d282 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtilsFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtilsFactory.java
@@ -67,5 +67,4 @@ public class JPAAnyUtilsFactory implements AnyUtilsFactory {
 
         return getInstance(type);
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 79bc299..81be309 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPASecurityQuestion;
 import org.apache.syncope.core.persistence.api.entity.AccessToken;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy;
@@ -122,7 +123,6 @@ import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrUniqueVa
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
-import org.springframework.stereotype.Component;
 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
 import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
@@ -141,10 +141,10 @@ import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResour
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAOrgUnitItem;
 import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity;
+import org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushCorrelationRuleEntity;
 import org.apache.syncope.core.spring.security.SecureRandomUtils;
 
-@Component
 public class JPAEntityFactory implements EntityFactory {
 
     @SuppressWarnings("unchecked")
@@ -315,4 +315,29 @@ public class JPAEntityFactory implements EntityFactory {
     public ConnPoolConf newConnPoolConf() {
         return new JPAConnPoolConf();
     }
+
+    @Override
+    public Class<? extends User> userClass() {
+        return JPAUser.class;
+    }
+
+    @Override
+    public Class<? extends Group> groupClass() {
+        return JPAGroup.class;
+    }
+
+    @Override
+    public Class<? extends AnyObject> anyObjectClass() {
+        return JPAAnyObject.class;
+    }
+
+    @Override
+    public Class<? extends Conf> confClass() {
+        return JPAConf.class;
+    }
+
+    @Override
+    public Class<? extends AnySearchDAO> anySearchDAOClass() {
+        return JPAAnySearchDAO.class;
+    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
index d72e36a..0a04e01 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
@@ -175,6 +175,12 @@ public class JPAAnyObject
     }
 
     @Override
+    public boolean remove(final AMembership membership) {
+        checkType(membership, JPAAMembership.class);
+        return this.memberships.remove((JPAAMembership) membership);
+    }
+
+    @Override
     public List<? extends AMembership> getMemberships() {
         return memberships;
     }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/JPAConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/JPAConf.java
index b4be360..3911d28 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/JPAConf.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/JPAConf.java
@@ -59,12 +59,12 @@ public class JPAConf extends AbstractProvidedKeyEntity implements Conf {
     @Override
     public boolean remove(final CPlainAttr attr) {
         checkType(attr, JPACPlainAttr.class);
-        return plainAttrs.remove((JPACPlainAttr) attr);
+        return getPlainAttrs().remove((JPACPlainAttr) attr);
     }
 
     @Override
     public Optional<? extends CPlainAttr> getPlainAttr(final String plainSchema) {
-        return plainAttrs.stream().filter(plainAttr
+        return getPlainAttrs().stream().filter(plainAttr
                 -> plainAttr != null && plainAttr.getSchema() != null
                 && plainSchema.equals(plainAttr.getSchema().getKey())).findFirst();
     }
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 53ef4fb..d10cb9a 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
@@ -164,7 +164,7 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     @Override
     public boolean remove(final GPlainAttr attr) {
         checkType(attr, JPAGPlainAttr.class);
-        return plainAttrs.remove((JPAGPlainAttr) attr);
+        return getPlainAttrs().remove((JPAGPlainAttr) attr);
     }
 
     @Override
@@ -236,5 +236,4 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     public List<? extends TypeExtension> getTypeExtensions() {
         return typeExtensions;
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/AbstractAnyTemplate.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/AbstractAnyTemplate.java
index 2af788b..ce9dae3 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/AbstractAnyTemplate.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/AbstractAnyTemplate.java
@@ -25,9 +25,10 @@ import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.entity.AnyTemplate;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyType;
-import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
 
 @MappedSuperclass
 public abstract class AbstractAnyTemplate extends AbstractGeneratedKeyEntity implements AnyTemplate {
@@ -56,7 +57,8 @@ public abstract class AbstractAnyTemplate extends AbstractGeneratedKeyEntity imp
         return template == null
                 ? anyType == null
                         ? null
-                        : new JPAAnyUtilsFactory().getInstance(anyType.getKind()).newAnyTO()
+                        : ApplicationContextProvider.getApplicationContext().getBean(AnyUtilsFactory.class).
+                                getInstance(anyType.getKind()).newAnyTO()
                 : anyType == null
                         ? null
                         : POJOHelper.deserialize(template, anyType.getKind().getTOClass());
@@ -70,5 +72,4 @@ public abstract class AbstractAnyTemplate extends AbstractGeneratedKeyEntity imp
             this.template = POJOHelper.serialize(template);
         }
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
index 4ede8b7..4d91360 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
@@ -454,6 +454,12 @@ public class JPAUser
     }
 
     @Override
+    public boolean remove(final UMembership membership) {
+        checkType(membership, JPAUMembership.class);
+        return this.memberships.remove((JPAUMembership) membership);
+    }
+
+    @Override
     public List<? extends UMembership> getMemberships() {
         return memberships;
     }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java
index fba6674..fefb1e7 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java
@@ -22,13 +22,14 @@ import javax.validation.ConstraintValidatorContext;
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.core.persistence.api.dao.AllowedSchemas;
 import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.GroupableRelatable;
 import org.apache.syncope.core.persistence.api.entity.Membership;
 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.conf.Conf;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
 
 @SuppressWarnings("rawtypes")
 public class AnyValidator extends AbstractValidator<AnyCheck, Any> {
@@ -57,8 +58,9 @@ public class AnyValidator extends AbstractValidator<AnyCheck, Any> {
         context.disableDefaultConstraintViolation();
 
         if (!(any instanceof Conf)) {
-            AllowedSchemas<PlainSchema> allowedPlainSchemas = new JPAAnyUtilsFactory().
-                    getInstance(any.getType().getKind()).dao().findAllowedSchemas(any, PlainSchema.class);
+            AllowedSchemas<PlainSchema> allowedPlainSchemas =
+                    ApplicationContextProvider.getApplicationContext().getBean(AnyUtilsFactory.class).
+                            getInstance(any.getType().getKind()).dao().findAllowedSchemas(any, PlainSchema.class);
 
             for (PlainAttr<?> attr : ((Any<?>) any).getPlainAttrs()) {
                 if (attr != null && !allowedPlainSchemas.forSelfContains(attr.getSchema().getKey())) {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValidator.java
index d9975db..89ee1f8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValidator.java
@@ -25,32 +25,31 @@ import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 public class PlainAttrValidator extends AbstractValidator<PlainAttrCheck, PlainAttr<?>> {
 
     @Override
-    public boolean isValid(final PlainAttr<?> object, final ConstraintValidatorContext context) {
-        boolean isValid;
+    public boolean isValid(final PlainAttr<?> attr, final ConstraintValidatorContext context) {
+        context.disableDefaultConstraintViolation();
 
-        if (object == null) {
+        boolean isValid;
+        if (attr == null) {
             isValid = true;
         } else {
-            if (object.getSchema().isUniqueConstraint()) {
-                isValid = object.getValues().isEmpty() && object.getUniqueValue() != null;
+            if (attr.getSchema().isUniqueConstraint()) {
+                isValid = attr.getValues().isEmpty() && attr.getUniqueValue() != null;
             } else {
-                isValid = !object.getValues().isEmpty() && object.getUniqueValue() == null;
+                isValid = !attr.getValues().isEmpty() && attr.getUniqueValue() == null;
 
-                if (!object.getSchema().isMultivalue()) {
-                    isValid &= object.getValues().size() == 1;
+                if (!attr.getSchema().isMultivalue()) {
+                    isValid &= attr.getValues().size() == 1;
                 }
             }
 
             if (!isValid) {
-                LOG.error("Invalid values for attribute " + object + ": " + "schema=" + object.getSchema().getKey()
-                        + ", values={}", object.getValuesAsStrings());
-
-                context.disableDefaultConstraintViolation();
+                LOG.error("Invalid values for attribute " + attr + ": "
+                        + "schema=" + attr.getSchema().getKey() + ", values={}", attr.getValuesAsStrings());
 
                 context.buildConstraintViolationWithTemplate(
                         getTemplate(EntityViolationType.InvalidValueList,
-                                "Invalid values " + object.getValuesAsStrings())).
-                        addPropertyNode(object.getSchema().getKey()).addConstraintViolation();
+                                "Invalid values " + attr.getValuesAsStrings())).
+                        addPropertyNode(attr.getSchema().getKey()).addConstraintViolation();
             }
         }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueValidator.java
index 64ec693..fcb008d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueValidator.java
@@ -27,56 +27,55 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 public class PlainAttrValueValidator extends AbstractValidator<PlainAttrValueCheck, PlainAttrValue> {
 
     @Override
-    public boolean isValid(final PlainAttrValue object, final ConstraintValidatorContext context) {
-        boolean isValid;
+    public boolean isValid(final PlainAttrValue value, final ConstraintValidatorContext context) {
+        context.disableDefaultConstraintViolation();
 
-        if (object == null) {
+        boolean isValid;
+        if (value == null) {
             isValid = true;
         } else {
             int nonNullVales = 0;
-            if (object.getBooleanValue() != null) {
+            if (value.getBooleanValue() != null) {
                 nonNullVales++;
             }
-            if (object.getDateValue() != null) {
+            if (value.getDateValue() != null) {
                 nonNullVales++;
             }
-            if (object.getDoubleValue() != null) {
+            if (value.getDoubleValue() != null) {
                 nonNullVales++;
             }
-            if (object.getLongValue() != null) {
+            if (value.getLongValue() != null) {
                 nonNullVales++;
             }
-            if (object.getBinaryValue() != null) {
+            if (value.getBinaryValue() != null) {
                 nonNullVales++;
             }
-            if (object.getStringValue() != null) {
+            if (value.getStringValue() != null) {
                 nonNullVales++;
             }
             isValid = nonNullVales == 1;
 
             if (!isValid) {
-                LOG.error("More than one non-null value for " + object);
+                LOG.error("More than one non-null value for " + value);
 
-                context.disableDefaultConstraintViolation();
                 context.buildConstraintViolationWithTemplate(
                         getTemplate(EntityViolationType.MoreThanOneNonNull, "More than one non-null value found")).
-                        addPropertyNode(object.getClass().getSimpleName().replaceAll("\\n", " ")).
+                        addPropertyNode(value.getClass().getSimpleName().replaceAll("\\n", " ")).
                         addConstraintViolation();
 
-            } else if (object instanceof PlainAttrUniqueValue) {
-                PlainSchema uniqueValueSchema = ((PlainAttrUniqueValue) object).getSchema();
-                PlainSchema attrSchema = object.getAttr().getSchema();
+            } else if (value instanceof PlainAttrUniqueValue) {
+                PlainSchema uniqueValueSchema = ((PlainAttrUniqueValue) value).getSchema();
+                PlainSchema attrSchema = value.getAttr().getSchema();
 
                 isValid = uniqueValueSchema.equals(attrSchema);
 
                 if (!isValid) {
-                    LOG.error("Unique value schema for " + object.getClass().getSimpleName() + "[" + object.getKey()
-                            + "]" + " is " + uniqueValueSchema + ", while owning attribute schema is " + attrSchema);
+                    LOG.error("Unique value schema for " + value + " is " + uniqueValueSchema
+                            + ", while owning attribute's schema is " + attrSchema);
 
-                    context.disableDefaultConstraintViolation();
                     context.buildConstraintViolationWithTemplate(getTemplate(EntityViolationType.InvalidPlainAttr,
                             "Unique value schema is " + uniqueValueSchema
-                            + ", while owning attribute schema is " + attrSchema)).addPropertyNode("schema").
+                            + ", while owning attribute's schema is " + attrSchema)).addPropertyNode("schema").
                             addConstraintViolation();
                 }
             }
diff --git a/core/persistence-jpa/src/main/resources/persistence.properties b/core/persistence-jpa/src/main/resources/persistence.properties
index 946e588..0841aa0 100644
--- a/core/persistence-jpa/src/main/resources/persistence.properties
+++ b/core/persistence-jpa/src/main/resources/persistence.properties
@@ -15,5 +15,12 @@
 # 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
diff --git a/core/persistence-jpa/src/main/resources/persistenceContext.xml b/core/persistence-jpa/src/main/resources/persistenceContext.xml
index 50153a4..c7c54c2 100644
--- a/core/persistence-jpa/src/main/resources/persistenceContext.xml
+++ b/core/persistence-jpa/src/main/resources/persistenceContext.xml
@@ -30,7 +30,14 @@ under the License.
   <context:annotation-config/>
   
   <context:component-scan base-package="org.apache.syncope.core.persistence.jpa"/>
+  <bean class="${entity.factory}"/>
+  <bean class="${plainSchema.dao}"/>
+  <bean class="${plainAttr.dao}"/>
+  <bean class="${plainAttrValue.dao}"/>
   <bean class="${any.search.dao}"/>
+  <bean class="${user.dao}"/>
+  <bean class="${group.dao}"/>
+  <bean class="${anyObject.dao}"/>
 
   <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
 
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/AbstractTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/AbstractTest.java
index d0151ba..a957b27 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/AbstractTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/AbstractTest.java
@@ -23,6 +23,10 @@ import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.orm.jpa.EntityManagerFactoryUtils;
 import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@@ -46,4 +50,12 @@ public abstract class AbstractTest {
 
         return entityManager;
     }
+
+    protected <T extends PlainAttr<?>> T findPlainAttr(final String key, final Class<T> reference) {
+        return reference.cast(entityManager().find(JPAPlainAttrDAO.getEntityReference(reference), key));
+    }
+
+    protected <T extends PlainAttrValue> T findPlainAttrValue(final String key, final Class<T> reference) {
+        return reference.cast(entityManager().find(JPAPlainAttrValueDAO.getEntityReference(reference), key));
+    }
 }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConfTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConfTest.java
index 87c4f3f..55f0b6b 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConfTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConfTest.java
@@ -30,10 +30,12 @@ import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntit
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttrUniqueValue;
+import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttrValue;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
-import org.apache.syncope.core.persistence.jpa.entity.conf.JPACPlainAttrValue;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
@@ -62,12 +64,12 @@ public class ConfTest extends AbstractTest {
     }
 
     private void add(final CPlainAttr newAttr, final String value) {
-        JPACPlainAttrValue attrValue;
+        PlainAttrValue attrValue;
         if (newAttr.getSchema().isUniqueConstraint()) {
-            attrValue = new JPACPlainAttrValue();
+            attrValue = entityFactory.newEntity(CPlainAttrUniqueValue.class);
             ((PlainAttrUniqueValue) attrValue).setSchema(newAttr.getSchema());
         } else {
-            attrValue = new JPACPlainAttrValue();
+            attrValue = entityFactory.newEntity(CPlainAttrValue.class);
         }
         newAttr.add(value, attrValue);
     }
@@ -101,6 +103,7 @@ public class ConfTest extends AbstractTest {
 
         // 4. delete conf
         confDAO.delete("useless");
+        plainSchemaDAO.delete("useless");
         assertFalse(confDAO.find("useless").isPresent());
     }
 
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
index 8197a55..8af7240 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
@@ -37,6 +37,7 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -45,6 +46,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.transaction.annotation.Transactional;
 
 @Transactional("Two")
+@Tag("multitenancy")
 public class MultitenancyTest extends AbstractTest {
 
     @Autowired
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java
index cc9281e..856e570 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java
@@ -41,6 +41,7 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.apache.syncope.core.spring.security.Encryptor;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+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;
@@ -57,17 +58,19 @@ public class PlainAttrTest extends AbstractTest {
     @Autowired
     private PlainSchemaDAO plainSchemaDAO;
 
+    @Tag("plainAttrTable")
     @Test
     public void findByKey() {
-        UPlainAttr attribute = plainAttrDAO.find("01f22fbd-b672-40af-b528-686d9b27ebc4", UPlainAttr.class);
+        UPlainAttr attribute = findPlainAttr("01f22fbd-b672-40af-b528-686d9b27ebc4", UPlainAttr.class);
         assertNotNull(attribute);
-        attribute = plainAttrDAO.find("9d0d9e40-1b18-488e-9482-37dab82163c9", UPlainAttr.class);
+        attribute = findPlainAttr("9d0d9e40-1b18-488e-9482-37dab82163c9", UPlainAttr.class);
         assertNotNull(attribute);
     }
 
+    @Tag("plainAttrTable")
     @Test
     public void read() {
-        UPlainAttr attribute = plainAttrDAO.find("01f22fbd-b672-40af-b528-686d9b27ebc4", UPlainAttr.class);
+        UPlainAttr attribute = findPlainAttr("01f22fbd-b672-40af-b528-686d9b27ebc4", UPlainAttr.class);
         assertNotNull(attribute);
         assertTrue(attribute.getValues().isEmpty());
         assertNotNull(attribute.getUniqueValue());
@@ -136,7 +139,36 @@ public class PlainAttrTest extends AbstractTest {
     }
 
     @Test
-    public void validateAndSave() {
+    public void invalidValueList() {
+        User user = userDAO.find("1417acbe-cbf6-4277-9372-e75e04f97000");
+
+        PlainSchema emailSchema = plainSchemaDAO.find("email");
+        assertNotNull(emailSchema);
+
+        PlainSchema fullnameSchema = plainSchemaDAO.find("fullname");
+        assertNotNull(fullnameSchema);
+
+        UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class);
+        attr.setOwner(user);
+        attr.setSchema(emailSchema);
+
+        user.add(attr);
+
+        InvalidEntityException iee = null;
+        try {
+            userDAO.save(user);
+            fail("This should not happen");
+        } catch (InvalidEntityException e) {
+            iee = e;
+        }
+        assertNotNull(iee);
+        // for attr because no values are set
+        assertTrue(iee.hasViolation(EntityViolationType.InvalidValueList));
+    }
+
+    @Tag("plainAttrTable")
+    @Test
+    public void invalidPlainAttr() {
         User user = userDAO.find("1417acbe-cbf6-4277-9372-e75e04f97000");
 
         PlainSchema emailSchema = plainSchemaDAO.find("email");
@@ -166,9 +198,9 @@ public class PlainAttrTest extends AbstractTest {
             iee = e;
         }
         assertNotNull(iee);
-        // for attribute
+        // for attr because no values are set
         assertTrue(iee.hasViolation(EntityViolationType.InvalidValueList));
-        // for uauv
+        // for uauv because uauv.schema and uauv.attr.schema are different
         assertTrue(iee.hasViolation(EntityViolationType.InvalidPlainAttr));
     }
 
@@ -222,13 +254,13 @@ public class PlainAttrTest extends AbstractTest {
         assertTrue(Arrays.equals(bytes, photo.getValues().get(0).getBinaryValue()));
     }
 
+    @Tag("plainAttrTable")
     @Test
     public void delete() {
-        UPlainAttr attribute = plainAttrDAO.find(
-                "9d0d9e40-1b18-488e-9482-37dab82163c9", UPlainAttr.class);
+        UPlainAttr attribute = findPlainAttr("9d0d9e40-1b18-488e-9482-37dab82163c9", UPlainAttr.class);
         String attrSchemaName = attribute.getSchema().getKey();
 
-        plainAttrDAO.delete(attribute.getKey(), UPlainAttr.class);
+        plainAttrDAO.delete(attribute);
 
         PlainSchema schema = plainSchemaDAO.find(attrSchemaName);
         assertNotNull(schema);
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
index ff46d62..d499652 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
@@ -37,6 +37,7 @@ import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr;
 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;
@@ -73,6 +74,7 @@ public class PlainSchemaTest extends AbstractTest {
         assertFalse(schema.getLabel(Locale.KOREAN).isPresent());
     }
 
+    @Tag("plainAttrTable")
     @Test
     public void findAttrs() {
         PlainSchema schema = plainSchemaDAO.find("icon");
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
index 01d379d..b35d83b 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.core.persistence.jpa.inner;
 
 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.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -36,6 +37,8 @@ import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.apache.syncope.core.provisioning.api.utils.policy.InvalidPasswordRuleConf;
 import org.apache.syncope.core.spring.security.PasswordGenerator;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrUniqueValue;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
@@ -56,6 +59,23 @@ public class UserTest extends AbstractTest {
     private ExternalResourceDAO resourceDAO;
 
     @Test
+    public void find() {
+        User user = userDAO.find("823074dc-d280-436d-a7dd-07399fae48ec");
+        assertNotNull(user);
+        assertEquals("puccini", user.getUsername());
+        assertFalse(user.isSuspended());
+        assertFalse(user.isMustChangePassword());
+        assertEquals("active", user.getStatus());
+        assertEquals(CipherAlgorithm.SHA1, user.getCipherAlgorithm());
+        assertEquals("e4c28e7a-9dbf-4ee7-9441-93812a0d4a28", user.getRealm().getKey());
+        assertNull(user.getSecurityQuestion());
+        assertNull(user.getSecurityAnswer());
+        assertEquals("admin", user.getCreator());
+        assertEquals("Giacomo", user.getPlainAttr("firstname").get().getValuesAsStrings().get(0));
+        assertEquals("Puccini", user.getPlainAttr("surname").get().getValuesAsStrings().get(0));
+    }
+
+    @Test
     public void findAll() {
         List<User> list = userDAO.findAll(1, 100);
         assertEquals(5, list.size());
@@ -110,8 +130,8 @@ public class UserTest extends AbstractTest {
     }
 
     @Test
-    public void findByPlainAttrValue() {
-        UPlainAttrValue fullnameValue = entityFactory.newEntity(UPlainAttrValue.class);
+    public void findByPlainAttrUniqueValue() {
+        UPlainAttrUniqueValue fullnameValue = entityFactory.newEntity(UPlainAttrUniqueValue.class);
         fullnameValue.setStringValue("Gioacchino Rossini");
 
         List<User> list = userDAO.findByPlainAttrValue("fullname", fullnameValue, false);
@@ -152,6 +172,13 @@ public class UserTest extends AbstractTest {
     }
 
     @Test
+    public void findMembership() {
+        UMembership memb = userDAO.findMembership("3d5e91f6-305e-45f9-ad30-4897d3d43bd9");
+        assertNotNull(memb);
+        assertEquals("1417acbe-cbf6-4277-9372-e75e04f97000", memb.getLeftEnd().getKey());
+    }
+
+    @Test
     public void saveInvalidPassword() {
         User user = entityFactory.newEntity(User.class);
         user.setUsername("username");
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConfTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConfTest.java
index 695bb87..17e35bb 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConfTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConfTest.java
@@ -24,9 +24,11 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttrUniqueValue;
+import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttrValue;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
-import org.apache.syncope.core.persistence.jpa.entity.conf.JPACPlainAttrValue;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
@@ -41,12 +43,12 @@ public class ConfTest extends AbstractTest {
     private PlainSchemaDAO plainSchemaDAO;
 
     private void add(final CPlainAttr newAttr, final String value) {
-        JPACPlainAttrValue attrValue;
+        PlainAttrValue attrValue;
         if (newAttr.getSchema().isUniqueConstraint()) {
-            attrValue = new JPACPlainAttrValue();
+            attrValue = entityFactory.newEntity(CPlainAttrUniqueValue.class);
             ((PlainAttrUniqueValue) attrValue).setSchema(newAttr.getSchema());
         } else {
-            attrValue = new JPACPlainAttrValue();
+            attrValue = entityFactory.newEntity(CPlainAttrValue.class);
         }
         newAttr.add(value, attrValue);
     }
@@ -68,5 +70,4 @@ public class ConfTest extends AbstractTest {
         CPlainAttr actual = confDAO.find("token.expireTime").get();
         assertEquals(expireTime, actual);
     }
-
 }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java
index 7f424fd..e596e2d 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java
@@ -38,8 +38,6 @@ import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntit
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
@@ -84,12 +82,6 @@ public class GroupTest extends AbstractTest {
     private PlainSchemaDAO plainSchemaDAO;
 
     @Autowired
-    private PlainAttrDAO plainAttrDAO;
-
-    @Autowired
-    private PlainAttrValueDAO plainAttrValueDAO;
-
-    @Autowired
     private AnyTypeClassDAO anyTypeClassDAO;
 
     @Test
@@ -169,10 +161,8 @@ public class GroupTest extends AbstractTest {
 
         assertNull(groupDAO.find("b1f7c12d-ec83-441f-a50e-1691daaedf3b"));
         assertEquals(userDAO.findAllGroups(userDAO.findByUsername("verdi")).size(), 2);
-        assertNull(plainAttrDAO.find(
-                "f82fc61f-8e74-4a4b-9f9e-b8a41f38aad9", GPlainAttr.class));
-        assertNull(plainAttrValueDAO.find(
-                "49f35879-2510-4f11-a901-24152f753538", GPlainAttrValue.class));
+        assertNull(findPlainAttr("f82fc61f-8e74-4a4b-9f9e-b8a41f38aad9", GPlainAttr.class));
+        assertNull(findPlainAttrValue("49f35879-2510-4f11-a901-24152f753538", GPlainAttrValue.class));
         assertNotNull(plainSchemaDAO.find("icon"));
     }
 
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainAttrTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainAttrTest.java
index 8eb98af..58771ca 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainAttrTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainAttrTest.java
@@ -18,18 +18,23 @@
  */
 package org.apache.syncope.core.persistence.jpa.outer;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.fail;
 
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
 import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue;
 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;
 
+@Tag("plainAttrTable")
 @Transactional("Master")
 public class PlainAttrTest extends AbstractTest {
 
@@ -40,30 +45,31 @@ public class PlainAttrTest extends AbstractTest {
     private PlainAttrValueDAO plainAttrValueDAO;
 
     @Test
-    public void deleteAttribute() {
-        plainAttrDAO.delete("35f407a2-d254-4890-9e45-5a7dd8c8df7d", UPlainAttr.class);
+    public void deleteAttr() {
+        plainAttrDAO.delete(findPlainAttr("35f407a2-d254-4890-9e45-5a7dd8c8df7d", UPlainAttr.class));
 
         plainAttrDAO.flush();
 
-        assertNull(plainAttrDAO.find("35f407a2-d254-4890-9e45-5a7dd8c8df7d", UPlainAttr.class));
-        assertNull(
-                plainAttrValueDAO.find("0c67225a-030a-4c56-b337-17cf7a311f0f", UPlainAttrValue.class));
+        assertNull(findPlainAttr("35f407a2-d254-4890-9e45-5a7dd8c8df7d", UPlainAttr.class));
+        assertNull(findPlainAttrValue("0c67225a-030a-4c56-b337-17cf7a311f0f", UPlainAttrValue.class));
     }
 
     @Test
-    public void deleteAttributeValue() {
-        UPlainAttrValue value = plainAttrValueDAO.find(
-                "7034de3b-3687-4db5-8454-363468f1a9de", UPlainAttrValue.class);
-        int attributeValueNumber = value.getAttr().getValues().size();
+    public void deleteAllAttValues() {
+        UPlainAttrValue value = findPlainAttrValue("7034de3b-3687-4db5-8454-363468f1a9de", UPlainAttrValue.class);
+        assertNotNull(value);
 
-        plainAttrValueDAO.delete(value.getKey(), UPlainAttrValue.class);
+        plainAttrValueDAO.deleteAll(value.getAttr(), anyUtilsFactory.getInstance(AnyTypeKind.USER));
 
-        plainAttrValueDAO.flush();
+        value = findPlainAttrValue("7034de3b-3687-4db5-8454-363468f1a9de", UPlainAttrValue.class);
+        assertNull(value);
 
-        assertNull(plainAttrValueDAO.find(value.getKey(), UPlainAttrValue.class));
-
-        UPlainAttr attribute = plainAttrDAO.find(
-                "9d0d9e40-1b18-488e-9482-37dab82163c9", UPlainAttr.class);
-        assertEquals(attribute.getValues().size(), attributeValueNumber - 1);
+        // by removing all values, the related attribute is not valid any more
+        try {
+            plainAttrValueDAO.flush();
+            fail();
+        } catch (InvalidEntityException e) {
+            assertNotNull(e);
+        }
     }
 }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java
index faaede5..39aa303 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java
@@ -37,7 +37,6 @@ import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
@@ -74,9 +73,6 @@ public class PlainSchemaTest extends AbstractTest {
     private DerSchemaDAO derSchemaDAO;
 
     @Autowired
-    private PlainAttrDAO plainAttrDAO;
-
-    @Autowired
     private ExternalResourceDAO resourceDAO;
 
     @BeforeAll
@@ -110,7 +106,7 @@ public class PlainSchemaTest extends AbstractTest {
             plainSchemaDAO.flush();
             fail("This should not happen");
         } catch (Exception e) {
-            assertTrue(e instanceof EntityExistsException);
+            assertTrue(e instanceof EntityExistsException || e.getCause() instanceof EntityExistsException);
         }
     }
 
@@ -173,8 +169,8 @@ public class PlainSchemaTest extends AbstractTest {
         }
         assertTrue(mapItems.isEmpty());
 
-        assertNull(plainAttrDAO.find("01f22fbd-b672-40af-b528-686d9b27ebc4", UPlainAttr.class));
-        assertNull(plainAttrDAO.find(UUID.randomUUID().toString(), UPlainAttr.class));
+        assertNull(findPlainAttr("01f22fbd-b672-40af-b528-686d9b27ebc4", UPlainAttr.class));
+        assertNull(findPlainAttr(UUID.randomUUID().toString(), UPlainAttr.class));
         assertFalse(userDAO.findByUsername("rossini").getPlainAttr("fullname").isPresent());
         assertFalse(userDAO.findByUsername("vivaldi").getPlainAttr("fullname").isPresent());
     }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
index 561e089..e6281b9 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
@@ -32,8 +32,6 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO;
@@ -68,12 +66,6 @@ public class UserTest extends AbstractTest {
     private PlainSchemaDAO plainSchemaDAO;
 
     @Autowired
-    private PlainAttrDAO plainAttrDAO;
-
-    @Autowired
-    private PlainAttrValueDAO plainAttrValueDAO;
-
-    @Autowired
     private DerSchemaDAO derSchemaDAO;
 
     @Test
@@ -86,8 +78,8 @@ public class UserTest extends AbstractTest {
         userDAO.flush();
 
         assertNull(userDAO.findByUsername("bellini"));
-        assertNull(plainAttrDAO.find(UUID.randomUUID().toString(), UPlainAttr.class));
-        assertNull(plainAttrValueDAO.find(UUID.randomUUID().toString(), UPlainAttrValue.class));
+        assertNull(findPlainAttr(UUID.randomUUID().toString(), UPlainAttr.class));
+        assertNull(findPlainAttrValue(UUID.randomUUID().toString(), UPlainAttrValue.class));
         assertNotNull(plainSchemaDAO.find("loginDate"));
 
         memberships = groupDAO.findUMemberships(groupDAO.findByName("managingDirector"));
@@ -103,7 +95,7 @@ public class UserTest extends AbstractTest {
                 "bf825fe1-7320-4a54-bd64-143b5c18ab97",
                 user.getMemberships().get(0).getRightEnd().getKey());
 
-        user.getMemberships().remove(0);
+        user.remove(user.getMemberships().get(0));
 
         UMembership newM = entityFactory.newEntity(UMembership.class);
         newM.setLeftEnd(user);
@@ -202,7 +194,7 @@ public class UserTest extends AbstractTest {
         user = userDAO.findByUsername("vivaldi");
         assertEquals(1, user.getMemberships().size());
 
-        final UMembership newM = user.getMembership(groupDAO.findByName("additional").getKey()).get();
+        UMembership newM = user.getMembership(groupDAO.findByName("additional").getKey()).get();
         assertEquals(1, user.getPlainAttrs(newM).size());
 
         assertNull(user.getPlainAttr("obscure").get().getMembership());
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java
index 7f2297c..4dfc322 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java
@@ -55,11 +55,11 @@ public class XMLContentExporterTest extends AbstractTest {
 
         List<String> realms = IOUtils.readLines(
                 IOUtils.toInputStream(exported, Charset.defaultCharset()), Charset.defaultCharset()).stream().
-                filter(row -> row.trim().startsWith("<REALM")).collect(Collectors.toList());
+                filter(row -> StringUtils.startsWithIgnoreCase(row.trim(), "<REALM")).collect(Collectors.toList());
         assertEquals(4, realms.size());
-        assertTrue(realms.get(0).contains("NAME=\"/\""));
-        assertTrue(realms.get(1).contains("NAME=\"odd\""));
-        assertTrue(realms.get(2).contains("NAME=\"even\""));
-        assertTrue(realms.get(3).contains("NAME=\"two\""));
+        assertTrue(StringUtils.containsIgnoreCase(realms.get(0), "NAME=\"/\""));
+        assertTrue(StringUtils.containsIgnoreCase(realms.get(1), "NAME=\"odd\""));
+        assertTrue(StringUtils.containsIgnoreCase(realms.get(2), "NAME=\"even\""));
+        assertTrue(StringUtils.containsIgnoreCase(realms.get(3), "NAME=\"two\""));
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
index c7499a8..528b88c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
@@ -296,14 +296,10 @@ abstract class AbstractAnyDataBinder {
                             && !patch.getAttrTO().getValues().isEmpty()
                             && !patch.getAttrTO().getValues().get(0).equals(attr.getUniqueValue().getValueAsString())) {
 
-                        plainAttrValueDAO.delete(attr.getUniqueValue().getKey(), anyUtils.plainAttrUniqueValueClass());
+                        plainAttrValueDAO.deleteAll(attr, anyUtils);
                     }
                 } else {
-                    Collection<String> valuesToBeRemoved = attr.getValues().stream().
-                            map(value -> value.getKey()).collect(Collectors.toSet());
-                    valuesToBeRemoved.forEach(attrValueKey -> {
-                        plainAttrValueDAO.delete(attrValueKey, anyUtils.plainAttrValueClass());
-                    });
+                    plainAttrValueDAO.deleteAll(attr, anyUtils);
                 }
 
                 // 1.2 add values
@@ -324,7 +320,7 @@ abstract class AbstractAnyDataBinder {
             case DELETE:
             default:
                 any.remove(attr);
-                plainAttrDAO.delete(attr.getKey(), anyUtils.plainAttrClass());
+                plainAttrDAO.delete(attr);
         }
 
         resources.stream().
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 3bab7fe..f772503 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
@@ -339,7 +339,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
         anyObjectPatch.getMemberships().stream().
                 filter((membPatch) -> (membPatch.getGroup() != null)).forEachOrdered(membPatch -> {
             anyObject.getMembership(membPatch.getGroup()).ifPresent(membership -> {
-                anyObject.getMemberships().remove(membership);
+                anyObject.remove(membership);
                 membership.setLeftEnd(null);
                 anyObject.getPlainAttrs(membership).forEach(attr -> {
                     anyObject.remove(attr);
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 e50f59e..055f7aa 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
@@ -412,7 +412,7 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
         userPatch.getMemberships().stream().
                 filter(membPatch -> membPatch.getGroup() != null).forEachOrdered((membPatch) -> {
             user.getMembership(membPatch.getGroup()).ifPresent(membership -> {
-                user.getMemberships().remove(membership);
+                user.remove(membership);
                 membership.setLeftEnd(null);
                 user.getPlainAttrs(membership).forEach(attr -> {
                     user.remove(attr);
diff --git a/docker/core/src/main/resources/persistence.properties b/docker/core/src/main/resources/persistence.properties
index 6df8091..b656eb2 100644
--- a/docker/core/src/main/resources/persistence.properties
+++ b/docker/core/src/main/resources/persistence.properties
@@ -15,5 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 content.directory=/etc/apache-syncope
+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=${OPENJPA_REMOTE_COMMIT}
diff --git a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
index a3048a1..194c758 100644
--- a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
+++ b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
@@ -15,5 +15,12 @@
 # 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.ElasticsearchAnySearchDAO
+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
diff --git a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
index a3048a1..194c758 100644
--- a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
+++ b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
@@ -15,5 +15,12 @@
 # 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.ElasticsearchAnySearchDAO
+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