From ded4b94e54a06b4f6060fc2ade3c9f9dd2f20cfa Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 18 Aug 2023 21:43:13 +1200 Subject: [PATCH] Add Query bean filterMany() taking a closure Query beans also generate a "Assoc" query bean that is used for the relationships. Previously these were generated into a .query.assoc package and with this they are generated as an inner class of the query bean. This then allows the change where an additional filterMany() is generated on the "Assoc" bean that takes a closure. This simplifies the use of query beans for filterMany. This change to the generated query beans needs a change to the ebean-agent enhancement. --- .../main/java/io/ebean/ExpressionFactory.java | 5 + .../expression/DefaultExpressionFactory.java | 5 + .../expression/DefaultExpressionList.java | 6 +- ebean-querybean/pom.xml | 2 +- .../java/io/ebean/typequery/TQRootBean.java | 7 + .../java/org/querytest/QCustomerTest.java | 76 +++-- .../ebean/querybean/generator/Constants.java | 5 +- .../generator/ProcessingContext.java | 16 +- .../ebean/querybean/generator/Processor.java | 1 - .../querybean/generator/PropertyMeta.java | 2 - .../generator/PropertyTypeAssoc.java | 11 +- .../generator/SimpleQueryBeanWriter.java | 299 +++++++----------- 12 files changed, 196 insertions(+), 239 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/ExpressionFactory.java b/ebean-api/src/main/java/io/ebean/ExpressionFactory.java index 1b67c6cc2..d8857acbe 100644 --- a/ebean-api/src/main/java/io/ebean/ExpressionFactory.java +++ b/ebean-api/src/main/java/io/ebean/ExpressionFactory.java @@ -36,6 +36,11 @@ import java.util.Map; */ public interface ExpressionFactory { + /** + * Return a new ExpressionList. + */ + ExpressionList expressionList(); + /** * Path exists - for the given path in a JSON document. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionFactory.java index 6b9900ba3..2cb77ece6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionFactory.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionFactory.java @@ -36,6 +36,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory { return "sql"; } + @Override + public ExpressionList expressionList() { + return new DefaultExpressionList<>(this); + } + @Override public Expression textMatch(String propertyName, String search, Match options) { return new TextMatchExpression(propertyName, search, options); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index 0830aa566..49e7a7144 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -56,6 +56,10 @@ public class DefaultExpressionList implements SpiExpressionList { this.parentExprList = parentExprList; } + protected DefaultExpressionList(ExpressionFactory expr) { + this(null, expr, null, new ArrayList<>()); + } + private DefaultExpressionList() { this(null, null, null, new ArrayList<>()); } @@ -112,10 +116,8 @@ public class DefaultExpressionList implements SpiExpressionList { *

* If this is the Top level "text" expressions then it detects if explicit or implicit Bool Should, Must etc is required * to wrap the expressions. - *

*

* If implicit Bool is required SHOULD is used. - *

*/ @Override public void writeDocQuery(DocQueryContext context) throws IOException { diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 8013ed74a..397317af1 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -107,7 +107,7 @@ true - io.ebean.tile:enhancement:13.17.1 + io.ebean.tile:enhancement:13.21.1-beta diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java index 78224d9c5..8a4360117 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java @@ -136,6 +136,13 @@ public abstract class TQRootBean { this.query = null; } + /** Construct for FilterMany */ + protected TQRootBean(ExpressionList filter) { + this.query = null; + this.whereStack = new ArrayStack<>(); + whereStack.push(filter); + } + /** * Return the fetch group. */ diff --git a/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java b/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java index 658ccd8aa..aa3975a23 100644 --- a/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java +++ b/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java @@ -4,10 +4,11 @@ import io.ebean.*; import io.ebean.annotation.Transactional; import io.ebean.test.LoggedSql; import io.ebean.types.Inet; -import org.example.domain.*; +import org.example.domain.Address; +import org.example.domain.Country; +import org.example.domain.Customer; import org.example.domain.otherpackage.PhoneNumber; import org.example.domain.otherpackage.ValidEmail; -import org.example.domain.query.QAnimal; import org.example.domain.query.QContact; import org.example.domain.query.QCustomer; import org.junit.jupiter.api.Disabled; @@ -26,7 +27,9 @@ import static io.ebean.StdOperators.*; import static org.assertj.core.api.Assertions.assertThat; import static org.example.domain.query.QAddress.Alias.country; import static org.example.domain.query.QAddress.Alias.line1; +import static org.example.domain.query.QContact.Alias.firstName; import static org.example.domain.query.QContact.Alias.lastName; +import static org.example.domain.query.QCustomer.Alias.*; import static org.example.domain.query.QCustomer.Alias.billingAddress; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -161,8 +164,8 @@ public class QCustomerTest { @Test void equalTo_byProperty() { Query query = new QCustomer() - .select(QCustomer.Alias.id) - .billingAddress.city.eq(QCustomer.Alias.shippingAddress.city) + .select(id) + .billingAddress.city.eq(shippingAddress.city) .query(); query.findList(); @@ -178,8 +181,8 @@ public class QCustomerTest { @Test void notEqual_byProperty() { Query query = new QCustomer() - .select(QCustomer.Alias.id) - .billingAddress.city.ne(QCustomer.Alias.shippingAddress.city) + .select(id) + .billingAddress.city.ne(shippingAddress.city) .query(); query.findList(); @@ -246,6 +249,37 @@ public class QCustomerTest { .findList(); } + static final FetchGroup FGCustomerContacts = QCustomer.forFetchGroup() + .select(name) + .contacts.fetch(firstName, lastName) + .buildFetchGroup(); + + @Test + void filterMany() { + + var q = new QCustomer() + .select(FGCustomerContacts) + .contacts.filterMany(contacts -> contacts + .firstName.startsWith("r") + .email.isNotNull()) + .query(); + + q.findList(); + assertThat(q.getGeneratedSql()).isEqualTo("select t0.id, t0.name, t1.id, t1.first_name, t1.last_name from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id where t1.first_name like ? escape'|' and t1.email is not null order by t0.id"); + } + + @Test + void filterManySingle() { + + var q = new QCustomer() + .select(FGCustomerContacts) + .contacts.filterMany(c -> c.firstName.startsWith("r")) + .query(); + + q.findList(); + assertThat(q.getGeneratedSql()).isEqualTo("select t0.id, t0.name, t1.id, t1.first_name, t1.last_name from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id where t1.first_name like ? escape'|' order by t0.id"); + } + @Test public void testIdIn() { @@ -277,10 +311,10 @@ public class QCustomerTest { @Test public void usingMaster() { - new QCustomer() - .registered.isNull() - .usingMaster() - .findList(); + new QCustomer() + .registered.isNull() + .usingMaster() + .findList(); } @Test @@ -555,7 +589,7 @@ public class QCustomerTest { assertContains(new QCustomer().registered.lt(new Date()).query(), " where t0.registered < ?"); assertContains(new QCustomer().registered.before(new Date()).query(), " where t0.registered < ?"); assertContains(new QCustomer().registered.lessThan(new Date()).query(), " where t0.registered < ?"); - assertContains(new QCustomer().whenCreated.lt(QCustomer.Alias.whenUpdated).query(), " where t0.when_created < t0.when_updated"); + assertContains(new QCustomer().whenCreated.lt(whenUpdated).query(), " where t0.when_created < t0.when_updated"); } @Test @@ -563,7 +597,7 @@ public class QCustomerTest { assertContains(new QCustomer().registered.le(new Date()).query(), " where t0.registered <= ?"); assertContains(new QCustomer().registered.lessOrEqualTo(new Date()).query(), " where t0.registered <= ?"); - assertContains(new QCustomer().whenCreated.le(QCustomer.Alias.whenUpdated).query(), " where t0.when_created <= t0.when_updated"); + assertContains(new QCustomer().whenCreated.le(whenUpdated).query(), " where t0.when_created <= t0.when_updated"); } @Test @@ -571,7 +605,7 @@ public class QCustomerTest { assertContains(new QCustomer().registered.after(new Date()).query(), " where t0.registered > ?"); assertContains(new QCustomer().registered.gt(new Date()).query(), " where t0.registered > ?"); - assertContains(new QCustomer().whenCreated.gt(QCustomer.Alias.whenUpdated).query(), " where t0.when_created > t0.when_updated"); + assertContains(new QCustomer().whenCreated.gt(whenUpdated).query(), " where t0.when_created > t0.when_updated"); assertContains(new QCustomer().registered.greaterThan(new Date()).query(), " where t0.registered > ?"); } @@ -581,7 +615,7 @@ public class QCustomerTest { assertContains(new QCustomer().registered.ge(new Date()).query(), " where t0.registered >= ?"); assertContains(new QCustomer().registered.greaterOrEqualTo(new Date()).query(), " where t0.registered >= ?"); - assertContains(new QCustomer().whenCreated.ge(QCustomer.Alias.whenUpdated).query(), " where t0.when_created >= t0.when_updated"); + assertContains(new QCustomer().whenCreated.ge(whenUpdated).query(), " where t0.when_created >= t0.when_updated"); } private void assertContains(Query query, String match) { @@ -598,8 +632,6 @@ public class QCustomerTest { .findList(); } - - @Test public void select_assocManyToOne() { @@ -788,16 +820,16 @@ public class QCustomerTest { .findList(); new QCustomer() - .add(in(QCustomer.Alias.name, List.of("foo", "bar"))) - .add(eq(QCustomer.Alias.currentInet, Inet.of("127.0.0.1"))) + .add(in(name, List.of("foo", "bar"))) + .add(eq(currentInet, Inet.of("127.0.0.1"))) .findList(); new QCustomer() //.add(gt(sum(QCustomer.Alias.version), 45)) - .add(eq(QCustomer.Alias.version, 45L)) - .add(eq(QCustomer.Alias.name, "junk")) - .add(eq(QCustomer.Alias.registered, new Date())) - .add(eq(QCustomer.Alias.whenUpdated, new Timestamp(System.currentTimeMillis()))) + .add(eq(version, 45L)) + .add(eq(name, "junk")) + .add(eq(registered, new Date())) + .add(eq(whenUpdated, new Timestamp(System.currentTimeMillis()))) .findList(); } 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 b456dbeb6..bf506d4e0 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 @@ -21,9 +21,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"; String DATABASE = "io.ebean.Database"; String DB = "io.ebean.DB"; @@ -37,4 +35,7 @@ interface Constants { String AVAJE_LANG_NULLABLE = "io.avaje.lang.Nullable"; String JAVA_COLLECTION = "java.util.Collection"; + String EXPRESSIONLIST = "io.ebean.ExpressionList"; + String EXPR = "io.ebean.Expr"; + String CONSUMER = "java.util.function.Consumer"; } diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java index 416d09af9..631eb4d3f 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java @@ -361,20 +361,8 @@ class ProcessingContext implements Constants { */ private PropertyType createPropertyTypeAssoc(String fullName) { String[] split = Split.split(fullName); - String propertyName = "QAssoc" + split[1]; - String packageName = packageAppend(split[0]); - return new PropertyTypeAssoc(propertyName, packageName); - } - - /** - * Prepend the package to the suffix taking null into account. - */ - private String packageAppend(String origPackage) { - if (origPackage == null) { - return "query.assoc"; - } else { - return origPackage + "." + "query.assoc"; - } + String propertyName = "Q" + split[1] + ".Assoc"; + return new PropertyTypeAssoc(propertyName); } /** diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/Processor.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/Processor.java index e52f5a3e3..9579042a3 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/Processor.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/Processor.java @@ -114,7 +114,6 @@ public class Processor extends AbstractProcessor implements Constants { try { SimpleQueryBeanWriter beanWriter = new SimpleQueryBeanWriter((TypeElement) element, processingContext); beanWriter.writeRootBean(); - beanWriter.writeAssocBean(); } catch (Throwable e) { e.printStackTrace(); processingContext.logError(element, "Error generating query beans: " + e); diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyMeta.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyMeta.java index 6634f5b21..33ae06697 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyMeta.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyMeta.java @@ -31,14 +31,12 @@ class PropertyMeta { } void writeFieldDefn(Append writer, String shortName, boolean assoc) { - writer.append(" public "); writer.append(getTypeDefn(shortName, assoc)); writer.append(" ").append(name).append(";"); } void writeFieldAliasDefn(Append writer, String shortName) { - writer.append(" public static "); writer.append(getTypeDefn(shortName, false)); writer.append(" ").append(name).append(" = _alias.").append(name).append(";"); diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeAssoc.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeAssoc.java index c9b912886..6e6847c86 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeAssoc.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeAssoc.java @@ -7,20 +7,13 @@ import java.util.Set; */ class PropertyTypeAssoc extends PropertyType { - /** - * The package name for this associated query bean. - */ - private final String assocPackage; - /** * Construct given the associated bean type name and package. * * @param qAssocTypeName the associated bean type name. - * @param assocPackage the associated bean package. */ - PropertyTypeAssoc(String qAssocTypeName, String assocPackage) { + PropertyTypeAssoc(String qAssocTypeName) { super(qAssocTypeName); - this.assocPackage = assocPackage; } /** @@ -28,7 +21,7 @@ class PropertyTypeAssoc extends PropertyType { */ @Override void addImports(Set allImports) { - allImports.add(assocPackage + "." + propertyType); + // do nothing } } 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 b3d2d723d..8ad6f9783 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 @@ -8,7 +8,6 @@ import javax.tools.JavaFileObject; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -29,10 +28,8 @@ class SimpleQueryBeanWriter { private final String beanFullName; private final boolean isEntity; private final boolean embeddable; - private boolean writingAssocBean; private String destPackage; - private String origDestPackage; private String shortName; private final String shortInnerName; private final String origShortName; @@ -80,16 +77,24 @@ class SimpleQueryBeanWriter { private void gatherPropertyDetails() { importTypes.add(Constants.GENERATED); importTypes.add(beanFullName); + importTypes.add(Constants.TQASSOCBEAN); importTypes.add(Constants.TQROOTBEAN); importTypes.add(Constants.TYPEQUERYBEAN); importTypes.add(Constants.DATABASE); importTypes.add(Constants.FETCHGROUP); importTypes.add(Constants.QUERY); importTypes.add(Constants.TRANSACTION); + importTypes.add(Constants.CONSUMER); + importTypes.add(Constants.EXPR); + importTypes.add(Constants.EXPRESSIONLIST); if (implementsInterface != null) { implementsInterfaceFullName = implementsInterface.getQualifiedName().toString(); boolean nested = implementsInterface.getNestingKind().isNested(); implementsInterfaceShortName = Util.shortName(nested, implementsInterfaceFullName); + + importTypes.add(Constants.AVAJE_LANG_NULLABLE); + importTypes.add(Constants.JAVA_COLLECTION); + importTypes.add(implementsInterfaceFullName); } if (dbName != null) { importTypes.add(Constants.DB); @@ -101,7 +106,6 @@ class SimpleQueryBeanWriter { * Recursively add properties from the inheritance hierarchy. *

* Includes properties from mapped super classes and usual inheritance. - *

*/ private void addClassProperties() { for (VariableElement field : processingContext.allFields(element)) { @@ -131,87 +135,17 @@ class SimpleQueryBeanWriter { writeFields(); writeConstructors(); writeStaticAliasClass(); + writeAssocClass(); writeClassEnd(); writer.close(); } } - /** - * Write the type query assoc bean. - */ - void writeAssocBean() throws IOException { - writingAssocBean = true; - origDestPackage = destPackage; - destPackage = destPackage + ".assoc"; - shortName = "Assoc" + shortName; - - prepareAssocBeanImports(); - - writer = new Append(createFileWriter()); - - writePackage(); - writeImports(); - writeClass(); - writeFields(); - writeConstructors(); - writeClassEnd(); - - writer.close(); - } - - /** - * Prepare the imports for writing assoc bean. - */ - private void prepareAssocBeanImports() { - importTypes.remove(Constants.DB); - importTypes.remove(Constants.TQROOTBEAN); - importTypes.remove(Constants.DATABASE); - importTypes.remove(Constants.FETCHGROUP); - importTypes.remove(Constants.QUERY); - if (embeddable) { - importTypes.add(Constants.TQASSOC); - } else { - importTypes.add(Constants.TQASSOCBEAN); - } - if (isEntity()) { - importTypes.add(Constants.TQPROPERTY); - importTypes.add(origDestPackage + ".Q" + origShortName); - if (implementsInterface != null) { - importTypes.add(Constants.AVAJE_LANG_NULLABLE); - importTypes.add(Constants.JAVA_COLLECTION); - importTypes.add(implementsInterfaceFullName); - } - } - - // remove imports for the same package - Iterator importsIterator = importTypes.iterator(); - String checkImportStart = destPackage + ".QAssoc"; - while (importsIterator.hasNext()) { - String importType = importsIterator.next(); - if (importType.startsWith(checkImportStart)) { - importsIterator.remove(); - } - } - } - /** * Write constructors. */ private void writeConstructors() { - if (writingAssocBean) { - writeAssocBeanFetch(); - writeAssocBeanConstructor(); - } else { - writeRootBeanConstructor(); - } - } - - /** - * Write the constructors for 'root' type query bean. - */ - private void writeRootBeanConstructor() { - writer.eol(); writer.append(" /**").eol(); writer.append(" * Return a query bean used to build a FetchGroup.").eol(); @@ -240,9 +174,7 @@ class SimpleQueryBeanWriter { writer.eol(); String name = (dbName == null) ? "default" : dbName; - writer.append(" /**").eol(); - writer.append(" * Construct using the %s Database.", name).eol(); - writer.append(" */").eol(); + writer.append(" /** Construct using the %s Database */", name).eol(); writer.append(" public Q%s() {", shortName).eol(); if (dbName == null) { writer.append(" super(%s.class);", shortName).eol(); @@ -252,9 +184,7 @@ class SimpleQueryBeanWriter { writer.append(" }").eol(); writer.eol(); - writer.append(" /**").eol(); - writer.append(" * Construct with a given transaction.", name).eol(); - writer.append(" */").eol(); + writer.append(" /** Construct with a given transaction */").eol(); writer.append(" public Q%s(Transaction transaction) {", shortName).eol(); if (dbName == null) { writer.append(" super(%s.class, transaction);", shortName).eol(); @@ -264,29 +194,122 @@ class SimpleQueryBeanWriter { writer.append(" }").eol(); writer.eol(); - writer.append(" /**").eol(); - writer.append(" * Construct with a given Database.").eol(); - writer.append(" */").eol(); + writer.append(" /** Construct with a given Database */").eol(); writer.append(" public Q%s(Database database) {", shortName).eol(); writer.append(" super(%s.class, database);", shortName).eol(); writer.append(" }").eol(); writer.eol(); writer.eol(); - writer.append(" /**").eol(); - writer.append(" * Construct for Alias.").eol(); - writer.append(" */").eol(); + writer.append(" /** Private constructor for Alias */").eol(); writer.append(" private Q%s(boolean dummy) {", shortName).eol(); writer.append(" super(dummy);").eol(); writer.append(" }").eol(); writer.eol(); - writer.append(" /**").eol(); - writer.append(" * Private constructor for FetchGroup building.").eol(); - writer.append(" */").eol(); + writer.append(" /** Private constructor for FetchGroup building */").eol(); writer.append(" private Q%s(Query<%s> fetchGroupQuery) {", shortName, shortName).eol(); writer.append(" super(fetchGroupQuery);").eol(); writer.append(" }").eol(); + + writer.eol(); + writer.append(" /** Private constructor for filterMany */").eol(); + writer.append(" private Q%s(ExpressionList<%s> filter) {", shortName, shortName).eol(); + writer.append(" super(filter);").eol(); + writer.append(" }").eol(); + } + + /** + * Write all the fields. + */ + private void writeFields() { + for (PropertyMeta property : properties) { + property.writeFieldDefn(writer, shortName, false); + writer.eol(); + } + writer.eol(); + } + + private void writeClass() { + writer.append("/**").eol(); + writer.append(" * Query bean for %s.", shortName).eol(); + writer.append(" * ").eol(); + writer.append(" * THIS IS A GENERATED OBJECT, DO NOT MODIFY THIS CLASS.").eol(); + writer.append(" */").eol(); + writer.append(Constants.AT_GENERATED).eol(); + writer.append(Constants.AT_TYPEQUERYBEAN).eol(); + writer.append("public final class Q%s extends TQRootBean<%1$s,Q%1$s> {", shortName).eol(); + writer.eol(); + } + + private void writeAlias() { + writer.append(" private static final Q%s _alias = new Q%1$s(true);", shortName).eol().eol(); + + writer.append(" /**").eol(); + writer.append(" * Return the shared 'Alias' instance used to provide properties to ").eol(); + writer.append(" * select() and fetch() ").eol(); + writer.append(" */").eol(); + writer.append(" public static Q%s alias() {", shortName).eol(); + writer.append(" return _alias;").eol(); + writer.append(" }").eol(); + writer.eol(); + } + + private void writeStaticAliasClass() { + writer.eol(); + writer.append(" /**").eol(); + writer.append(" * Provides static properties to use in select() and fetch() ").eol(); + writer.append(" * clauses of a query. Typically referenced via static imports. ").eol(); + writer.append(" */").eol(); + writer.append(" ").append(Constants.AT_GENERATED).eol(); + writer.append(" public static final class Alias {").eol(); + for (PropertyMeta property : properties) { + property.writeFieldAliasDefn(writer, shortName); + writer.eol(); + } + writer.append(" }").eol(); + } + + private void writeAssocClass() { + writer.eol(); + writer.append(" /** Association query bean */").eol(); + writer.append(" ").append(Constants.AT_GENERATED).eol(); + writer.append(" ").append(Constants.AT_TYPEQUERYBEAN).eol(); + if (embeddable) { + writer.append(" public static final class Assoc extends TQAssoc<%s,R> {", shortName, shortInnerName).eol(); + } else { + writer.append(" public static final class Assoc extends TQAssocBean<%s,R,Q%s> {", shortName, shortInnerName, origShortName).eol(); + } + for (PropertyMeta property : properties) { + writer.append(" "); + property.writeFieldDefn(writer, shortName, true); + writer.eol(); + } + writer.eol(); + writeAssocBeanConstructor(); + writeAssocFilterMany(); + writeAssocBeanFetch(); + writer.append(" }").eol(); + } + + private void writeAssocFilterMany() { + writer.append(" public final R filterMany(Consumer apply) {", shortName).eol(); + writer.append(" final ExpressionList list = Expr.factory().expressionList();", shortName).eol(); + writer.append(" final var qb = new Q%s(list);", shortName).eol(); + writer.append(" apply.accept(qb);").eol(); + writer.append(" expr().filterMany(_name).addAll(list);").eol(); + writer.append(" return _root;").eol(); + writer.append(" }").eol(); + } + + private void writeAssocBeanConstructor() { + writer.append(" public Assoc(String name, R root) {").eol(); + writer.append(" super(name, root);").eol(); + writer.append(" }").eol().eol(); + + writer.append(" public Assoc(String name, R root, String prefix) {").eol(); + writer.append(" super(name, root, prefix);").eol(); + writer.append(" }").eol(); } private void writeAssocBeanFetch() { @@ -320,101 +343,6 @@ class SimpleQueryBeanWriter { writer.eol(); } - private void writeAssocBeanFetch(String fetchType, String comment) { - writer.append(" /**").eol(); - writer.append(" * ").append(comment).eol(); - writer.append(" */").eol(); - writer.append(" @SafeVarargs @SuppressWarnings(\"varargs\")").eol(); - writer.append(" public final R fetch%s(TQProperty... properties) {", fetchType, origShortName).eol(); - writer.append(" return fetch%sProperties(properties);", fetchType).eol(); - writer.append(" }").eol(); - writer.eol(); - } - - /** - * Write constructor for 'assoc' type query bean. - */ - private void writeAssocBeanConstructor() { - writer.append(" public Q%s(String name, R root) {", shortName).eol(); - writer.append(" super(name, root);").eol(); - writer.append(" }").eol().eol(); - - writer.append(" public Q%s(String name, R root, String prefix) {", shortName).eol(); - writer.append(" super(name, root, prefix);").eol(); - writer.append(" }").eol(); - } - - /** - * Write all the fields. - */ - private void writeFields() { - for (PropertyMeta property : properties) { - property.writeFieldDefn(writer, shortName, writingAssocBean); - writer.eol(); - } - writer.eol(); - } - - /** - * Write the class definition. - */ - private void writeClass() { - if (writingAssocBean) { - writer.append("/**").eol(); - writer.append(" * Association query bean for %s.", shortName).eol(); - writer.append(" * ").eol(); - writer.append(" * THIS IS A GENERATED OBJECT, DO NOT MODIFY THIS CLASS.").eol(); - writer.append(" */").eol(); - writer.append(Constants.AT_GENERATED).eol(); - writer.append(Constants.AT_TYPEQUERYBEAN).eol(); - if (embeddable) { - writer.append("public final class Q%s extends TQAssoc<%s,R> {", shortName, shortInnerName).eol(); - } else { - writer.append("public final 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(); - writer.append(" * ").eol(); - writer.append(" * THIS IS A GENERATED OBJECT, DO NOT MODIFY THIS CLASS.").eol(); - writer.append(" */").eol(); - writer.append(Constants.AT_GENERATED).eol(); - writer.append(Constants.AT_TYPEQUERYBEAN).eol(); - writer.append("public final class Q%s extends TQRootBean<%1$s,Q%1$s> {", shortName).eol(); - } - - writer.eol(); - } - - private void writeAlias() { - if (!writingAssocBean) { - writer.append(" private static final Q%s _alias = new Q%1$s(true);", shortName).eol().eol(); - - writer.append(" /**").eol(); - writer.append(" * Return the shared 'Alias' instance used to provide properties to ").eol(); - writer.append(" * select() and fetch() ").eol(); - writer.append(" */").eol(); - writer.append(" public static Q%s alias() {", shortName).eol(); - writer.append(" return _alias;").eol(); - writer.append(" }").eol(); - writer.eol(); - } - } - - private void writeStaticAliasClass() { - writer.eol(); - writer.append(" /**").eol(); - writer.append(" * Provides static properties to use in select() and fetch() ").eol(); - writer.append(" * clauses of a query. Typically referenced via static imports. ").eol(); - writer.append(" */").eol(); - writer.append(" public static class Alias {").eol(); - for (PropertyMeta property : properties) { - property.writeFieldAliasDefn(writer, shortName); - writer.eol(); - } - writer.append(" }").eol(); - } - private void writeClassEnd() { writer.append("}").eol(); } @@ -433,7 +361,6 @@ class SimpleQueryBeanWriter { writer.append("package %s;", destPackage).eol().eol(); } - private Writer createFileWriter() throws IOException { JavaFileObject jfo = processingContext.createWriter(destPackage + "." + "Q" + shortName, element); return jfo.openWriter();