From b066f9c072a41d1ced2495cdff0aee13419910fa Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sun, 9 Feb 2025 21:24:51 +1300 Subject: [PATCH] #3536 kotlin-querybean-generator update for filterMany() changes Brings the kotlin-querybean-generator up to date with the [java] querybean-generator in terms of how it has specific "associated beans" for ToOne and ToMany type relationships --- .../ebean/querybean/generator/Constants.java | 6 +- .../generator/KotlinLangAdapter.java | 78 ++----- .../querybean/generator/LangAdapter.java | 16 -- .../generator/ProcessingContext.java | 79 ++++--- .../ebean/querybean/generator/Processor.java | 3 +- .../generator/PropertyTypeAssoc.java | 17 +- .../generator/SimpleQueryBeanWriter.java | 210 ++++++++---------- tests/test-kotlin/pom.xml | 2 +- .../kotlin/org/example/domain/MyInnerEmb.kt | 17 ++ 9 files changed, 179 insertions(+), 249 deletions(-) delete mode 100644 kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/LangAdapter.java create mode 100644 tests/test-kotlin/src/main/kotlin/org/example/domain/MyInnerEmb.kt 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 5d7938458..e353cbd3b 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 @@ -11,6 +11,8 @@ interface Constants { String ENTITY = "jakarta.persistence.Entity"; String EMBEDDABLE = "jakarta.persistence.Embeddable"; String CONVERTER = "jakarta.persistence.Converter"; + String ONE_TO_MANY = "jakarta.persistence.OneToMany"; + String MANY_TO_MANY = "jakarta.persistence.ManyToMany"; String EBEAN_COMPONENT = "io.ebean.annotation.EbeanComponent"; String DBARRAY = "io.ebean.annotation.DbArray"; @@ -18,10 +20,6 @@ interface Constants { String DBJSONB = "io.ebean.annotation.DbJsonB"; String DBNAME = "io.ebean.annotation.DbName"; - String TQASSOC = "io.ebean.typequery.TQAssoc"; - String TQASSOCBEAN = "io.ebean.typequery.TQAssocBean"; - String TQPROPERTY = "io.ebean.typequery.TQProperty"; - String MODULEINFO = "io.ebean.config.ModuleInfo"; String METAINF_MANIFEST = "META-INF/ebean-generated-info.mf"; String METAINF_SERVICES_MODULELOADER = "META-INF/services/io.ebean.config.EntityClassRegister"; 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 08c03cad8..d8e72a490 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 @@ -1,9 +1,8 @@ package io.ebean.querybean.generator; -class KotlinLangAdapter implements LangAdapter { +final class KotlinLangAdapter { - @Override - public void alias(Append writer, String shortName, String fullName) { + void alias(Append writer, String shortName, String fullName) { writer.append(" companion object {").eol(); writer.append(" /**").eol(); writer.append(" * shared 'Alias' instance used to provide").eol(); @@ -20,87 +19,38 @@ class KotlinLangAdapter implements LangAdapter { writer.append(" }").eol().eol(); } - @Override - public void assocBeanConstructor(Append writer, String shortName) { - writer.append(" constructor(name: String, root: R) : super(name, root)").eol(); - writer.eol(); - writer.append(" constructor(name: String, root: R, prefix: String) : super(name, root, prefix)").eol(); - } - - @Override - public void fetch(Append writer, String origShortName) { - writeAssocBeanFetch(writer, origShortName, "", "Eagerly fetch this association loading the specified properties."); - writeAssocBeanFetch(writer, origShortName, "Query", "Eagerly fetch this association using a 'query join' loading the specified properties."); - writeAssocBeanFetch(writer, origShortName, "Cache", "Eagerly fetch this association using L2 cache."); - writeAssocBeanFetch(writer, origShortName, "Lazy", "Use lazy loading for this association loading the specified properties."); - } - - private void writeAssocBeanFetch(Append writer, String origShortName, String fetchType, String comment) { -// fun fetch(vararg properties: TQProperty): R { -// return fetchProperties(*properties) -// } - writer.append(" /**").eol(); - writer.append(" * ").append(comment).eol(); - writer.append(" */").eol(); - writer.append(" fun fetch%s(vararg properties: TQProperty) : R {", fetchType, origShortName).eol(); - writer.append(" return fetch%sProperties(*properties)", fetchType).eol(); - writer.append(" }").eol(); - writer.eol(); - } - - @Override - public void rootBeanConstructor(Append writer, String shortName, String dbName, String fullName) { + void rootBeanConstructor(Append writer, String shortName, String dbName, String fullName) { 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(); if (dbName == null) { writer.append(" constructor() : super(%s::class.java)", fullName).eol().eol(); } else { writer.append(" constructor() : super(%s::class.java, io.ebean.DB.byName(\"%s\"))", fullName, dbName).eol().eol(); } - writer.append(" /**").eol(); - writer.append(" * @deprecated migrate to query.usingTransaction()", name).eol(); - writer.append(" */").eol(); - writer.append(" @Deprecated(message=\"migrate to query.usingTransaction()\")").eol(); + writer.append(" /** @deprecated migrate to query.usingTransaction() */", name).eol(); + writer.append(" @Deprecated(message=\"migrate to query.usingTransaction()\")").eol(); if (dbName == null) { writer.append(" constructor(transaction: io.ebean.Transaction) : super(%s::class.java, transaction)", fullName).eol().eol(); } else { writer.append(" constructor(transaction: io.ebean.Transaction) : super(%s::class.java, io.ebean.DB.byName(\"%s\"), transaction)", fullName, dbName).eol().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(" constructor(database: io.ebean.Database) : super(%s::class.java, database)", fullName).eol().eol(); - writer.append(" /**").eol(); - writer.append(" * Construct for Alias.").eol(); - writer.append(" */").eol(); + writer.append(" /** Construct for Alias. */").eol(); writer.append(" private constructor(dummy: Boolean) : super(dummy)").eol().eol(); - writer.append(" /**").eol(); - writer.append(" * Private constructor for FetchGroup building.").eol(); - writer.append(" */").eol(); - writer.append(" private constructor(fetchGroupQuery: io.ebean.Query<%s>) : super(fetchGroupQuery)", fullName).eol(); + writer.append(" /** Private constructor for FetchGroup building. */").eol(); + writer.append(" private constructor(fetchGroupQuery: io.ebean.Query<%s>) : super(fetchGroupQuery)", fullName).eol().eol(); + + writer.append(" /** Private constructor for filterMany */").eol(); + writer.append(" private constructor(filter: io.ebean.ExpressionList<%s>) : super(filter)", fullName).eol().eol(); - writer.eol(); writer.append(" /** Return a copy of the query. */").eol(); writer.append(" override fun copy() : Q%s {", shortName).eol(); writer.append(" return Q%s(query().copy())", shortName).eol(); - writer.append(" }").eol(); - writer.eol(); + writer.append(" }").eol().eol(); } - - @Override - public void fieldDefn(Append writer, String propertyName, String typeDefn) { - writer.append(" lateinit var %s: ", propertyName); - if (typeDefn.endsWith(",Integer>")) { - typeDefn = typeDefn.replace(",Integer>", ",Int>"); - } - writer.append(typeDefn); - } - } diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/LangAdapter.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/LangAdapter.java deleted file mode 100644 index 9bb48e39d..000000000 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/LangAdapter.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.ebean.querybean.generator; - -public interface LangAdapter { - - - void alias(Append writer, String shortName, String beanFullName); - - void rootBeanConstructor(Append writer, String shortName, String dbName, String beanFullName); - - void assocBeanConstructor(Append writer, String shortName); - - void fetch(Append writer, String origShortName); - - void fieldDefn(Append writer, String propertyName, String typeDefn); - -} diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java index 2af114077..3e8381d47 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java @@ -239,6 +239,10 @@ class ProcessingContext implements Constants { return hasAnnotations(element, EMBEDDABLE); } + private static boolean dbToMany(Element field) { + return hasAnnotations(field, ONE_TO_MANY, MANY_TO_MANY); + } + /** * Find the DbName annotation and return name if found. */ @@ -261,6 +265,7 @@ class ProcessingContext implements Constants { } PropertyType getPropertyType(VariableElement field) { + boolean toMany = dbToMany(field); if (dbJsonField(field)) { return propertyTypeMap.getDbJsonType(); } @@ -301,36 +306,30 @@ class ProcessingContext implements Constants { if (targetEntity != null) { final TypeElement element = elementUtils.getTypeElement(targetEntity); if (isEntityOrEmbedded(element)) { - return createPropertyTypeAssoc(typeDef(element.asType())); + boolean embeddable = isEmbeddable(element); + return createPropertyTypeAssoc(embeddable, toMany, typeDef(element.asType())); } } if (isEntityOrEmbedded(fieldType)) { - // public QAssocContact contacts; - return createPropertyTypeAssoc(typeDef(typeMirror)); + boolean embeddable = isEmbeddable(fieldType); + return createPropertyTypeAssoc(embeddable, toMany, typeDef(typeMirror)); } + final PropertyType result; if (typeMirror.getKind() == TypeKind.DECLARED) { - DeclaredType declaredType = (DeclaredType) typeMirror; - List typeArguments = declaredType.getTypeArguments(); - if (typeArguments.size() == 1) { - TypeMirror argType = typeArguments.get(0); - Element argElement = asElement(argType); - if (isEntityOrEmbedded(argElement)) { - return createPropertyTypeAssoc(typeDef(argElement.asType())); - } - } else if (typeArguments.size() == 2) { - TypeMirror argType = typeArguments.get(1); - Element argElement = asElement(argType); - if (isEntityOrEmbedded(argElement)) { - return createPropertyTypeAssoc(typeDef(argElement.asType())); - } - } + result = createManyTypeAssoc(field, (DeclaredType) typeMirror); + } else { + result = null; } - if (typeInstanceOf(typeMirror, "java.lang.Comparable")) { - return new PropertyTypeScalarComparable(trimAnnotations(typeMirror.toString())); + if (result != null) { + return result; } else { - return new PropertyTypeScalar(trimAnnotations(typeMirror.toString())); + if (typeInstanceOf(typeMirror, "java.lang.Comparable")) { + return new PropertyTypeScalarComparable(trimAnnotations(typeMirror.toString())); + } else { + return new PropertyTypeScalar(trimAnnotations(typeMirror.toString())); + } } } @@ -357,6 +356,25 @@ class ProcessingContext implements Constants { .anyMatch(t -> typeInstanceOf(t, desiredInterface)); } + private PropertyType createManyTypeAssoc(VariableElement field, DeclaredType declaredType) { + boolean toMany = dbToMany(field); + List typeArguments = declaredType.getTypeArguments(); + if (typeArguments.size() == 1) { + Element argElement = typeUtils.asElement(typeArguments.get(0)); + if (isEntityOrEmbedded(argElement)) { + boolean embeddable = isEmbeddable(argElement); + return createPropertyTypeAssoc(embeddable, toMany, typeDef(argElement.asType())); + } + } else if (typeArguments.size() == 2) { + Element argElement = typeUtils.asElement(typeArguments.get(1)); + if (isEntityOrEmbedded(argElement)) { + boolean embeddable = isEmbeddable(argElement); + return createPropertyTypeAssoc(embeddable, toMany, typeDef(argElement.asType())); + } + } + return null; + } + private String readTargetEntity(Element declaredType) { for (AnnotationMirror annotation : declaredType.getAnnotationMirrors()) { final Object targetEntity = readTargetEntityFromAnnotation(annotation); @@ -386,11 +404,20 @@ class ProcessingContext implements Constants { /** * Create the QAssoc PropertyType. */ - 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); + private PropertyType createPropertyTypeAssoc(boolean embeddable, boolean toMany, String fullName) { + TypeElement typeElement = elementUtils.getTypeElement(fullName); + String type; + if (typeElement.getNestingKind().isNested()) { + type = typeElement.getEnclosingElement().toString() + "$" + typeElement.getSimpleName(); + } else { + type = typeElement.getQualifiedName().toString(); + } + + String suffix = toMany ? "Many" : embeddable ? "": "One"; + String[] split = Split.split(type); + String propertyName = "Q" + split[1] + ".Assoc" + suffix; + String importName = split[0] + ".query.Q" + split[1]; + return new PropertyTypeAssoc(propertyName, importName); } /** diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Processor.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Processor.java index 6986138ca..ef0dd0de4 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Processor.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Processor.java @@ -109,8 +109,7 @@ public class Processor extends AbstractProcessor implements Constants { private void generateQueryBeans(Element element) { try { SimpleQueryBeanWriter beanWriter = new SimpleQueryBeanWriter((TypeElement) element, processingContext); - beanWriter.writeRootBean(); - beanWriter.writeAssocBean(); + beanWriter.writeBean(); } catch (Throwable e) { processingContext.logError(element, "Error generating query beans: " + e); } diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeAssoc.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeAssoc.java index 07611b782..e3c8943e5 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeAssoc.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeAssoc.java @@ -7,25 +7,16 @@ import java.util.Set; */ class PropertyTypeAssoc extends PropertyType { - /** - * The package name for this associated query bean. - */ - private final String assocPackage; + private final String importName; - /** - * 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, String importName) { super(qAssocTypeName); - this.assocPackage = assocPackage; + this.importName = importName; } @Override void addImports(Set allImports) { - allImports.add(assocPackage + "." + propertyType); + allImports.add(importName); } } 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 7737d33a3..9d9901d52 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 @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -74,19 +73,17 @@ class SimpleQueryBeanWriter { private final boolean embeddable; private final String dbName; private final String beanFullName; - private final LangAdapter langAdapter; - private boolean writingAssocBean; + private final KotlinLangAdapter langAdapter = new KotlinLangAdapter(); + private boolean writingEmbeddedBean; private final String generatedSourcesDir; private String destPackage; - private String origDestPackage; private String shortName; private final String shortInnerName; private final String origShortName; private Append writer; SimpleQueryBeanWriter(TypeElement element, ProcessingContext processingContext) { - this.langAdapter = new KotlinLangAdapter(); this.generatedSourcesDir = processingContext.generatedSourcesDir(); this.element = element; this.processingContext = processingContext; @@ -95,7 +92,7 @@ class SimpleQueryBeanWriter { this.destPackage = Util.packageOf(nested, beanFullName) + ".query"; String sn = Util.shortName(nested, beanFullName); this.shortInnerName = Util.shortName(false, sn); - this.shortName = sn.replace(".", ""); + this.shortName = sn.replace(".", "_"); // $ not supported with Kotlin, see kotlinInnerType() this.origShortName = shortName; this.isEntity = processingContext.isEntity(element); this.embeddable = processingContext.isEmbeddable(element); @@ -118,8 +115,8 @@ class SimpleQueryBeanWriter { return processingContext.findDbName(element); } - private LangAdapter lang() { - return langAdapter; + private static String kotlinInnerType(String fullType) { + return fullType.replace('$', '_'); } private void gatherPropertyDetails() { @@ -148,25 +145,24 @@ class SimpleQueryBeanWriter { } /** - * Write the type query bean (root bean). + * Write the type query bean. */ - void writeRootBean() throws IOException { + void writeBean() throws IOException { gatherPropertyDetails(); + translateKotlinImportTypes(); if (isEmbeddable()) { processingContext.addEntity(beanFullName, dbName); + writeEmbeddedBean(); } else if (isEntity()) { processingContext.addEntity(beanFullName, dbName); writer = new Append(createFileWriter()); - translateKotlinImportTypes(); - writePackage(); writeImports(); writeClass(); writeAlias(); - writeFields(); + writeFields(false); writeConstructors(); - //writeStaticAliasClass(); writeClassEnd(); writer.close(); @@ -196,139 +192,53 @@ class SimpleQueryBeanWriter { /** * Write the type query assoc bean. */ - void writeAssocBean() throws IOException { - writingAssocBean = true; - origDestPackage = destPackage; - destPackage = destPackage + ".assoc"; - shortName = "Assoc" + shortName; + void writeEmbeddedBean() throws IOException { + writingEmbeddedBean = true; - prepareAssocBeanImports(); writer = new Append(createFileWriter()); - writePackage(); writeImports(); writeClass(); - writeFields(); - writeConstructors(); + writeEmbeddedAssoc(); writeClassEnd(); writer.close(); } - /** - * Prepare the imports for writing assoc bean. - */ - private void prepareAssocBeanImports() { - 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(); - } + langAdapter.rootBeanConstructor(writer, shortName, dbName, beanFullName); + writeAssocClasses(); } - /** - * Write the constructors for 'root' type query bean. - */ - private void writeRootBeanConstructor() { - lang().rootBeanConstructor(writer, shortName, dbName, beanFullName); - } - - private void writeAssocBeanFetch() { - if (isEntity()) { - //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."); - // writeAssocBeanExpression(false, "in", "IN the given values.", implementsInterfaceShortName + "...", "in"); - // writeAssocBeanExpression(false, "inBy", "IN the given interface values.", "Collection<" + implementsInterfaceShortName + ">", "in"); - // writeAssocBeanExpression(true, "inOrEmptyBy", "IN the given interface values if the collection is not empty. No expression is added if the collection is empty..", "Collection<" + implementsInterfaceShortName + ">", "inOrEmpty"); - //} - } - } - - private void writeAssocBeanExpression(boolean nullable,String expression, String comment) { - writeAssocBeanExpression(nullable, expression, comment, implementsInterfaceShortName, expression); - } - - private void writeAssocBeanExpression(boolean nullable, String expression, String comment, String param, String actualExpression) { - final String nullableAnnotation = nullable ? "@Nullable " : ""; - String values = expression.startsWith("in") ? "values" : "value"; - writer.append(" /**").eol(); - writer.append(" * ").append(comment).eol(); - writer.append(" */").eol(); - writer.append(" fun %s(%s%s %s): R {", expression, nullableAnnotation, param, values).eol(); - writer.append(" expr().%s(_name, %s);", actualExpression, values).eol(); - writer.append(" return _root;").eol(); - writer.append(" }").eol(); - writer.eol(); - } - - /** - * Write constructor for 'assoc' type query bean. - */ - private void writeAssocBeanConstructor() { - lang().assocBeanConstructor(writer, shortName); - } - - /** - * Write all the fields. - */ - private void writeFields() { + private void writeFields(boolean assocBeans) { + String padding = assocBeans ? " " : ""; for (PropertyMeta property : properties) { - String typeDefn = property.getTypeDefn(shortName, writingAssocBean); - lang().fieldDefn(writer, property.getName(), typeDefn); - writer.eol(); + String typeDefn = kotlinTypeDefn(property.getTypeDefn(shortName, assocBeans)); + writer.append("%s lateinit var %s: %s", padding, property.getName(), kotlinInnerType(typeDefn)).eol(); } writer.eol(); } + private static String kotlinTypeDefn(String type) { + if (type.endsWith(",Integer>")) { + return type.replace(",Integer>", ",Int>"); + } else { + return type; + } + } + /** * Write the class definition. */ private void writeClass() { - if (writingAssocBean) { + if (writingEmbeddedBean) { 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("class Q%s : TQAssoc<%s,R> {", shortName, beanFullName).eol(); - } else { - writer.append("class Q%s : TQAssocBean<%s,R,Q%s> {", shortName, beanFullName, origShortName).eol(); - } + writer.append("class Q%s {", shortName).eol(); } else { writer.append("/**").eol(); writer.append(" * Query bean for %s.", shortName).eol(); @@ -344,9 +254,7 @@ class SimpleQueryBeanWriter { } private void writeAlias() { - if (!writingAssocBean) { - lang().alias(writer, shortName, beanFullName); - } + langAdapter.alias(writer, shortName, beanFullName); } private void writeClassEnd() { @@ -358,7 +266,7 @@ class SimpleQueryBeanWriter { */ private void writeImports() { for (String importType : importTypes) { - writer.append("import %s;", importType).eol(); + writer.append("import %s;", kotlinInnerType(importType)).eol(); } writer.eol(); } @@ -379,4 +287,60 @@ class SimpleQueryBeanWriter { return new FileWriter(absFile); } + private void writeEmbeddedAssoc() { + writer.append(" @io.ebean.typequery.Generated(\"io.ebean.querybean.generator\") @io.ebean.typequery.TypeQueryBean(\"v1\")").eol(); + writer.append(" class Assoc : io.ebean.typequery.TQAssoc<%s,R> {", beanFullName).eol().eol(); + writeFields(true); + writer.append(" protected constructor(name: String, root: R) : super(name, root)").eol(); + writer.append(" protected constructor(name: String, root: R, prefix: String) : super(name, root, prefix)").eol(); + writer.append(" }").eol().eol(); + } + + private void writeAssocClasses() { + writer.append(" @io.ebean.typequery.Generated(\"io.ebean.querybean.generator\") @io.ebean.typequery.TypeQueryBean(\"v1\")").eol(); + writer.append(" abstract class Assoc : io.ebean.typequery.TQAssocBean<%s, R, Q%s> {", beanFullName, shortName).eol(); + + writeFields(true); + writer.append(" protected constructor(name: String, root: R) : super(name, root)").eol(); + writer.append(" protected constructor(name: String, root: R, prefix: String) : super(name, root, prefix)").eol(); + writer.append(" }").eol().eol(); + + writer.append(" @io.ebean.typequery.Generated(\"io.ebean.querybean.generator\") @io.ebean.typequery.TypeQueryBean(\"v1\")").eol(); + writer.append(" class AssocOne : Assoc {").eol(); + writer.append(" constructor(name: String, root: R) : super(name, root)").eol(); + writer.append(" constructor(name: String, root: R, prefix: String) : super(name, root, prefix)").eol(); + writer.append(" }").eol().eol(); + + writer.append(" @io.ebean.typequery.Generated(\"io.ebean.querybean.generator\") @io.ebean.typequery.TypeQueryBean(\"v1\")").eol(); + writer.append(" class AssocMany : Assoc, io.ebean.typequery.TQAssocMany<%s, R, Q%s> {", beanFullName, shortName).eol(); + writer.append(" constructor(name: String, root: R) : super(name, root)").eol(); + writer.append(" constructor(name: String, root: R, prefix: String) : super(name, root, prefix)").eol(); + writer.eol(); + writer.append(" override fun filterMany(apply: java.util.function.Consumer): R {", shortName).eol(); + writer.append(" val list: io.ebean.ExpressionList<%s> = _newExpressionList<%s>()", beanFullName, beanFullName).eol(); + writer.append(" apply.accept(Q%s(list))", shortName).eol(); + writer.append(" return _filterMany(list)").eol(); + writer.append(" }").eol().eol(); + writer.append(" override fun filterMany(filter: io.ebean.ExpressionList<%s>): R {", beanFullName).eol(); + writer.append(" return _filterMany(filter)").eol(); + writer.append(" }").eol().eol(); + writer.append(" override fun filterManyRaw(rawExpressions: String, vararg params: Any): R {").eol(); + writer.append(" return _filterManyRaw(rawExpressions, *params)").eol(); + writer.append(" }").eol().eol(); + + // Ebean 14.x only + writer.append(" @Deprecated(\"for removal, migrate to filterManyRaw()\")").eol(); + writer.append(" override fun filterMany(rawExpressions: String, vararg params: Any): R {").eol(); + writer.append(" return _filterMany(rawExpressions, *params)").eol(); + writer.append(" }").eol().eol(); + + writer.append(" override fun isEmpty(): R {").eol(); + writer.append(" return _isEmpty() ").eol(); + writer.append(" }").eol().eol(); + writer.append(" override fun isNotEmpty(): R {").eol(); + writer.append(" return _isNotEmpty() ").eol(); + writer.append(" }").eol().eol(); + + writer.append(" }").eol(); + } } diff --git a/tests/test-kotlin/pom.xml b/tests/test-kotlin/pom.xml index 2c8ef66d8..4029e8986 100644 --- a/tests/test-kotlin/pom.xml +++ b/tests/test-kotlin/pom.xml @@ -92,7 +92,7 @@ io.ebean kotlin-querybean-generator - 14.8.0 + 14.8.1 diff --git a/tests/test-kotlin/src/main/kotlin/org/example/domain/MyInnerEmb.kt b/tests/test-kotlin/src/main/kotlin/org/example/domain/MyInnerEmb.kt new file mode 100644 index 000000000..0e4f948b8 --- /dev/null +++ b/tests/test-kotlin/src/main/kotlin/org/example/domain/MyInnerEmb.kt @@ -0,0 +1,17 @@ +package org.example.domain + +import jakarta.persistence.* + +@Entity +@Table(name = "t_inner2") +class MyKInnerEmb { + + @Id + var one: Long = 0 + var two: String = "0" + var address: EmbAddre? = null + + @Embeddable + data class EmbAddre(val line1: String, val line2: String) + +}