Avoid inefficient SQL when deleting a role (#39238)

Closes #39237
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Alexander Schwartz 2025-04-28 11:29:05 +02:00 committed by GitHub
parent 4e95bde179
commit 660217dc41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 16 additions and 6 deletions

View File

@ -458,18 +458,15 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
// So in any case, native query is not an option. This is not optimal as it executes additional queries but
// the alternative of clearing the persistence context is not either as we don't know if something currently present
// in the context is not needed later.
Stream<RoleEntity> parentRoles = em.createNamedQuery("getParentRolesOfACompositeRole", RoleEntity.class).setParameter("compositeRole", roleEntity).getResultStream();
parentRoles.forEach(parentRole -> parentRole.getCompositeRoles().remove(roleEntity));
parentRoles.close();
roleEntity.getParentRoles().forEach(roleEntity1 -> roleEntity1.getCompositeRoles().remove(roleEntity));
em.createNamedQuery("deleteClientScopeRoleMappingByRole").setParameter("role", roleEntity).executeUpdate();
em.flush();
em.remove(roleEntity);
session.getKeycloakSessionFactory().publish(roleRemovedEvent(role));
em.flush();
return true;
}

View File

@ -65,7 +65,6 @@ import java.util.Set;
@NamedQuery(name="searchForRealmRoles", query="select role from RoleEntity role where role.clientRole = false and role.realmId = :realm and ( lower(role.name) like :search or lower(role.description) like :search ) order by role.name"),
@NamedQuery(name="getRoleIdsFromIdList", query="select role.id from RoleEntity role where role.realmId = :realm and role.id in :ids order by role.name ASC"),
@NamedQuery(name="getRoleIdsByNameContainingFromIdList", query="select role.id from RoleEntity role where role.realmId = :realm and lower(role.name) like lower(concat('%',:search,'%')) and role.id in :ids order by role.name ASC"),
@NamedQuery(name="getParentRolesOfACompositeRole", query = "select role from RoleEntity role where :compositeRole member of role.compositeRoles"),
})
public class RoleEntity {
@ -99,6 +98,9 @@ public class RoleEntity {
@JoinTable(name = "COMPOSITE_ROLE", joinColumns = @JoinColumn(name = "COMPOSITE"), inverseJoinColumns = @JoinColumn(name = "CHILD_ROLE"))
private Set<RoleEntity> compositeRoles;
@ManyToMany(mappedBy = "compositeRoles", fetch = FetchType.LAZY, cascade = {})
private Set<RoleEntity> parentRoles;
// Explicitly not using OrphanRemoval as we're handling the removal manually through HQL but at the same time we still
// want to remove elements from the entity's collection in a manual way. Without this, Hibernate would do a duplicit
// delete query.
@ -158,6 +160,17 @@ public class RoleEntity {
return compositeRoles;
}
public Set<RoleEntity> getParentRoles() {
if (parentRoles == null) {
parentRoles = new HashSet<>();
}
return parentRoles;
}
public void setParentRoles(Set<RoleEntity> parentRoles) {
this.parentRoles = parentRoles;
}
public void setCompositeRoles(Set<RoleEntity> compositeRoles) {
this.compositeRoles = compositeRoles;
}