-
Notifications
You must be signed in to change notification settings - Fork 188
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Documented Midpoint PreparedQuery and TypedQuery, made APIs more user…
… friendly
- Loading branch information
1 parent
6f4e3f0
commit d54cad4
Showing
3 changed files
with
336 additions
and
0 deletions.
There are no files selected for viewing
94 changes: 94 additions & 0 deletions
94
infra/schema/src/main/java/com/evolveum/midpoint/schema/query/AbstractTypedQuery.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/* | ||
* Copyright (c) 2023 Evolveum and contributors | ||
* | ||
* This work is dual-licensed under the Apache License 2.0 | ||
* and European Union Public License. See LICENSE file for details. | ||
*/ | ||
package com.evolveum.midpoint.schema.query; | ||
|
||
|
||
import com.evolveum.midpoint.prism.PrismContext; | ||
import com.evolveum.midpoint.prism.path.ItemPath; | ||
import com.evolveum.midpoint.prism.query.ObjectOrdering; | ||
import com.evolveum.midpoint.prism.query.ObjectPaging; | ||
import com.evolveum.midpoint.prism.query.OrderDirection; | ||
import com.evolveum.midpoint.schema.GetOperationOptions; | ||
import com.evolveum.midpoint.schema.GetOperationOptionsBuilder; | ||
import com.evolveum.midpoint.schema.SchemaService; | ||
import com.evolveum.midpoint.schema.SelectorOptions; | ||
|
||
import org.jetbrains.annotations.Nullable; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
|
||
public abstract class AbstractTypedQuery<T, F extends AbstractTypedQuery<T,F>> { | ||
|
||
protected Class<T> type; | ||
private final GetOperationOptionsBuilder optionsBuilder; | ||
|
||
private List<ObjectOrdering> orderBy = new ArrayList<>(); | ||
|
||
private Integer offset; | ||
private Integer maxSize; | ||
|
||
private Collection<SelectorOptions<GetOperationOptions>> options; | ||
|
||
public AbstractTypedQuery(Class<T> type) { | ||
this.type = type; | ||
this.optionsBuilder = SchemaService.get().getOperationOptionsBuilder(); | ||
} | ||
|
||
|
||
abstract protected F self(); | ||
|
||
|
||
public GetOperationOptionsBuilder operationOptionsBuilder() { | ||
return optionsBuilder; | ||
} | ||
|
||
public Integer getOffset() { | ||
return offset; | ||
} | ||
|
||
public F offset(Integer offset) { | ||
this.offset = offset; | ||
return self(); | ||
} | ||
|
||
public Integer getMaxSize() { | ||
return maxSize; | ||
} | ||
|
||
public F maxSize(Integer maxSize) { | ||
this.maxSize = maxSize; | ||
return self(); | ||
} | ||
|
||
public F orderBy(ItemPath path, OrderDirection direction) { | ||
orderBy.add(PrismContext.get().queryFactory().createOrdering(path, direction)); | ||
return self(); | ||
} | ||
|
||
protected boolean pagingRequired() { | ||
return maxSize != null || offset != null || !orderBy.isEmpty(); | ||
} | ||
|
||
@Nullable | ||
protected ObjectPaging buildObjectPaging() { | ||
if (pagingRequired()) { | ||
var paging = PrismContext.get().queryFactory().createPaging(offset, maxSize); | ||
paging.setOrdering(new ArrayList<>(orderBy)); | ||
return paging; | ||
} | ||
return null; | ||
} | ||
|
||
protected void fillFrom(AbstractTypedQuery<?,?> other) { | ||
offset = other.offset; | ||
maxSize = other.maxSize; | ||
orderBy = new ArrayList<>(other.orderBy); | ||
} | ||
} | ||
|
132 changes: 132 additions & 0 deletions
132
infra/schema/src/main/java/com/evolveum/midpoint/schema/query/PreparedQuery.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/* | ||
* Copyright (c) 2023 Evolveum and contributors | ||
* | ||
* This work is dual-licensed under the Apache License 2.0 | ||
* and European Union Public License. See LICENSE file for details. | ||
*/ | ||
package com.evolveum.midpoint.schema.query; | ||
|
||
import com.evolveum.midpoint.prism.PrismContext; | ||
import com.evolveum.midpoint.prism.query.ObjectFilter; | ||
import com.evolveum.midpoint.prism.query.ObjectQuery; | ||
import com.evolveum.midpoint.prism.query.PreparedPrismQuery; | ||
import com.evolveum.midpoint.schema.GetOperationOptionsBuilder; | ||
import com.evolveum.midpoint.util.exception.SchemaException; | ||
|
||
/** | ||
* Prepared Query represents query with filter with placeholders, which can be used for search. | ||
* | ||
* Prepared Query differs from {@link TypedQuery} that original query contained placeholders, which needs to be bound to actual values | ||
* before query is used. | ||
* | ||
* Values could be bound using {@link #bindValue(Object)} method for binding first unbound values or by using {@link #bind(Object...)} | ||
* method which binds multiple unbound values in order of their appeareance. | ||
* | ||
* Prepared Query can be converted to {@link TypedQuery} by binding values using {@link #bind(Object...)} method. Once prepared query | ||
* is successfully bound, filter is cached and new typed queries could be generated using {@link #toTypedQuery()}. | ||
* | ||
* @param <T> <T> Resulting item type | ||
*/ | ||
public class PreparedQuery<T> extends AbstractTypedQuery<T, PreparedQuery<T>> { | ||
|
||
|
||
private PreparedPrismQuery delegate; | ||
|
||
private GetOperationOptionsBuilder options; | ||
/** | ||
* Final filter, available once query is successfully bound using {@link #bind(Object...)}. | ||
*/ | ||
private ObjectFilter finalFilter; | ||
|
||
PreparedQuery(Class<T> type, PreparedPrismQuery delegate) { | ||
super(type); | ||
this.delegate = delegate; | ||
} | ||
|
||
/** | ||
* Parses supplied query string for type with placeholder support. | ||
* | ||
* @param type Type of the items which query should return | ||
* @param query Midpoint Query string without placeholders | ||
* @return new Prepared Query which contains partially parsed filter, type information and allows for values to be bound to placeholders. | ||
* @param <T> Type of the items which query should return | ||
* @throws SchemaException when supplied query string was syntactically or logically incorrect | ||
*/ | ||
public static <T> PreparedQuery<T> parse(Class<T> type, String query) throws SchemaException { | ||
var prismQuery = PrismContext.get().createQueryParser().parse(type, query); | ||
return new PreparedQuery<>(type, prismQuery); | ||
} | ||
|
||
/** | ||
* Binds next unbound value in filter to provided value. | ||
* | ||
* IMPORTANT: Current behavior is strict in checking value types for bound values, user must supply | ||
* correct class for realValue (eg. {@link com.evolveum.midpoint.prism.polystring.PolyString}, | ||
* {@link javax.xml.datatype.XMLGregorianCalendar} | ||
* | ||
* @param realValue Real Value to be bound | ||
* @throws SchemaException If provided value is invalid according to schema definition (type of value) | ||
* @throws IllegalStateException If there is no positional value to be bound | ||
*/ | ||
public PreparedQuery<T> bindValue(Object realValue) throws SchemaException { | ||
delegate.bindValue(realValue); | ||
return self(); | ||
} | ||
|
||
/** | ||
* Binds multiple values and returns final Typed Query which will contain filter. | ||
* | ||
* @param args Real values to be bound to unbound placeholders in order of appearance. See {@link #bindValue(Object)} for more details. | ||
* @return Typed Query which contains final filter and configuration of paging and ordering. | ||
* @throws IllegalStateException If not all placeholders were bound to values | ||
* @throws SchemaException If provided values are invalid according to schema definition (type of value) | ||
* resulting filter with bound values is invalid. | ||
*/ | ||
public TypedQuery<T> bind(Object... args) throws SchemaException { | ||
if (finalFilter == null) { | ||
finalFilter = delegate.bind(args); | ||
} | ||
return toTypedQuery(); | ||
} | ||
|
||
/** | ||
* Creates new {@link TypedQuery} based on type, filter, bound values and configuration of paging and ordering. | ||
* | ||
* @return New Typed Query based on bound values and configuration of paging and ordering. | ||
* @throws IllegalStateException If query was not successfully bound before using {@link #bind(Object...)} or {@link #build()}. | ||
*/ | ||
public TypedQuery<T> toTypedQuery() { | ||
if (finalFilter == null) { | ||
throw new IllegalStateException("Filter was not bound"); | ||
} | ||
var typed = TypedQuery.from(type, finalFilter); | ||
typed.fillFrom(this); | ||
typed.setOptions(operationOptionsBuilder().build()); | ||
return typed; | ||
} | ||
|
||
public ObjectQuery toObjectQuery() { | ||
return toTypedQuery().toObjectQuery(); | ||
} | ||
|
||
/** | ||
* | ||
* @return | ||
* @throws SchemaException | ||
*/ | ||
public TypedQuery<T> build() throws SchemaException { | ||
return bind(); | ||
} | ||
|
||
/** | ||
* @return True if all placeholders were bound to value. | ||
*/ | ||
public boolean allPlaceholdersBound() { | ||
return delegate.allPlaceholdersBound(); | ||
} | ||
|
||
@Override | ||
protected PreparedQuery<T> self() { | ||
return this; | ||
} | ||
} |
110 changes: 110 additions & 0 deletions
110
infra/schema/src/main/java/com/evolveum/midpoint/schema/query/TypedQuery.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* | ||
* Copyright (c) 2023 Evolveum and contributors | ||
* | ||
* This work is dual-licensed under the Apache License 2.0 | ||
* and European Union Public License. See LICENSE file for details. | ||
*/ | ||
package com.evolveum.midpoint.schema.query; | ||
|
||
import com.evolveum.midpoint.prism.ItemDefinition; | ||
import com.evolveum.midpoint.prism.PrismContext; | ||
import com.evolveum.midpoint.prism.query.ObjectFilter; | ||
import com.evolveum.midpoint.prism.query.ObjectQuery; | ||
import com.evolveum.midpoint.schema.GetOperationOptions; | ||
import com.evolveum.midpoint.schema.SelectorOptions; | ||
import com.evolveum.midpoint.util.exception.SchemaException; | ||
|
||
import java.util.Collection; | ||
|
||
/** | ||
* Typed Query represents query with filter, which can be used for searching references, containers and objects (depending on item type). | ||
* | ||
* Typed Query can be converted to existing {@link ObjectQuery} interface using {@link #toObjectQuery()} method. This allows | ||
* to use TypedQuery as a builder for {@link ObjectQuery} where resulting object queries will only differ in paging information, | ||
* but use same filter. | ||
* | ||
* @param <T> Resulting item type | ||
*/ | ||
public class TypedQuery<T> extends AbstractTypedQuery<T,TypedQuery<T>> { | ||
|
||
private ObjectFilter filter; | ||
|
||
private Collection<SelectorOptions<GetOperationOptions>> options; | ||
|
||
|
||
TypedQuery(Class<T> type, ObjectFilter filter) { | ||
super(type); | ||
this.filter = filter; | ||
} | ||
|
||
public Class<T> getType() { | ||
return type; | ||
} | ||
|
||
/** | ||
* Creates TypedFilter for supplied type and query string. | ||
* | ||
* Query string must not contain placeholders. If you need placeholder support see {@link PreparedQuery#parse(Class, String)}. | ||
* Resulting query doe | ||
* | ||
* @param type Type of the items which query should return | ||
* @param query Midpoint Query string without placeholders | ||
* @return new Typed Query which contains parsed filter and type information, no ordering and no paging information. | ||
* @param <T> | ||
* @throws SchemaException when supplied query string was syntactically or logically incorrect | ||
*/ | ||
public static <T> TypedQuery<T> parse(Class<T> type, String query) throws SchemaException { | ||
var filter = PrismContext.get().createQueryParser().parseFilter(type, query); | ||
return from(type, filter); | ||
} | ||
|
||
/** | ||
* Creates TypedFilter for supplied type and filter. | ||
* | ||
* @param type Type of the items which query should return | ||
* @param filter Existing filter to be used for typed query | ||
* @return Typed Query which contains filter and type information, no ordering and no paging information. | ||
* @param <T> Type of the items which query should return | ||
*/ | ||
public static <T> TypedQuery<T> from(Class<T> type, ObjectFilter filter) { | ||
return new TypedQuery<>(type, filter); | ||
} | ||
|
||
/** | ||
* Creates new {@link ObjectQuery} representing this {@link TypedQuery}, created Object query uses same filter and | ||
* copy of current paging information. | ||
* | ||
* This allows to use one TypedQuery to be used to generate multiple page specific queries. | ||
* | ||
* @return new Object Query representing this Type Query | ||
*/ | ||
public ObjectQuery toObjectQuery() { | ||
var query = PrismContext.get().queryFactory().createQuery(filter); | ||
query.setPaging(buildObjectPaging()); | ||
return query; | ||
|
||
} | ||
|
||
/** | ||
* Returns explicitly configured set of {@link GetOperationOptions} | ||
* | ||
* If set was not explicitly set using {@link #setOptions(Collection)}, it is constructed using {@link #operationOptionsBuilder()}. | ||
* | ||
* @return Set of {@link GetOperationOptions} | ||
*/ | ||
public Collection<SelectorOptions<GetOperationOptions>> getOptions() { | ||
if (options == null) { | ||
options = operationOptionsBuilder().build(); | ||
} | ||
return options; | ||
} | ||
|
||
public void setOptions(Collection<SelectorOptions<GetOperationOptions>> options) { | ||
this.options = options; | ||
} | ||
|
||
@Override | ||
protected TypedQuery<T> self() { | ||
return this; | ||
} | ||
} |