diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 74d28f8da..35f4b0373 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -103,11 +103,11 @@ io.repaint.maven tiles-maven-plugin - 2.24 + 2.34 true - io.ebean.tile:enhancement:13.13.1 + io.ebean.tile:enhancement:13.13.2 diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/TQAssoc.java b/ebean-querybean/src/main/java/io/ebean/typequery/TQAssoc.java new file mode 100644 index 000000000..bdfe47cc0 --- /dev/null +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQAssoc.java @@ -0,0 +1,162 @@ +package io.ebean.typequery; + +import io.avaje.lang.Nullable; + +import java.util.Collection; + +/** + * Base type for associated beans. + * + * @param the entity bean type (normal entity bean type e.g. Customer) + * @param the specific root query bean type (e.g. QCustomer) + */ +public abstract class TQAssoc extends TQProperty { + + /** + * 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. + *

+ * 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 eqIfPresent() 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 values) { + expr().in(_name, values); + return _root; + } + + /** + * In where null or empty values means that no predicate is added to the query. + *

+ * That is, only add the IN predicate if the values are not null or empty. + *

+ * Without this we typically need to code an if block to only add + * the IN predicate if the collection is not empty like: + *

+ * + *

Without inOrEmpty()

+ *
{@code
+   *
+   *   List 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();
+   *
+   * }
+ * + *

Using inOrEmpty()

+ *
{@code
+   *
+   *   List names = Arrays.asList("foo", "bar");
+   *
+   *   new QCustomer()
+   *       .registered.before(LocalDate.now())
+   *       .name.inOrEmpty(names)
+   *       .findList();
+   *
+   * }
+ */ + public final R inOrEmpty(Collection 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 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; + } + +} diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/TQAssocBean.java b/ebean-querybean/src/main/java/io/ebean/typequery/TQAssocBean.java index 14a1e92b5..a60b2f99d 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQAssocBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQAssocBean.java @@ -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 the entity bean type (normal entity bean type e.g. Customer) * @param the specific root query bean type (e.g. QCustomer) + * @param the query bean type */ @SuppressWarnings("rawtypes") -public abstract class TQAssocBean extends TQProperty { +public abstract class TQAssocBean extends TQAssoc { private static final FetchConfig FETCH_DEFAULT = FetchConfig.ofDefault(); private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery(); @@ -110,38 +109,37 @@ public abstract class TQAssocBean extends TQProperty { } /** - * 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... 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... 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... 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... 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 extends TQProperty { return (SpiQueryFetch) ((TQRootBean) _root).query(); } - @SafeVarargs private Set properties(TQProperty... props) { Set set = new LinkedHashSet<>(); for (TQProperty prop : props) { @@ -187,135 +184,6 @@ public abstract class TQAssocBean extends TQProperty { 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. - *

- * 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 eqIfPresent() 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 values) { - expr().in(_name, values); - return _root; - } - - /** - * In where null or empty values means that no predicate is added to the query. - *

- * That is, only add the IN predicate if the values are not null or empty. - *

- * Without this we typically need to code an if block to only add - * the IN predicate if the collection is not empty like: - *

- * - *

Without inOrEmpty()

- *
{@code
-   *
-   *   List 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();
-   *
-   * }
- * - *

Using inOrEmpty()

- *
{@code
-   *
-   *   List names = Arrays.asList("foo", "bar");
-   *
-   *   new QCustomer()
-   *       .registered.before(LocalDate.now())
-   *       .name.inOrEmpty(names)
-   *       .findList();
-   *
-   * }
- */ - public final R inOrEmpty(Collection 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 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. diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index d101b6226..0733b4e70 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -113,7 +113,7 @@ io.ebean kotlin-querybean-generator - 13.13.0 + 13.13.3-RC1 diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Constants.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Constants.java index 4d03ad6db..efb7728fb 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Constants.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Constants.java @@ -19,6 +19,7 @@ interface Constants { String DBNAME = "io.ebean.annotation.DbName"; String TQROOTBEAN = "io.ebean.typequery.TQRootBean"; + String TQASSOC = "io.ebean.typequery.TQAssoc"; String TQASSOCBEAN = "io.ebean.typequery.TQAssocBean"; String TQPROPERTY = "io.ebean.typequery.TQProperty"; String TYPEQUERYBEAN = "io.ebean.typequery.TypeQueryBean"; diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/KotlinLangAdapter.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/KotlinLangAdapter.java index f3da81840..f0c6f1573 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/KotlinLangAdapter.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/KotlinLangAdapter.java @@ -9,7 +9,7 @@ class KotlinLangAdapter implements LangAdapter { @Override public void beginAssocClass(Append writer, String shortName, String origShortName) { - writer.append("class Q%s : TQAssocBean<%s,R> {", shortName, origShortName).eol(); +// writer.append("class Q%s : TQAssocBean<%s,R> {", shortName, origShortName).eol(); } @Override diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java index 5ccb617d9..01b398fe3 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java @@ -236,7 +236,11 @@ class SimpleQueryBeanWriter { importTypes.remove(Constants.DATABASE); importTypes.remove(Constants.FETCHGROUP); importTypes.remove(Constants.QUERY); - importTypes.add(Constants.TQASSOCBEAN); + if (embeddable) { + importTypes.add(Constants.TQASSOC); + } else { + importTypes.add(Constants.TQASSOCBEAN); + } if (isEntity()) { importTypes.add(Constants.TQPROPERTY); importTypes.add(origDestPackage + ".Q" + origShortName); @@ -280,7 +284,6 @@ class SimpleQueryBeanWriter { private void writeAssocBeanFetch() { if (isEntity()) { - lang().fetch(writer, origShortName); //if (implementsInterface != null) { // writeAssocBeanExpression(false, "eq", "Is equal to by ID property."); // writeAssocBeanExpression(true, "eqIfPresent", "Is equal to by ID property if the value is not null, if null no expression is added."); @@ -340,7 +343,11 @@ class SimpleQueryBeanWriter { writer.append(" */").eol(); writer.append(Constants.AT_GENERATED).eol(); writer.append(Constants.AT_TYPEQUERYBEAN).eol(); - lang().beginAssocClass(writer, shortName, shortInnerName); + if (embeddable) { + writer.append("class Q%s : TQAssoc<%s,R> {", shortName, shortInnerName).eol(); + } else { + writer.append("class Q%s : TQAssocBean<%s,R,Q%s> {", shortName, shortInnerName, origShortName).eol(); + } } else { writer.append("/**").eol(); diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/Constants.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/Constants.java index 8263cd160..bc73ccd0d 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/Constants.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/Constants.java @@ -20,6 +20,7 @@ interface Constants { String DBNAME = "io.ebean.annotation.DbName"; String TQROOTBEAN = "io.ebean.typequery.TQRootBean"; + String TQASSOC = "io.ebean.typequery.TQAssoc"; String TQASSOCBEAN = "io.ebean.typequery.TQAssocBean"; String TQPROPERTY = "io.ebean.typequery.TQProperty"; String TYPEQUERYBEAN = "io.ebean.typequery.TypeQueryBean"; diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java index 537d216b3..efc67b53f 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java @@ -117,7 +117,6 @@ class SimpleQueryBeanWriter { * Write the type query bean (root bean). */ void writeRootBean() throws IOException { - gatherPropertyDetails(); if (isEmbeddable()) { processingContext.addEntity(beanFullName, dbName); @@ -170,7 +169,11 @@ class SimpleQueryBeanWriter { importTypes.remove(Constants.DATABASE); importTypes.remove(Constants.FETCHGROUP); importTypes.remove(Constants.QUERY); - importTypes.add(Constants.TQASSOCBEAN); + if (embeddable) { + importTypes.add(Constants.TQASSOC); + } else { + importTypes.add(Constants.TQASSOCBEAN); + } if (isEntity()) { importTypes.add(Constants.TQPROPERTY); importTypes.add(origDestPackage + ".Q" + origShortName); @@ -288,10 +291,7 @@ class SimpleQueryBeanWriter { private void writeAssocBeanFetch() { if (isEntity()) { - writeAssocBeanFetch("", "Eagerly fetch this association loading the specified properties."); - writeAssocBeanFetch("Query", "Eagerly fetch this association using a 'query join' loading the specified properties."); - writeAssocBeanFetch("Cache", "Eagerly fetch this association using L2 cache."); - writeAssocBeanFetch("Lazy", "Use lazy loading for this association loading the specified properties."); + // inherit the fetch methods if (implementsInterface != null) { writeAssocBeanExpression(false, "eq", "Is equal to by ID property."); writeAssocBeanExpression(true, "eqIfPresent", "Is equal to by ID property if the value is not null, if null no expression is added."); @@ -367,8 +367,11 @@ class SimpleQueryBeanWriter { writer.append(" */").eol(); writer.append(Constants.AT_GENERATED).eol(); writer.append(Constants.AT_TYPEQUERYBEAN).eol(); - writer.append("public class Q%s extends TQAssocBean<%s,R> {", shortName, shortInnerName).eol(); - + if (embeddable) { + writer.append("public class Q%s extends TQAssoc<%s,R> {", shortName, shortInnerName).eol(); + } else { + writer.append("public class Q%s extends TQAssocBean<%s,R,Q%s> {", shortName, shortInnerName, origShortName).eol(); + } } else { writer.append("/**").eol(); writer.append(" * Query bean for %s.", shortName).eol(); @@ -420,7 +423,6 @@ class SimpleQueryBeanWriter { * Write all the imports. */ private void writeImports() { - for (String importType : importTypes) { writer.append("import %s;", importType).eol(); } diff --git a/tests/test-java16/pom.xml b/tests/test-java16/pom.xml index 25fd20235..5c7578883 100644 --- a/tests/test-java16/pom.xml +++ b/tests/test-java16/pom.xml @@ -52,7 +52,7 @@ io.ebean querybean-generator - 13.13.1 + 13.13.2