You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by an...@apache.org on 2019/05/21 14:00:07 UTC

[syncope] branch 2_1_X updated: [SYNCOPE-1472] fixed multiple entries creation in relationship tables through list checks and constraints on many yo many relationships

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

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


The following commit(s) were added to refs/heads/2_1_X by this push:
     new 5664ec9  [SYNCOPE-1472] fixed multiple entries creation in relationship tables through list checks and constraints on many yo many relationships
5664ec9 is described below

commit 5664ec94ccb233c6acc59748863dc09b386a2604
Author: Andrea Patricelli <an...@apache.org>
AuthorDate: Tue May 21 09:45:28 2019 +0200

    [SYNCOPE-1472] fixed multiple entries creation in relationship tables through list checks and constraints on many yo many relationships
---
 .../core/persistence/jpa/entity/JPAAnyType.java    |  7 ++-
 .../core/persistence/jpa/entity/JPARealm.java      |  9 ++--
 .../core/persistence/jpa/entity/JPAReport.java     |  5 ++-
 .../core/persistence/jpa/entity/JPARole.java       | 17 +++++---
 .../jpa/entity/anyobject/JPAAnyObject.java         | 11 +++--
 .../persistence/jpa/entity/group/JPAGroup.java     | 11 +++--
 .../jpa/entity/group/JPATypeExtension.java         |  3 +-
 .../jpa/entity/policy/JPAAccountPolicy.java        |  9 +++-
 .../jpa/entity/policy/JPAPasswordPolicy.java       |  5 ++-
 .../jpa/entity/resource/JPAExternalResource.java   |  5 ++-
 .../jpa/entity/resource/JPAMappingItem.java        |  5 ++-
 .../jpa/entity/resource/JPAOrgUnitItem.java        |  5 ++-
 .../jpa/entity/resource/JPAProvision.java          |  4 +-
 .../persistence/jpa/entity/task/JPAPullTask.java   |  5 ++-
 .../persistence/jpa/entity/task/JPAPushTask.java   |  5 ++-
 .../core/persistence/jpa/entity/user/JPAUser.java  | 14 +++---
 .../jpa/entity/JPAOIDCProviderItem.java            |  8 +++-
 .../persistence/jpa/entity/JPASAML2IdPItem.java    |  8 +++-
 .../apache/syncope/fit/core/AnyObjectITCase.java   | 33 ++++++++++++++
 .../org/apache/syncope/fit/core/AnyTypeITCase.java | 19 ++++++++
 .../org/apache/syncope/fit/core/GroupITCase.java   | 36 +++++++++++++++
 .../org/apache/syncope/fit/core/RealmITCase.java   | 19 ++++++++
 .../org/apache/syncope/fit/core/RoleITCase.java    | 28 ++++++++++++
 .../apache/syncope/fit/core/UserIssuesITCase.java  | 51 ++++++++++++++++++++++
 24 files changed, 284 insertions(+), 38 deletions(-)

diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java
index d9025bc..1af918d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java
@@ -29,6 +29,7 @@ import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
@@ -53,7 +54,9 @@ public class JPAAnyType extends AbstractProvidedKeyEntity implements AnyType {
     @JoinTable(joinColumns =
             @JoinColumn(name = "anyType_id", referencedColumnName = "id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id", referencedColumnName = "id"))
+            @JoinColumn(name = "anyTypeClass_id", referencedColumnName = "id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "anyType_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> classes = new ArrayList<>();
 
     @Override
@@ -69,7 +72,7 @@ public class JPAAnyType extends AbstractProvidedKeyEntity implements AnyType {
     @Override
     public boolean add(final AnyTypeClass anyTypeClass) {
         checkType(anyTypeClass, JPAAnyTypeClass.class);
-        return this.classes.add((JPAAnyTypeClass) anyTypeClass);
+        return classes.contains((JPAAnyTypeClass) anyTypeClass) || this.classes.add((JPAAnyTypeClass) anyTypeClass);
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
index 49bb739..7e1a476 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
@@ -77,7 +77,9 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm {
             joinColumns =
             @JoinColumn(name = "realm_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "realm_id", "implementation_id" }))
     private List<JPAImplementation> actions = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "realm")
@@ -87,7 +89,8 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm {
     @JoinTable(joinColumns =
             @JoinColumn(name = "realm_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "realm_id", "resource_id" }))
     private List<JPAExternalResource> resources = new ArrayList<>();
 
     @Override
@@ -173,7 +176,7 @@ public class JPARealm extends AbstractGeneratedKeyEntity implements Realm {
     @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
-        return resources.add((JPAExternalResource) resource);
+        return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource);
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
index bed6e36..df3317f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
@@ -30,6 +30,7 @@ import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
@@ -55,7 +56,9 @@ public class JPAReport extends AbstractGeneratedKeyEntity implements Report {
             joinColumns =
             @JoinColumn(name = "report_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "report_id", "implementation_id" }))
     private List<JPAImplementation> reportlets = new ArrayList<>();
 
     private String cronExpression;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
index 4dbddbf..2a0269a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
@@ -36,6 +36,7 @@ import javax.persistence.Lob;
 import javax.persistence.ManyToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.Valid;
 import org.apache.syncope.core.persistence.api.entity.Application;
 import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership;
@@ -67,7 +68,9 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @JoinTable(joinColumns =
             @JoinColumn(name = "role_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "realm_id"))
+            @JoinColumn(name = "realm_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "role_id", "realm_id" }))
     @Valid
     private List<JPARealm> realms = new ArrayList<>();
 
@@ -75,7 +78,9 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @JoinTable(joinColumns =
             @JoinColumn(name = "role_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "dynamicRealm_id"))
+            @JoinColumn(name = "dynamicRealm_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "role_id", "dynamicRealm_id" }))
     @Valid
     private List<JPADynRealm> dynRealms = new ArrayList<>();
 
@@ -90,7 +95,9 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @JoinTable(joinColumns =
             @JoinColumn(name = "role_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "privilege_id"))
+            @JoinColumn(name = "privilege_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "role_id", "privilege_id" }))
     @Valid
     private Set<JPAPrivilege> privileges = new HashSet<>();
 
@@ -102,7 +109,7 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @Override
     public boolean add(final Realm realm) {
         checkType(realm, JPARealm.class);
-        return realms.add((JPARealm) realm);
+        return realms.contains((JPARealm) realm) || realms.add((JPARealm) realm);
     }
 
     @Override
@@ -113,7 +120,7 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
     @Override
     public boolean add(final DynRealm dynamicRealm) {
         checkType(dynamicRealm, JPADynRealm.class);
-        return dynRealms.add((JPADynRealm) dynamicRealm);
+        return dynRealms.contains((JPADynRealm) dynamicRealm) || dynRealms.add((JPADynRealm) dynamicRealm);
     }
 
     @Override
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 0a04e01..91af63a 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
@@ -32,6 +32,7 @@ import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
@@ -75,14 +76,16 @@ public class JPAAnyObject
     @JoinTable(joinColumns =
             @JoinColumn(name = "anyObject_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "anyObject_id", "resource_id" }))
     private List<JPAExternalResource> resources = new ArrayList<>();
 
     @ManyToMany(fetch = FetchType.LAZY)
     @JoinTable(joinColumns =
             @JoinColumn(name = "anyObject_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "anyObject_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd")
@@ -117,7 +120,7 @@ public class JPAAnyObject
     @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
-        return resources.add((JPAExternalResource) resource);
+        return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource);
     }
 
     @Override
@@ -139,7 +142,7 @@ public class JPAAnyObject
     @Override
     public boolean add(final AnyTypeClass auxClass) {
         checkType(auxClass, JPAAnyTypeClass.class);
-        return this.auxClasses.add((JPAAnyTypeClass) auxClass);
+        return auxClasses.contains((JPAAnyTypeClass) auxClass) || this.auxClasses.add((JPAAnyTypeClass) auxClass);
     }
 
     @Override
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 7c6724a..5f1da30 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
@@ -32,6 +32,7 @@ import javax.persistence.ManyToMany;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
@@ -79,14 +80,16 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     @JoinTable(joinColumns =
             @JoinColumn(name = "group_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "group_id", "resource_id" }))
     private List<JPAExternalResource> resources = new ArrayList<>();
 
     @ManyToMany(fetch = FetchType.LAZY)
     @JoinTable(joinColumns =
             @JoinColumn(name = "group_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "group_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "group")
@@ -122,7 +125,7 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
-        return resources.add((JPAExternalResource) resource);
+        return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource);
     }
 
     @Override
@@ -190,7 +193,7 @@ public class JPAGroup extends AbstractAny<GPlainAttr> implements Group {
     @Override
     public boolean add(final AnyTypeClass auxClass) {
         checkType(auxClass, JPAAnyTypeClass.class);
-        return this.auxClasses.add((JPAAnyTypeClass) auxClass);
+        return auxClasses.contains((JPAAnyTypeClass) auxClass) || this.auxClasses.add((JPAAnyTypeClass) auxClass);
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java
index 648436e..207096a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPATypeExtension.java
@@ -54,7 +54,8 @@ public class JPATypeExtension extends AbstractGeneratedKeyEntity implements Type
     @JoinTable(joinColumns =
             @JoinColumn(name = "typeExtension_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "typeExtension_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
index 24b1a2e..b3dd6a0 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
@@ -28,6 +28,7 @@ import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
@@ -54,7 +55,9 @@ public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
             joinColumns =
             @JoinColumn(name = "policy_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "policy_id", "implementation_id" }))
     private List<JPAImplementation> rules = new ArrayList<>();
 
     /**
@@ -64,7 +67,9 @@ public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
     @JoinTable(joinColumns =
             @JoinColumn(name = "accountPolicy_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "accountPolicy_id", "resource_id" }))
     private Set<JPAExternalResource> resources = new HashSet<>();
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
index c3de4e2..2e2ba98 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
@@ -26,6 +26,7 @@ import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
@@ -50,7 +51,9 @@ public class JPAPasswordPolicy extends AbstractPolicy implements PasswordPolicy
             joinColumns =
             @JoinColumn(name = "policy_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "policy_id", "implementation_id" }))
     private List<JPAImplementation> rules = new ArrayList<>();
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
index b44035e..5af5c09 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
@@ -41,6 +41,7 @@ import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.NotNull;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
@@ -159,7 +160,9 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex
             joinColumns =
             @JoinColumn(name = "resource_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "resource_id", "implementation_id" }))
     private List<JPAImplementation> propagationActions = new ArrayList<>();
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java
index 1f55c8d..8cfa6b1 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAMappingItem.java
@@ -28,6 +28,7 @@ import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
@@ -51,7 +52,9 @@ public class JPAMappingItem extends AbstractItem implements MappingItem {
             joinColumns =
             @JoinColumn(name = "item_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "item_id", "implementation_id" }))
     private List<JPAImplementation> transformers = new ArrayList<>();
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnitItem.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnitItem.java
index 49b8cdd..9911d39 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnitItem.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAOrgUnitItem.java
@@ -28,6 +28,7 @@ import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
@@ -51,7 +52,9 @@ public class JPAOrgUnitItem extends AbstractItem implements OrgUnitItem {
             joinColumns =
             @JoinColumn(name = "item_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "item_id", "implementation_id" }))
     private List<JPAImplementation> transformers = new ArrayList<>();
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java
index df194b4..331e68b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java
@@ -69,7 +69,9 @@ public class JPAProvision extends AbstractGeneratedKeyEntity implements Provisio
     @JoinTable(joinColumns =
             @JoinColumn(name = "provision_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "provision_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @Lob
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java
index 3dc7ee6..fdc4221 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java
@@ -33,6 +33,7 @@ import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
+import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.common.lib.types.PullMode;
@@ -65,7 +66,9 @@ public class JPAPullTask extends AbstractProvisioningTask implements PullTask {
             joinColumns =
             @JoinColumn(name = "task_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "task_id", "implementation_id" }))
     private List<JPAImplementation> actions = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pullTask")
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
index abadd8c..b899212 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
@@ -30,6 +30,7 @@ import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
+import javax.persistence.UniqueConstraint;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
@@ -53,7 +54,9 @@ public class JPAPushTask extends AbstractProvisioningTask implements PushTask {
             joinColumns =
             @JoinColumn(name = "task_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "task_id", "implementation_id" }))
     private List<JPAImplementation> actions = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pushTask")
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 4d91360..f580e91 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
@@ -42,6 +42,7 @@ import javax.persistence.Table;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.persistence.Transient;
+import javax.persistence.UniqueConstraint;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
@@ -88,7 +89,8 @@ public class JPAUser
     @JoinTable(joinColumns =
             @JoinColumn(name = "user_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "role_id"))
+            @JoinColumn(name = "role_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "user_id", "role_id" }))
     private List<JPARole> roles = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
@@ -152,14 +154,16 @@ public class JPAUser
     @JoinTable(joinColumns =
             @JoinColumn(name = "user_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "resource_id"))
+            @JoinColumn(name = "resource_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "user_id", "resource_id" }))
     private List<JPAExternalResource> resources = new ArrayList<>();
 
     @ManyToMany(fetch = FetchType.LAZY)
     @JoinTable(joinColumns =
             @JoinColumn(name = "user_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "anyTypeClass_id"))
+            @JoinColumn(name = "anyTypeClass_id"),
+            uniqueConstraints = @UniqueConstraint(columnNames = { "user_id", "anyTypeClass_id" }))
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd")
@@ -189,7 +193,7 @@ public class JPAUser
     @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
-        return resources.add((JPAExternalResource) resource);
+        return resources.contains((JPAExternalResource) resource) || resources.add((JPAExternalResource) resource);
     }
 
     @Override
@@ -419,7 +423,7 @@ public class JPAUser
     @Override
     public boolean add(final AnyTypeClass auxClass) {
         checkType(auxClass, JPAAnyTypeClass.class);
-        return this.auxClasses.add((JPAAnyTypeClass) auxClass);
+        return auxClasses.contains((JPAAnyTypeClass) auxClass) || this.auxClasses.add((JPAAnyTypeClass) auxClass);
     }
 
     @Override
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java
index 69268cb..f4d2123 100644
--- a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java
@@ -28,6 +28,7 @@ import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
@@ -62,14 +63,17 @@ public class JPAOIDCProviderItem extends AbstractItem implements OIDCProviderIte
             joinColumns =
             @JoinColumn(name = "item_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "item_id", "implementation_id" }))
     private List<JPAImplementation> transformers = new ArrayList<>();
 
     @Override
     public boolean add(final Implementation transformer) {
         checkType(transformer, JPAImplementation.class);
         checkImplementationType(transformer, ImplementationType.ITEM_TRANSFORMER);
-        return this.transformers.add((JPAImplementation) transformer);
+        return transformers.contains((JPAImplementation) transformer)
+                || this.transformers.add((JPAImplementation) transformer);
     }
 
     @Override
diff --git a/ext/saml2sp/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2IdPItem.java b/ext/saml2sp/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2IdPItem.java
index 6e22a43..75f1cdc 100644
--- a/ext/saml2sp/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2IdPItem.java
+++ b/ext/saml2sp/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2IdPItem.java
@@ -28,6 +28,7 @@ import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.SAML2IdP;
@@ -51,7 +52,9 @@ public class JPASAML2IdPItem extends AbstractItem implements SAML2IdPItem {
             joinColumns =
             @JoinColumn(name = "item_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "item_id", "implementation_id" }))
     private List<JPAImplementation> transformers = new ArrayList<>();
 
     @Override
@@ -69,7 +72,8 @@ public class JPASAML2IdPItem extends AbstractItem implements SAML2IdPItem {
     public boolean add(final Implementation transformer) {
         checkType(transformer, JPAImplementation.class);
         checkImplementationType(transformer, ImplementationType.ITEM_TRANSFORMER);
-        return this.transformers.add((JPAImplementation) transformer);
+        return transformers.contains((JPAImplementation) transformer)
+                || this.transformers.add((JPAImplementation) transformer);
     }
 
     @Override
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
index 36cd40f..4d1dc89 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
@@ -31,6 +31,7 @@ import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.patch.StringPatchItem;
 import org.apache.syncope.common.lib.to.ConnObjectTO;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AttrTO;
@@ -39,6 +40,7 @@ import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.common.rest.api.beans.AnyQuery;
 import org.apache.syncope.fit.AbstractITCase;
@@ -211,4 +213,35 @@ public class AnyObjectITCase extends AbstractITCase {
             assertEquals(ClientExceptionType.InvalidAnyType, e.getType());
         }
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. assign resource-db-scripted again to Canon MF 8030cn and update twice
+        AnyObjectPatch anyObjectPatch = new AnyObjectPatch();
+        anyObjectPatch.setKey("8559d14d-58c2-46eb-a2d4-a7d35161e8f8");
+        anyObjectPatch.getResources().add(new StringPatchItem.Builder().value(RESOURCE_NAME_DBSCRIPTED).build());
+        anyObjectPatch.getAuxClasses().add(new StringPatchItem.Builder().value("csv").build());
+
+        for (int i = 0; i < 2; i++) {
+            updateAnyObject(anyObjectPatch);
+        }
+
+        // 2. remove resources and auxiliary classes
+        anyObjectPatch.getResources().clear();
+        anyObjectPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_DBSCRIPTED)
+                .operation(PatchOperation.DELETE)
+                .build());
+        anyObjectPatch.getAuxClasses().clear();
+        anyObjectPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .value("csv")
+                .operation(PatchOperation.DELETE)
+                .build());
+
+        updateAnyObject(anyObjectPatch);
+
+        AnyObjectTO printer = anyObjectService.read("8559d14d-58c2-46eb-a2d4-a7d35161e8f8");
+        assertFalse(printer.getResources().contains(RESOURCE_NAME_DBSCRIPTED), "Should not contain removed resources");
+        assertFalse(printer.getAuxClasses().contains("csv"), "Should not contain removed auxiliary classes");
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java
index 28ae442..e5eb88d 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyTypeITCase.java
@@ -178,4 +178,23 @@ public class AnyTypeITCase extends AbstractITCase {
             anyTypeService.update(group);
         }
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. add any type class csv twice to PRINTER any type
+        AnyTypeTO anyTypeTO = anyTypeService.read("PRINTER");
+        anyTypeTO.getClasses().clear();
+        anyTypeTO.getClasses().add("minimal printer");
+        anyTypeTO.getClasses().add("csv");
+        anyTypeTO.getClasses().add("csv");
+        anyTypeService.update(anyTypeTO);
+
+        // 2. read again and remove any type class
+        anyTypeTO = anyTypeService.read("PRINTER");
+        anyTypeTO.getClasses().remove("csv");
+        anyTypeService.update(anyTypeTO);
+
+        assertFalse(anyTypeService.read("PRINTER").getClasses().contains("csv"), 
+                "Should not contain removed any type classes");
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
index 33cbe38..95071aa 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
@@ -1155,4 +1155,40 @@ public class GroupITCase extends AbstractITCase {
             }
         }
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. update group artDirector by assigning twice resource-testdb and auxiliary class csv
+        GroupPatch groupPatch = new GroupPatch();
+        groupPatch.setKey("ece66293-8f31-4a84-8e8d-23da36e70846");
+        groupPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_TESTDB)
+                .operation(PatchOperation.ADD_REPLACE)
+                .build());
+        groupPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .operation(PatchOperation.ADD_REPLACE)
+                .value("csv")
+                .build());
+        for (int i = 0; i < 2; i++) {
+            updateGroup(groupPatch);
+        }
+
+        // 2. remove resources and auxiliary classes
+        groupPatch.getResources().clear();
+        groupPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_TESTDB)
+                .operation(PatchOperation.DELETE)
+                .build());
+        groupPatch.getAuxClasses().clear();
+        groupPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .value("csv")
+                .operation(PatchOperation.DELETE)
+                .build());
+
+        updateGroup(groupPatch);
+
+        GroupTO groupTO = groupService.read("ece66293-8f31-4a84-8e8d-23da36e70846");
+        assertFalse(groupTO.getResources().contains(RESOURCE_NAME_TESTDB), "Should not contain removed resources");
+        assertFalse(groupTO.getAuxClasses().contains("csv"), "Should not contain removed auxiliary classes");
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
index f62f2f9..c250c47 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
@@ -268,4 +268,23 @@ public class RealmITCase extends AbstractITCase {
         assertNull(
                 getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, "ou=test,o=isp"));
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. assign twice resource-ldap-orgunit to /odd
+        RealmTO realmTO = realmService.list("/odd").get(0);
+        realmTO.getResources().clear();
+        realmTO.getResources().add("resource-ldap-orgunit");
+        realmTO.getResources().add("resource-ldap-orgunit");
+        realmTO = realmService.update(realmTO).readEntity(new GenericType<ProvisioningResult<RealmTO>>() {
+        }).getEntity();
+
+        // 2. remove resource-ldap-orgunit resource
+        realmTO.getResources().remove("resource-ldap-orgunit");
+
+        realmTO = realmService.update(realmTO).readEntity(new GenericType<ProvisioningResult<RealmTO>>() {
+        }).getEntity();
+
+        assertFalse(realmTO.getResources().contains("resource-ldap-orgunit"), "Should not contain removed resources");
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java
index 44a3d5c..82649fc 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java
@@ -28,8 +28,10 @@ import java.util.List;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.DynRealmTO;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.FlowableEntitlement;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
@@ -146,4 +148,30 @@ public class RoleITCase extends AbstractITCase {
         assertTrue(bellini.getDynMemberships().isEmpty());
         assertTrue(bellini.getPrivileges().isEmpty());
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        final DynRealmTO dynRealmTO = new DynRealmTO();
+        dynRealmTO.setKey("dynRealm");
+        dynRealmTO.getDynMembershipConds().put(AnyTypeKind.USER.name(), "username=~rossini");
+        dynRealmService.create(dynRealmTO);
+
+        // 1. associate role Other again to /odd realm and twice to dynRealm
+        RoleTO roleTO = roleService.read("Other");
+        roleTO.getRealms().add("/odd");
+        roleTO.getDynRealms().add("dynRealm");
+        roleTO.getDynRealms().add("dynRealm");
+        roleService.update(roleTO);
+
+        // 2. update by removing realm and dynamic realm
+        roleTO = roleService.read("Other");
+        roleTO.getRealms().remove("/odd");
+        roleTO.getDynRealms().remove("dynRealm");
+        roleService.update(roleTO);
+
+        roleTO = roleService.read("Other");
+
+        assertFalse(roleTO.getRealms().contains("/odd"), "Should not contain removed realms");
+        assertFalse(roleTO.getDynRealms().contains("dynRealm"), "Should not contain removed dynamic realms");
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
index b86fa94..9d2a101 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
@@ -1520,4 +1520,55 @@ public class UserIssuesITCase extends AbstractITCase {
             configurationService.set(original);
         }
     }
+
+    @Test
+    public void issueSYNCOPE1472() {
+        // 1. update user rossini by assigning twice resource-testdb2 and auxiliary class csv
+        UserPatch userPatch = new UserPatch();
+        userPatch.setKey("1417acbe-cbf6-4277-9372-e75e04f97000");
+        userPatch.setPassword(new PasswordPatch.Builder()
+                .onSyncope(false)
+                .resource(RESOURCE_NAME_TESTDB)
+                .value("Password123")
+                .build());
+        userPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_TESTDB)
+                .operation(PatchOperation.ADD_REPLACE)
+                .build());
+        userPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .operation(PatchOperation.ADD_REPLACE)
+                .value("csv")
+                .build());
+        userPatch.getRoles().add(new StringPatchItem.Builder()
+                .operation(PatchOperation.ADD_REPLACE)
+                .value("Other")
+                .build());
+
+        for (int i = 0; i < 2; i++) {
+            updateUser(userPatch);
+        }
+
+        // 2. remove resources, auxiliary classes and roles
+        userPatch.getResources().clear();
+        userPatch.getResources().add(new StringPatchItem.Builder()
+                .value(RESOURCE_NAME_TESTDB)
+                .operation(PatchOperation.DELETE)
+                .build());
+        userPatch.getAuxClasses().clear();
+        userPatch.getAuxClasses().add(new StringPatchItem.Builder()
+                .value("csv")
+                .operation(PatchOperation.DELETE)
+                .build());
+        userPatch.getRoles().clear();
+        userPatch.getRoles().add(new StringPatchItem.Builder()
+                .value("Other")
+                .operation(PatchOperation.DELETE)
+                .build());
+        updateUser(userPatch);
+
+        UserTO userTO = userService.read("1417acbe-cbf6-4277-9372-e75e04f97000");
+        assertFalse(userTO.getResources().contains(RESOURCE_NAME_TESTDB), "Should not contain removed resources");
+        assertFalse(userTO.getAuxClasses().contains("csv"), "Should not contain removed auxiliary classes");
+        assertFalse(userTO.getRoles().contains("Other"),"Should not contain removed roles");
+    }
 }