KEYCLOAK-6589: Optimize jpql in User search API
This commit removes the 6 n+1 select that are issued when calling GET /users api. We now have 4 select queries.
This commit is contained in:
parent
9ef1f1b73c
commit
898347366d
2 changed files with 35 additions and 5 deletions
|
@ -59,6 +59,7 @@ import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -945,6 +946,7 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
UserEntity userRef = em.getReference(UserEntity.class, user.getId());
|
UserEntity userRef = em.getReference(UserEntity.class, user.getId());
|
||||||
entity.setUser(userRef);
|
entity.setUser(userRef);
|
||||||
em.persist(entity);
|
em.persist(entity);
|
||||||
|
|
||||||
MultivaluedHashMap<String, String> config = cred.getConfig();
|
MultivaluedHashMap<String, String> config = cred.getConfig();
|
||||||
if (config != null && !config.isEmpty()) {
|
if (config != null && !config.isEmpty()) {
|
||||||
|
|
||||||
|
@ -962,6 +964,11 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserEntity userEntity = userInEntityManagerContext(user.getId());
|
||||||
|
if (userEntity != null) {
|
||||||
|
userEntity.getCredentials().add(entity);
|
||||||
|
}
|
||||||
return toModel(entity);
|
return toModel(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -970,6 +977,10 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
CredentialEntity entity = em.find(CredentialEntity.class, id);
|
CredentialEntity entity = em.find(CredentialEntity.class, id);
|
||||||
if (entity == null) return false;
|
if (entity == null) return false;
|
||||||
em.remove(entity);
|
em.remove(entity);
|
||||||
|
UserEntity userEntity = userInEntityManagerContext(user.getId());
|
||||||
|
if (userEntity != null) {
|
||||||
|
userEntity.getCredentials().remove(entity);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1017,11 +1028,19 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
|
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
|
||||||
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
List<CredentialEntity> results;
|
||||||
|
UserEntity userEntity = userInEntityManagerContext(user.getId());
|
||||||
|
if (userEntity != null) {
|
||||||
|
|
||||||
|
// user already in persistence context, no need to execute a query
|
||||||
|
results = userEntity.getCredentials().stream().filter(it -> it.getType().equals(type)).collect(Collectors.toList());
|
||||||
|
} else {
|
||||||
|
userEntity = em.getReference(UserEntity.class, user.getId());
|
||||||
TypedQuery<CredentialEntity> query = em.createNamedQuery("credentialByUserAndType", CredentialEntity.class)
|
TypedQuery<CredentialEntity> query = em.createNamedQuery("credentialByUserAndType", CredentialEntity.class)
|
||||||
.setParameter("type", type)
|
.setParameter("type", type)
|
||||||
.setParameter("user", userEntity);
|
.setParameter("user", userEntity);
|
||||||
List<CredentialEntity> results = query.getResultList();
|
results = query.getResultList();
|
||||||
|
}
|
||||||
List<CredentialModel> rtn = new LinkedList<>();
|
List<CredentialModel> rtn = new LinkedList<>();
|
||||||
for (CredentialEntity entity : results) {
|
for (CredentialEntity entity : results) {
|
||||||
rtn.add(toModel(entity));
|
rtn.add(toModel(entity));
|
||||||
|
@ -1062,4 +1081,10 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
em.persist(user);
|
em.persist(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserEntity userInEntityManagerContext(String id) {
|
||||||
|
UserEntity user = em.getReference(UserEntity.class, id);
|
||||||
|
boolean isLoaded = em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(user);
|
||||||
|
return isLoaded ? user : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
package org.keycloak.models.jpa.entities;
|
package org.keycloak.models.jpa.entities;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Fetch;
|
||||||
|
import org.hibernate.annotations.FetchMode;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
import javax.persistence.Access;
|
import javax.persistence.Access;
|
||||||
|
@ -90,12 +92,15 @@ public class UserEntity {
|
||||||
protected String realmId;
|
protected String realmId;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
|
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
|
||||||
|
@Fetch(FetchMode.SUBSELECT)
|
||||||
protected Collection<UserAttributeEntity> attributes = new ArrayList<UserAttributeEntity>();
|
protected Collection<UserAttributeEntity> attributes = new ArrayList<UserAttributeEntity>();
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
|
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
|
||||||
|
@Fetch(FetchMode.SUBSELECT)
|
||||||
protected Collection<UserRequiredActionEntity> requiredActions = new ArrayList<UserRequiredActionEntity>();
|
protected Collection<UserRequiredActionEntity> requiredActions = new ArrayList<UserRequiredActionEntity>();
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
|
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
|
||||||
|
@Fetch(FetchMode.SUBSELECT)
|
||||||
protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
|
protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
|
||||||
|
|
||||||
@Column(name="FEDERATION_LINK")
|
@Column(name="FEDERATION_LINK")
|
||||||
|
|
Loading…
Reference in a new issue