Merge pull request #2986 from ebean-orm/feature/querybeans-assoc-supportInterfaceUse

[querybeans] Add support for using interface types with querybean expressions eq() in() etc WHEN entity beans implement an interface
This commit is contained in:
Rob Bygrave
2023-02-27 15:29:07 +13:00
committed by GitHub
9 changed files with 142 additions and 3 deletions
@@ -31,4 +31,7 @@ interface Constants {
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";
String AVAJE_LANG_NULLABLE = "io.avaje.lang.Nullable";
String JAVA_COLLECTION = "java.util.Collection";
}
@@ -334,7 +334,7 @@ class ProcessingContext implements Constants {
}
}
private Element asElement(TypeMirror argType) {
Element asElement(TypeMirror argType) {
if (argType.getKind() == TypeKind.WILDCARD) {
argType = ((WildcardType) argType).getExtendsBound();
}
@@ -3,6 +3,7 @@ package io.ebean.querybean.generator;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@@ -65,6 +66,9 @@ class SimpleQueryBeanWriter {
private final Set<String> importTypes = new TreeSet<>();
private final List<PropertyMeta> properties = new ArrayList<>();
private final TypeElement element;
private final TypeElement implementsInterface;
private String implementsInterfaceFullName;
private String implementsInterfaceShortName;
private final ProcessingContext processingContext;
private final boolean isEntity;
private final boolean embeddable;
@@ -96,6 +100,18 @@ class SimpleQueryBeanWriter {
this.isEntity = processingContext.isEntity(element);
this.embeddable = processingContext.isEmbeddable(element);
this.dbName = findDbName();
this.implementsInterface = initInterface(element);
}
private TypeElement initInterface(TypeElement element) {
for (TypeMirror anInterface : element.getInterfaces()) {
TypeElement e = (TypeElement)processingContext.asElement(anInterface);
String name = e.getQualifiedName().toString();
if (!name.startsWith("java") && !name.startsWith("io.ebean")) {
return e;
}
}
return null;
}
private String findDbName() {
@@ -115,6 +131,11 @@ class SimpleQueryBeanWriter {
importTypes.add(Constants.FETCHGROUP);
importTypes.add(Constants.QUERY);
importTypes.add(Constants.TRANSACTION);
if (implementsInterface != null) {
implementsInterfaceFullName = implementsInterface.getQualifiedName().toString();
boolean nested = implementsInterface.getNestingKind().isNested();
implementsInterfaceShortName = Util.shortName(nested, implementsInterfaceFullName);
}
if (dbName != null) {
importTypes.add(Constants.DB);
}
@@ -219,6 +240,11 @@ class SimpleQueryBeanWriter {
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
@@ -255,9 +281,33 @@ 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.");
// 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.
*/
@@ -33,4 +33,6 @@ interface Constants {
String METAINF_MANIFEST = "META-INF/ebean-generated-info.mf";
String METAINF_SERVICES_MODULELOADER = "META-INF/services/io.ebean.config.EntityClassRegister";
String AVAJE_LANG_NULLABLE = "io.avaje.lang.Nullable";
String JAVA_COLLECTION = "java.util.Collection";
}
@@ -541,4 +541,8 @@ class ProcessingContext implements Constants {
}
return null;
}
Element asElement(TypeMirror mirror) {
return typeUtils.asElement(mirror);
}
}
@@ -3,6 +3,7 @@ package io.ebean.querybean.generator;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
@@ -20,6 +21,9 @@ class SimpleQueryBeanWriter {
private final Set<String> importTypes = new TreeSet<>();
private final List<PropertyMeta> properties = new ArrayList<>();
private final TypeElement element;
private final TypeElement implementsInterface;
private String implementsInterfaceFullName;
private String implementsInterfaceShortName;
private final ProcessingContext processingContext;
private final String dbName;
private final String beanFullName;
@@ -47,6 +51,18 @@ class SimpleQueryBeanWriter {
this.isEntity = processingContext.isEntity(element);
this.embeddable = processingContext.isEmbeddable(element);
this.dbName = findDbName();
this.implementsInterface = initInterface(element);
}
private TypeElement initInterface(TypeElement element) {
for (TypeMirror anInterface : element.getInterfaces()) {
TypeElement e = (TypeElement)processingContext.asElement(anInterface);
String name = e.getQualifiedName().toString();
if (!name.startsWith("java") && !name.startsWith("io.ebean")) {
return e;
}
}
return null;
}
private String findDbName() {
@@ -70,7 +86,11 @@ class SimpleQueryBeanWriter {
importTypes.add(Constants.FETCHGROUP);
importTypes.add(Constants.QUERY);
importTypes.add(Constants.TRANSACTION);
if (implementsInterface != null) {
implementsInterfaceFullName = implementsInterface.getQualifiedName().toString();
boolean nested = implementsInterface.getNestingKind().isNested();
implementsInterfaceShortName = Util.shortName(nested, implementsInterfaceFullName);
}
if (dbName != null) {
importTypes.add(Constants.DB);
}
@@ -154,6 +174,11 @@ class SimpleQueryBeanWriter {
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
@@ -267,9 +292,34 @@ class SimpleQueryBeanWriter {
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.");
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<? extends " + 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<? extends " + 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";
String castVarargs = expression.equals("in") ? "(Object[])" : "";
writer.append(" /**").eol();
writer.append(" * ").append(comment).eol();
writer.append(" */").eol();
writer.append(" public final R %s(%s%s %s) {", expression, nullableAnnotation, param, values).eol();
writer.append(" expr().%s(_name, %s%s);", actualExpression, castVarargs, values).eol();
writer.append(" return _root;").eol();
writer.append(" }").eol();
writer.eol();
}
private void writeAssocBeanFetch(String fetchType, String comment) {
writer.append(" /**").eol();
writer.append(" * ").append(comment).eol();
+24
View File
@@ -21,6 +21,12 @@
</properties>
<dependencies>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>13.13.2</version>
</dependency>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
@@ -81,6 +87,24 @@
<goal>test-compile</goal>
</goals>
</execution>
<!-- <execution>-->
<!-- <id>test-kapt</id>-->
<!-- <goals>-->
<!-- <goal>test-kapt</goal>-->
<!-- </goals>-->
<!-- <configuration>-->
<!-- <sourceDirs>-->
<!-- <sourceDir>src/test/kotlin</sourceDir>-->
<!-- </sourceDirs>-->
<!-- <annotationProcessorPaths>-->
<!-- <annotationProcessorPath>-->
<!-- <groupId>io.ebean</groupId>-->
<!-- <artifactId>kotlin-querybean-generator</artifactId>-->
<!-- <version>13.13.2</version>-->
<!-- </annotationProcessorPath>-->
<!-- </annotationProcessorPaths>-->
<!-- </configuration>-->
<!-- </execution>-->
</executions>
</plugin>
<plugin>
@@ -0,0 +1,5 @@
package org.example.api
interface ICustomer {
}
@@ -3,11 +3,12 @@ package org.example.order
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Version
import org.example.api.ICustomer
@Entity
class Customer(
name: String,
) {
) : ICustomer {
@Id
var id: Int = 0
val name: String = name