From 13172b0efae0329ba10f21d50ba53ee296fdf84c Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Sat, 4 Feb 2017 11:42:14 +0100 Subject: [PATCH] OO-2493: optimize the import workflow to reduce the number of DB calls, limit the size of the table --- .../admin/user/UserSearchFlexiController.java | 5 +- .../org/olat/basesecurity/BaseSecurity.java | 8 +- .../basesecurity/BaseSecurityManager.java | 8 +- .../ImportMemberBySearchController.java | 16 +--- ...ortMemberOverviewIdentitiesController.java | 88 +++++++++++++------ .../olat/repository/RepositoryManager.java | 10 ++- .../manager/RepositoryEntryRelationDAO.java | 9 ++ .../basesecurity/BaseSecurityManagerTest.java | 20 +++++ 8 files changed, 118 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/olat/admin/user/UserSearchFlexiController.java b/src/main/java/org/olat/admin/user/UserSearchFlexiController.java index 50f7345d340..072fbabeacc 100644 --- a/src/main/java/org/olat/admin/user/UserSearchFlexiController.java +++ b/src/main/java/org/olat/admin/user/UserSearchFlexiController.java @@ -219,7 +219,7 @@ public class UserSearchFlexiController extends FlexiAutoCompleterController { Translator myTrans = userManager.getPropertyHandlerTranslator(getTranslator()); userTableModel = new UserSearchFlexiTableModel(Collections.<Identity>emptyList(), resultingPropertyHandlers, isAdministrativeUser, getLocale(), tableColumnModel); - tableEl = uifactory.addTableElement(getWindowControl(), "users", userTableModel, myTrans, formLayout); + tableEl = uifactory.addTableElement(getWindowControl(), "users", userTableModel, 250, false, myTrans, formLayout); tableEl.setCustomizeColumns(false); tableEl.setMultiSelect(true); tableEl.setSelectAllEnable(true); @@ -422,7 +422,8 @@ public class UserSearchFlexiController extends FlexiAutoCompleterController { } tableEl.reset(); - List<Identity> users = searchUsers(login, userPropertiesSearch, true); + + List<Identity> users = searchUsers(login, userPropertiesSearch, true); if (!users.isEmpty()) { userTableModel.setObjects(users); flc.contextPut("showButton","true"); diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java index 62c526e1b61..c2445becd78 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurity.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java @@ -173,7 +173,13 @@ public interface BaseSecurity { */ public Identity findIdentityByNumber(String identityNumber); - + /** + * The list of visible identities with a institutional number like in the + * specified list. Deleted ones are not included. + * + * @param identityNumbers + * @return A list of identities + */ public List<Identity> findIdentitiesByNumber(Collection<String> identityNumbers); /** diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java index 2b23870a293..d71ffa54508 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java @@ -991,13 +991,15 @@ public class BaseSecurityManager implements BaseSecurity { if(identityNumbers == null || identityNumbers.isEmpty()) return Collections.emptyList(); StringBuilder sb = new StringBuilder(); - sb.append("select identity from ").append(IdentityImpl.class.getName()).append(" identity ") - .append(" inner join fetch identity.user user ") - .append(" where user.").append(UserConstants.INSTITUTIONALUSERIDENTIFIER).append(" in (:idNumbers) "); + sb.append("select ident from ").append(IdentityImpl.class.getName()).append(" ident ") + .append(" inner join fetch ident.user user ") + .append(" where user.").append(UserConstants.INSTITUTIONALUSERIDENTIFIER).append(" in (:idNumbers) ") + .append(" and ident.status<:status"); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Identity.class) .setParameter("idNumbers", identityNumbers) + .setParameter("status", Identity.STATUS_VISIBLE_LIMIT) .getResultList(); } diff --git a/src/main/java/org/olat/course/member/wizard/ImportMemberBySearchController.java b/src/main/java/org/olat/course/member/wizard/ImportMemberBySearchController.java index d65c4cf6345..d4b86ac953b 100644 --- a/src/main/java/org/olat/course/member/wizard/ImportMemberBySearchController.java +++ b/src/main/java/org/olat/course/member/wizard/ImportMemberBySearchController.java @@ -19,7 +19,6 @@ */ package org.olat.course.member.wizard; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -58,16 +57,11 @@ public class ImportMemberBySearchController extends StepFormBasicController { protected void event(UserRequest ureq, Controller source, Event event) { if(event instanceof SingleIdentityChosenEvent) { SingleIdentityChosenEvent e = (SingleIdentityChosenEvent)event; - String key = e.getChosenIdentity().getKey().toString(); - addToRunContext("keys", Collections.singletonList(key)); + addToRunContext("keyIdentities", Collections.singletonList(e.getChosenIdentity())); fireEvent(ureq, StepsEvent.ACTIVATE_NEXT); } else if(event instanceof MultiIdentityChosenEvent) { MultiIdentityChosenEvent e = (MultiIdentityChosenEvent)event; - List<String> keys = new ArrayList<>(); - for(Identity identity: e.getChosenIdentities()) { - keys.add(identity.getKey().toString()); - } - addToRunContext("keys", keys); + addToRunContext("keyIdentities", e.getChosenIdentities()); fireEvent(ureq, StepsEvent.ACTIVATE_NEXT); } else { super.event(ureq, source, event); @@ -80,11 +74,7 @@ public class ImportMemberBySearchController extends StepFormBasicController { if(identities.isEmpty()) { searchController.doSearch(); } else { - List<String> keys = new ArrayList<>(identities.size()); - for(Identity identity: identities) { - keys.add(identity.getKey().toString()); - } - addToRunContext("keys", keys); + addToRunContext("keyIdentities", identities); fireEvent(ureq, StepsEvent.ACTIVATE_NEXT); } } diff --git a/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewIdentitiesController.java b/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewIdentitiesController.java index 2249ce0eff8..90d8973787b 100644 --- a/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewIdentitiesController.java +++ b/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewIdentitiesController.java @@ -20,7 +20,9 @@ package org.olat.course.member.wizard; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.olat.admin.user.UserTableDataModel; import org.olat.basesecurity.BaseSecurity; @@ -28,7 +30,6 @@ import org.olat.basesecurity.BaseSecurityModule; import org.olat.basesecurity.Constants; import org.olat.basesecurity.SecurityGroup; import org.olat.core.CoreSpringFactory; -import org.olat.core.commons.persistence.PersistenceHelper; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; @@ -47,6 +48,7 @@ import org.olat.core.id.Identity; import org.olat.core.id.UserConstants; import org.olat.user.UserManager; import org.olat.user.propertyhandlers.UserPropertyHandler; +import org.springframework.beans.factory.annotation.Autowired; /** * @@ -60,9 +62,12 @@ public class ImportMemberOverviewIdentitiesController extends StepFormBasicContr private List<String> notfounds; private boolean isAdministrativeUser; - private final UserManager userManager; - private final BaseSecurity securityManager; - private final BaseSecurityModule securityModule; + @Autowired + private UserManager userManager; + @Autowired + private BaseSecurity securityManager; + @Autowired + private BaseSecurityModule securityModule; public ImportMemberOverviewIdentitiesController(UserRequest ureq, WindowControl wControl, Form rootForm, StepsRunContext runContext) { super(ureq, wControl, rootForm, runContext, LAYOUT_VERTICAL, null); @@ -78,6 +83,10 @@ public class ImportMemberOverviewIdentitiesController extends StepFormBasicContr @SuppressWarnings("unchecked") List<String> keys = (List<String>)runContext.get("keys"); loadModel(keys); + } else if(containsRunContextKey("keyIdentities")) { + @SuppressWarnings("unchecked") + List<Identity> keys = (List<Identity>)runContext.get("keyIdentities"); + loadModelByIdentities(keys); } isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles()); @@ -129,28 +138,50 @@ public class ImportMemberOverviewIdentitiesController extends StepFormBasicContr } private void loadModel(List<String> keys) { - oks = new ArrayList<Identity>(); - List<String> isanonymous = new ArrayList<String>(); - notfounds = new ArrayList<String>(); - + notfounds = new ArrayList<>(); + + Set<Identity> okSet = new HashSet<>(); SecurityGroup anonymousSecGroup = securityManager.findSecurityGroupByName(Constants.GROUP_ANONYMOUS); + List<Identity> anonymousUsers = securityManager.getIdentitiesOfSecurityGroup(anonymousSecGroup); + for (String identityKey : keys) { Identity ident = securityManager.loadIdentityByKey(Long.parseLong(identityKey)); if (ident == null) { // not found, add to not-found-list notfounds.add(identityKey); - } else if (securityManager.isIdentityInSecurityGroup(ident, anonymousSecGroup)) { - isanonymous.add(identityKey); - } else if (!PersistenceHelper.containsPersistable(oks, ident)) { - oks.add(ident); + } else if (anonymousUsers.contains(ident)) { + //ignore + } else if (!okSet.contains(ident)) { + okSet.add(ident); } } + oks = new ArrayList<>(okSet); + } + + private void loadModelByIdentities(List<Identity> keys) { + notfounds = new ArrayList<>(); + + Set<Identity> okSet = new HashSet<>(); + SecurityGroup anonymousSecGroup = securityManager.findSecurityGroupByName(Constants.GROUP_ANONYMOUS); + List<Identity> anonymousUsers = securityManager.getIdentitiesOfSecurityGroup(anonymousSecGroup); + + for (Identity ident : keys) { + if (ident == null || anonymousUsers.contains(ident)) { + //ignore + } else if (!okSet.contains(ident)) { + okSet.add(ident); + } + } + oks = new ArrayList<>(okSet); } private void loadModel(String inp) { - oks = new ArrayList<Identity>(); - notfounds = new ArrayList<String>(); + oks = new ArrayList<>(); + notfounds = new ArrayList<>(); + + Set<Identity> okSet = new HashSet<>(); - SecurityGroup anonymousSecGroup = securityManager.findSecurityGroupByName(Constants.GROUP_ANONYMOUS); + SecurityGroup anonymousGroup = securityManager.findSecurityGroupByName(Constants.GROUP_ANONYMOUS); + Set<Identity> anonymousUsers = new HashSet<>(securityManager.getIdentitiesOfSecurityGroup(anonymousGroup)); List<String> identList = new ArrayList<String>(); String[] lines = inp.split("\r?\n"); @@ -168,13 +199,12 @@ public class ImportMemberOverviewIdentitiesController extends StepFormBasicContr if(userIdent != null) { identList.remove(userIdent); } - if (!PersistenceHelper.containsPersistable(oks, identity) - && !securityManager.isIdentityInSecurityGroup(identity, anonymousSecGroup)) { - oks.add(identity); + if (!okSet.contains(identity) && !anonymousUsers.contains(identity)) { + okSet.add(identity); } } // make a lowercase copy of identList for processing username and email - List<String> identListLowercase = new ArrayList<String>(identList.size()); + List<String> identListLowercase = new ArrayList<>(identList.size()); for (String ident:identList) { identListLowercase.add(ident.toLowerCase()); } @@ -182,14 +212,20 @@ public class ImportMemberOverviewIdentitiesController extends StepFormBasicContr List<Identity> identities = securityManager.findIdentitiesByNameCaseInsensitive(identListLowercase); for(Identity identity:identities) { identListLowercase.remove(identity.getName().toLowerCase()); - if (!PersistenceHelper.containsPersistable(oks, identity) - && !securityManager.isIdentityInSecurityGroup(identity, anonymousSecGroup)) { - oks.add(identity); + if (!okSet.contains(identity) && !anonymousUsers.contains(identity)) { + okSet.add(identity); } } //search by email, case insensitive - List<Identity> mailIdentities = userManager.findIdentitiesByEmail(identListLowercase); + List<String> emailListLowercase = new ArrayList<>(identList.size()); + for (String ident:identList) { + if(ident.indexOf('@') > 0) { + emailListLowercase.add(ident.toLowerCase()); + } + } + + List<Identity> mailIdentities = userManager.findIdentitiesByEmail(emailListLowercase); for(Identity identity:mailIdentities) { String email = identity.getUser().getProperty(UserConstants.EMAIL, null); if(email != null) { @@ -199,13 +235,13 @@ public class ImportMemberOverviewIdentitiesController extends StepFormBasicContr if(institutEmail != null) { identListLowercase.remove(institutEmail.toLowerCase()); } - if (!PersistenceHelper.containsPersistable(oks, identity) - && !securityManager.isIdentityInSecurityGroup(identity, anonymousSecGroup)) { - oks.add(identity); + if (!okSet.contains(identity) && !anonymousUsers.contains(identity)) { + okSet.add(identity); } } notfounds.addAll(identListLowercase); + oks = new ArrayList<>(okSet); } public boolean validate() { diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java index 24884835631..fe75173fcd7 100644 --- a/src/main/java/org/olat/repository/RepositoryManager.java +++ b/src/main/java/org/olat/repository/RepositoryManager.java @@ -1811,9 +1811,13 @@ public class RepositoryManager { boolean allOk = repositoryEntryRelationDao.removeMembers(re, members); if (allOk) { + int count = 0; List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>(); for(Identity identity:members) { deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(identity, re)); + if(++count % 100 == 0) { + dbInstance.commitAndCloseSession(); + } } dbInstance.commit(); sendDeferredEvents(deferredEvents, re); @@ -2268,12 +2272,16 @@ public class RepositoryManager { public void updateRepositoryEntryMemberships(Identity ureqIdentity, Roles ureqRoles, RepositoryEntry re, List<RepositoryEntryPermissionChangeEvent> changes, MailPackage mailing) { + int count = 0; List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>(); for(RepositoryEntryPermissionChangeEvent e:changes) { updateRepositoryEntryMembership(ureqIdentity, ureqRoles, re, e, mailing, deferredEvents); + if(++count % 100 == 0) { + dbInstance.commitAndCloseSession(); + } } - dbInstance.commit(); + dbInstance.commitAndCloseSession(); sendDeferredEvents(deferredEvents, re); } diff --git a/src/main/java/org/olat/repository/manager/RepositoryEntryRelationDAO.java b/src/main/java/org/olat/repository/manager/RepositoryEntryRelationDAO.java index 459a6519ba8..6d7bebaca35 100644 --- a/src/main/java/org/olat/repository/manager/RepositoryEntryRelationDAO.java +++ b/src/main/java/org/olat/repository/manager/RepositoryEntryRelationDAO.java @@ -181,6 +181,14 @@ public class RepositoryEntryRelationDAO { return groupDao.removeMemberships(group, role); } + /** + * Retrieve the default group of the repository entry (the one + * marked with the flag defaultGroup=true). The query is cached + * by hibernate 2nd level cache. + * + * @param re The repository entry + * @return The group + */ public Group getDefaultGroup(RepositoryEntryRef re) { StringBuilder sb = new StringBuilder(); sb.append("select baseGroup from ").append(RepositoryEntry.class.getName()).append(" as v ") @@ -190,6 +198,7 @@ public class RepositoryEntryRelationDAO { return dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Group.class) .setParameter("repoKey", re.getKey()) + .setHint("org.hibernate.cacheable", Boolean.TRUE) .getSingleResult(); } diff --git a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java index 91e6fd1d826..e68fb702172 100644 --- a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java +++ b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java @@ -247,6 +247,26 @@ public class BaseSecurityManagerTest extends OlatTestCase { Assert.assertTrue(foundIds.contains(id2)); } + @Test + public void findIdentitiesByNumber() { + //create a user it + String username = "fINd-ME-6-" + UUID.randomUUID(); + String institutionalNumber = UUID.randomUUID().toString(); + Identity id = JunitTestHelper.createAndPersistIdentityAsUser(username); + id.getUser().setProperty(UserConstants.INSTITUTIONALUSERIDENTIFIER, institutionalNumber); + userManager.updateUserFromIdentity(id); + dbInstance.commitAndCloseSession(); + + List<String> numbers = new ArrayList<String>(2); + numbers.add(institutionalNumber); + + //find it + List<Identity> foundIds = securityManager.findIdentitiesByNumber(numbers); + Assert.assertNotNull(foundIds); + Assert.assertEquals(1, foundIds.size()); + Assert.assertTrue(foundIds.contains(id)); + } + @Test public void loadIdentityShortByKey() { //create a user it -- GitLab