[QueryBeans] Refactor splitting TQAssocBean into TQAssoc & TQAssocBean

- Requires ebean-agent to be up-to-date (13.13.2 or greater)
- Split TQAssocBean into 2, one for embeddable beans and the other for the reset
- The eq(), in() etc expressions go to TQAssoc
- Add fetch() methods to TQAssocBean
- No longer generate the fetch() methods as we now have them on TQAssocBean
This commit is contained in:
Rob Bygrave
2023-02-27 17:46:11 +13:00
parent e9ab0f34f3
commit 4ec46e3008
10 changed files with 209 additions and 168 deletions
@@ -0,0 +1,162 @@
package io.ebean.typequery;
import io.avaje.lang.Nullable;
import java.util.Collection;
/**
* Base type for associated beans.
*
* @param <T> the entity bean type (normal entity bean type e.g. Customer)
* @param <R> the specific root query bean type (e.g. QCustomer)
*/
public abstract class TQAssoc<T, R> extends TQProperty<R, Object> {
/**
* Construct with a property name and root instance.
*
* @param name the name of the property
* @param root the root query bean instance
*/
public TQAssoc(String name, R root) {
this(name, root, null);
}
/**
* Construct with additional path prefix.
*/
public TQAssoc(String name, R root, String prefix) {
super(name, root, prefix);
}
/**
* Is equal to by ID property.
*/
public final R eq(T other) {
expr().eq(_name, other);
return _root;
}
/**
* Is EQUAL TO if value is non-null and otherwise no expression is added to the query.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>eqIfPresent()</code> rather than having a separate if block.
*/
public final R eqIfPresent(@Nullable T other) {
expr().eqIfPresent(_name, other);
return _root;
}
/**
* Is equal to by ID property.
*/
public final R equalTo(T other) {
return eq(other);
}
/**
* Is not equal to by ID property.
*/
public final R ne(T other) {
expr().ne(_name, other);
return _root;
}
/**
* Is not equal to by ID property.
*/
public final R notEqualTo(T other) {
return ne(other);
}
/**
* Is in a list of values.
*
* @param values the list of values for the predicate
* @return the root query bean instance
*/
@SafeVarargs
public final R in(T... values) {
expr().in(_name, (Object[]) values);
return _root;
}
/**
* Is in a list of values.
*
* @param values the list of values for the predicate
* @return the root query bean instance
*/
public final R in(Collection<T> values) {
expr().in(_name, values);
return _root;
}
/**
* In where null or empty values means that no predicate is added to the query.
* <p>
* That is, only add the IN predicate if the values are not null or empty.
* <p>
* Without this we typically need to code an <code>if</code> block to only add
* the IN predicate if the collection is not empty like:
* </p>
*
* <h3>Without inOrEmpty()</h3>
* <pre>{@code
*
* List<String> names = Arrays.asList("foo", "bar");
*
* QCustomer query = new QCustomer()
* .registered.before(LocalDate.now())
*
* // conditionally add the IN expression to the query
* if (names != null && !names.isEmpty()) {
* query.name.in(names)
* }
*
* query.findList();
*
* }</pre>
*
* <h3>Using inOrEmpty()</h3>
* <pre>{@code
*
* List<String> names = Arrays.asList("foo", "bar");
*
* new QCustomer()
* .registered.before(LocalDate.now())
* .name.inOrEmpty(names)
* .findList();
*
* }</pre>
*/
public final R inOrEmpty(Collection<T> values) {
expr().inOrEmpty(_name, values);
return _root;
}
/**
* Is NOT in a list of values.
*
* @param values the list of values for the predicate
* @return the root query bean instance
*/
public final R notIn(Collection<T> values) {
expr().notIn(_name, values);
return _root;
}
/**
* Is NOT in a list of values.
*
* @param values the list of values for the predicate
* @return the root query bean instance
*/
@SafeVarargs
public final R notIn(T... values) {
expr().notIn(_name, (Object[]) values);
return _root;
}
}
@@ -1,6 +1,5 @@
package io.ebean.typequery;
import io.avaje.lang.Nullable;
import io.ebean.ExpressionList;
import io.ebean.FetchConfig;
import io.ebean.FetchGroup;
@@ -8,18 +7,18 @@ import io.ebeaninternal.api.SpiQueryFetch;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.querydefn.SpiFetchGroup;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Base type for associated beans.
* Base type for associated beans that are not embeddable.
*
* @param <T> the entity bean type (normal entity bean type e.g. Customer)
* @param <R> the specific root query bean type (e.g. QCustomer)
* @param <QB> the query bean type
*/
@SuppressWarnings("rawtypes")
public abstract class TQAssocBean<T, R> extends TQProperty<R, Object> {
public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
private static final FetchConfig FETCH_DEFAULT = FetchConfig.ofDefault();
private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery();
@@ -110,38 +109,37 @@ public abstract class TQAssocBean<T, R> extends TQProperty<R, Object> {
}
/**
* Eagerly fetch this association fetching some of the properties.
* Eagerly fetch this association loading the specified properties.
*/
@SafeVarargs
protected final R fetchProperties(TQProperty<?, ?>... props) {
return fetchWithProperties(FETCH_DEFAULT, props);
@SafeVarargs @SuppressWarnings("varargs")
public final R fetch(TQProperty<QB,?>... properties) {
return fetchWithProperties(FETCH_DEFAULT, properties);
}
/**
* Eagerly fetch query this association fetching some of the properties.
* Eagerly fetch this association using a 'query join' loading the specified properties.
*/
@SafeVarargs
protected final R fetchQueryProperties(TQProperty<?, ?>... props) {
return fetchWithProperties(FETCH_QUERY, props);
@SafeVarargs @SuppressWarnings("varargs")
public final R fetchQuery(TQProperty<QB,?>... properties) {
return fetchWithProperties(FETCH_QUERY, properties);
}
/**
* Eagerly fetch this association using L2 bean cache.
* Eagerly fetch this association using L2 cache.
*/
@SafeVarargs
protected final R fetchCacheProperties(TQProperty<?, ?>... props) {
return fetchWithProperties(FETCH_CACHE, props);
@SafeVarargs @SuppressWarnings("varargs")
public final R fetchCache(TQProperty<QB,?>... properties) {
return fetchWithProperties(FETCH_CACHE, properties);
}
/**
* Eagerly fetch query this association fetching some of the properties.
* Use lazy loading for this association loading the specified properties.
*/
@SafeVarargs
protected final R fetchLazyProperties(TQProperty<?, ?>... props) {
return fetchWithProperties(FETCH_LAZY, props);
@SafeVarargs @SuppressWarnings("varargs")
public final R fetchLazy(TQProperty<QB,?>... properties) {
return fetchWithProperties(FETCH_LAZY, properties);
}
@SafeVarargs
private R fetchWithProperties(FetchConfig config, TQProperty<?, ?>... props) {
spiQuery().fetchProperties(_name, properties(props), config);
return _root;
@@ -178,7 +176,6 @@ public abstract class TQAssocBean<T, R> extends TQProperty<R, Object> {
return (SpiQueryFetch) ((TQRootBean) _root).query();
}
@SafeVarargs
private Set<String> properties(TQProperty<?, ?>... props) {
Set<String> set = new LinkedHashSet<>();
for (TQProperty<?, ?> prop : props) {
@@ -187,135 +184,6 @@ public abstract class TQAssocBean<T, R> extends TQProperty<R, Object> {
return set;
}
/**
* Is equal to by ID property.
*/
public final R eq(T other) {
expr().eq(_name, other);
return _root;
}
/**
* Is EQUAL TO if value is non-null and otherwise no expression is added to the query.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>eqIfPresent()</code> rather than having a separate if block.
*/
public final R eqIfPresent(@Nullable T other) {
expr().eqIfPresent(_name, other);
return _root;
}
/**
* Is equal to by ID property.
*/
public final R equalTo(T other) {
return eq(other);
}
/**
* Is not equal to by ID property.
*/
public final R ne(T other) {
expr().ne(_name, other);
return _root;
}
/**
* Is not equal to by ID property.
*/
public final R notEqualTo(T other) {
return ne(other);
}
/**
* Is in a list of values.
*
* @param values the list of values for the predicate
* @return the root query bean instance
*/
@SafeVarargs
public final R in(T... values) {
expr().in(_name, (Object[]) values);
return _root;
}
/**
* Is in a list of values.
*
* @param values the list of values for the predicate
* @return the root query bean instance
*/
public final R in(Collection<T> values) {
expr().in(_name, values);
return _root;
}
/**
* In where null or empty values means that no predicate is added to the query.
* <p>
* That is, only add the IN predicate if the values are not null or empty.
* <p>
* Without this we typically need to code an <code>if</code> block to only add
* the IN predicate if the collection is not empty like:
* </p>
*
* <h3>Without inOrEmpty()</h3>
* <pre>{@code
*
* List<String> names = Arrays.asList("foo", "bar");
*
* QCustomer query = new QCustomer()
* .registered.before(LocalDate.now())
*
* // conditionally add the IN expression to the query
* if (names != null && !names.isEmpty()) {
* query.name.in(names)
* }
*
* query.findList();
*
* }</pre>
*
* <h3>Using inOrEmpty()</h3>
* <pre>{@code
*
* List<String> names = Arrays.asList("foo", "bar");
*
* new QCustomer()
* .registered.before(LocalDate.now())
* .name.inOrEmpty(names)
* .findList();
*
* }</pre>
*/
public final R inOrEmpty(Collection<T> values) {
expr().inOrEmpty(_name, values);
return _root;
}
/**
* Is NOT in a list of values.
*
* @param values the list of values for the predicate
* @return the root query bean instance
*/
public final R notIn(Collection<T> values) {
expr().notIn(_name, values);
return _root;
}
/**
* Is NOT in a list of values.
*
* @param values the list of values for the predicate
* @return the root query bean instance
*/
@SafeVarargs
public final R notIn(T... values) {
expr().notIn(_name, (Object[]) values);
return _root;
}
/**
* Apply a filter when fetching these beans.