88
99import java .util .*;
1010
11+ import com .evolveum .midpoint .repo .sqale .qmodel .focus .QUser ;
12+ import com .evolveum .midpoint .repo .sqale .qmodel .role .MAbstractRole ;
13+ import com .evolveum .midpoint .repo .sqale .qmodel .role .QAbstractRole ;
1114import com .evolveum .midpoint .repo .sqale .qmodel .shadow .QShadow ;
1215import com .evolveum .midpoint .repo .sqlbase .SqlBaseOperationTracker ;
1316
3235
3336public abstract class ReferenceNameResolver {
3437
38+ /**
39+ * Holds resolved name and display name for an object.
40+ * Display name is: fullName for Users, displayName for AbstractRoles, null otherwise.
41+ */
42+ public record ResolvedNames (String name , String displayName ) {}
43+
3544 public abstract <S > S resolve (S object , JdbcSession session );
3645
46+ /**
47+ * Batch resolve names and display names for a set of OIDs.
48+ * Results are cached and can be retrieved via {@link #getResolvedNames(UUID)}.
49+ *
50+ * @param session JDBC session
51+ * @param oids OIDs to resolve
52+ * @param includeDisplayNames if true, also resolve display names (requires LEFT JOIN)
53+ */
54+ public abstract void batchResolve (JdbcSession session , Set <UUID > oids , boolean includeDisplayNames );
55+
56+ /**
57+ * Get cached resolved names for an OID, or null if not resolved.
58+ */
59+ public abstract ResolvedNames getResolvedNames (UUID oid );
60+
3761 public static ReferenceNameResolver from (Collection <SelectorOptions <GetOperationOptions >> options ) {
3862 @ NotNull
3963 List <? extends ItemPath > paths = getPathsToResolve (options );
@@ -71,15 +95,27 @@ private static final class Noop extends ReferenceNameResolver {
7195 public <S > S resolve (S object , JdbcSession session ) {
7296 return object ;
7397 }
98+
99+ @ Override
100+ public void batchResolve (JdbcSession session , Set <UUID > oids , boolean includeDisplayNames ) {
101+ // No-op
102+ }
103+
104+ @ Override
105+ public ResolvedNames getResolvedNames (UUID oid ) {
106+ return null ;
107+ }
74108 }
75109
76110 private static final class Impl extends ReferenceNameResolver {
77111
78112 private final List <? extends ItemPath > paths ;
79- private final Map <UUID , PolyString > uuidToName = new HashMap <>();
80113 private final Set <UUID > oidsToResolve = new HashSet <>();
81114 private final Set <UUID > shadowOidsToResolve = new HashSet <>();
82115
116+ // Unified cache for name and displayName
117+ private final Map <UUID , ResolvedNames > resolvedNamesCache = new HashMap <>();
118+
83119
84120 public Impl (List <? extends ItemPath > paths ) {
85121 super ();
@@ -120,30 +156,31 @@ public <S> S resolve(S object, JdbcSession session) {
120156 }
121157
122158 private void resolveNames (JdbcSession session ) {
123-
124- // TODO: Add batch processing
125159 var object = new QObject <>(MObject .class , "obj" );
126160 var shadow = new QShadow ("s" );
127- resolveNames (session , object , oidsToResolve );
128- resolveNames (session , shadow , shadowOidsToResolve );
129-
161+ resolveNamesSimple (session , object , oidsToResolve );
162+ resolveNamesSimple (session , shadow , shadowOidsToResolve );
130163 }
131- private void resolveNames (JdbcSession session , QObject <?> object , Set <UUID > oidsToResolve ) {
164+
165+ /**
166+ * Simple name resolution (name only, no displayName).
167+ * Used by resolve() for references not pre-loaded by batchResolve().
168+ */
169+ private void resolveNamesSimple (JdbcSession session , QObject <?> object , Set <UUID > oidsToResolve ) {
132170 if (oidsToResolve .isEmpty ()) {
133171 return ;
134172 }
135173 List <Tuple > namesResult = session .newQuery ()
136174 .from (object )
137- .select (object .oid , object .nameOrig , object . nameNorm )
175+ .select (object .oid , object .nameOrig )
138176 .where (object .oid .in (oidsToResolve ))
139177 .fetch ();
140178 for (Tuple named : namesResult ) {
141179 UUID uuid = named .get (object .oid );
142180 oidsToResolve .remove (uuid );
143- String orig = named .get (object .nameOrig );
144- String norm = named .get (object .nameNorm );
145- PolyString poly = new PolyString (orig , norm );
146- uuidToName .put (uuid , poly );
181+ String name = named .get (object .nameOrig );
182+ // Only set name, displayName is null (not requested)
183+ resolvedNamesCache .put (uuid , new ResolvedNames (name , null ));
147184 }
148185 }
149186
@@ -162,10 +199,10 @@ private void initialVisit(PrismReferenceValue value) {
162199 return ;
163200 }
164201 UUID oid = SqaleUtils .oidToUuid (value .getOid ());
165- PolyString maybe = uuidToName .get (oid );
166- if (maybe != null ) {
167- value .setTargetName (maybe );
168- } else if (QNameUtil .match (ShadowType .COMPLEX_TYPE ,value .getTargetType ())) {
202+ ResolvedNames resolved = resolvedNamesCache .get (oid );
203+ if (resolved != null ) {
204+ value .setTargetName (PolyString . fromOrig ( resolved . name ()) );
205+ } else if (QNameUtil .match (ShadowType .COMPLEX_TYPE , value .getTargetType ())) {
169206 shadowOidsToResolve .add (oid );
170207 } else {
171208 oidsToResolve .add (oid );
@@ -176,10 +213,77 @@ private void updateReference(PrismReferenceValue value) {
176213 if (value .getOid () == null ) {
177214 return ;
178215 }
179- PolyString name = uuidToName .get (SqaleUtils .oidToUuid (value .getOid ()));
180- if (name != null ) {
181- value .setTargetName (name );
216+ ResolvedNames resolved = resolvedNamesCache .get (SqaleUtils .oidToUuid (value .getOid ()));
217+ if (resolved != null ) {
218+ value .setTargetName (PolyString .fromOrig (resolved .name ()));
219+ }
220+ }
221+
222+ @ Override
223+ public void batchResolve (JdbcSession session , Set <UUID > oids , boolean includeDisplayNames ) {
224+ if (oids == null || oids .isEmpty ()) {
225+ return ;
182226 }
227+
228+ // Filter out OIDs that are already cached
229+ Set <UUID > oidsToLoad = new HashSet <>();
230+ for (UUID oid : oids ) {
231+ if (!resolvedNamesCache .containsKey (oid )) {
232+ oidsToLoad .add (oid );
233+ }
234+ }
235+
236+ if (oidsToLoad .isEmpty ()) {
237+ return ;
238+ }
239+
240+ if (includeDisplayNames ) {
241+ // Query m_object with LEFT JOIN to m_user and m_abstract_role for display names
242+ QObject <MObject > qObj = new QObject <>(MObject .class , "obj" );
243+ QUser qUser = new QUser ("usr" );
244+ QAbstractRole <MAbstractRole > qRole = new QAbstractRole <>(MAbstractRole .class , "ar" );
245+
246+ List <Tuple > rows = session .newQuery ()
247+ .select (qObj .oid , qObj .nameOrig , qUser .fullNameOrig , qRole .displayNameOrig )
248+ .from (qObj )
249+ .leftJoin (qUser ).on (qObj .oid .eq (qUser .oid ))
250+ .leftJoin (qRole ).on (qObj .oid .eq (qRole .oid ))
251+ .where (qObj .oid .in (oidsToLoad ))
252+ .fetch ();
253+
254+ for (Tuple row : rows ) {
255+ UUID oid = row .get (qObj .oid );
256+ String fullName = row .get (qUser .fullNameOrig );
257+ String roleDisplayName = row .get (qRole .displayNameOrig );
258+ String name = row .get (qObj .nameOrig );
259+
260+ // fullName for Users, displayName for AbstractRoles, null otherwise
261+ // GUI components decide whether to fallback to name
262+ String displayName = fullName != null ? fullName : roleDisplayName ;
263+
264+ resolvedNamesCache .put (oid , new ResolvedNames (name , displayName ));
265+ }
266+ } else {
267+ // Simple name-only query
268+ QObject <MObject > qObj = new QObject <>(MObject .class , "obj" );
269+
270+ List <Tuple > rows = session .newQuery ()
271+ .select (qObj .oid , qObj .nameOrig )
272+ .from (qObj )
273+ .where (qObj .oid .in (oidsToLoad ))
274+ .fetch ();
275+
276+ for (Tuple row : rows ) {
277+ UUID oid = row .get (qObj .oid );
278+ String name = row .get (qObj .nameOrig );
279+ resolvedNamesCache .put (oid , new ResolvedNames (name , null ));
280+ }
281+ }
282+ }
283+
284+ @ Override
285+ public ResolvedNames getResolvedNames (UUID oid ) {
286+ return resolvedNamesCache .get (oid );
183287 }
184288 }
185289}
0 commit comments