#2692 - [querybean generation] Compile error, incorrect generation with nested inner class/record @Embeddable and @IdClass

This commit is contained in:
Rob Bygrave
2022-05-16 18:48:42 +12:00
parent b7d6374370
commit 9dea51b7a0
13 changed files with 316 additions and 68 deletions
@@ -0,0 +1,69 @@
package org.example.domain;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import java.util.Objects;
@Entity
@IdClass(MyInner.ID.class)
public class MyInner {
@Embeddable
public static class ID {
final long id;
final String one;
public ID(long id, String one) {
this.id = id;
this.one = one;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ID id1 = (ID) o;
return id == id1.id && one.equals(id1.one);
}
@Override
public int hashCode() {
return Objects.hash(id, one);
}
}
@Id
long id;
@Id
String one;
String description;
public long id() {
return id;
}
public MyInner id(long id) {
this.id = id;
return this;
}
public String one() {
return one;
}
public MyInner one(String beanOne) {
this.one = beanOne;
return this;
}
public String description() {
return description;
}
public MyInner description(String description) {
this.description = description;
return this;
}
}
@@ -0,0 +1,30 @@
package org.querytest;
import io.ebean.DB;
import org.example.domain.MyInner;
import org.example.domain.query.QMyInner;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class MyInnerTest {
@Test
void insert_and_find() {
MyInner myInner = new MyInner()
.id(42).one("one").description("foo");
DB.save(myInner);
MyInner found = DB.find(MyInner.class, new MyInner.ID(42, "one"));
assertThat(found).isNotNull();
MyInner found2 = new QMyInner()
.id.eq(42)
.one.eq("one")
.findOne();
assertThat(found2).isNotNull();
}
}
+11 -4
View File
@@ -12,7 +12,7 @@
<artifactId>kotlin-querybean-generator</artifactId>
<properties>
<kotlin.version>1.6.0</kotlin.version>
<kotlin.version>1.6.20</kotlin.version>
</properties>
<dependencies>
@@ -61,6 +61,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>13.6.2-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
@@ -106,7 +113,7 @@
<annotationProcessorPath>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>12.13.0</version>
<version>13.6.2-SNAPSHOT</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
@@ -155,8 +162,8 @@
</execution>
</executions>
<configuration>
<source>1.8</source>
<target>1.8</target>
<source>11</source>
<target>11</target>
<!-- Turn off annotation processing for building -->
<compilerArgument>-proc:none</compilerArgument>
</configuration>
@@ -63,13 +63,9 @@ class SimpleQueryBeanWriter {
);
private final Set<String> importTypes = new TreeSet<>();
private final List<PropertyMeta> properties = new ArrayList<>();
private final TypeElement element;
private final ProcessingContext processingContext;
private final boolean isEntity;
private final boolean embeddable;
private final String dbName;
@@ -80,10 +76,9 @@ class SimpleQueryBeanWriter {
private String destPackage;
private String origDestPackage;
private String shortName;
private String origShortName;
private final String shortInnerName;
private final String origShortName;
private Append writer;
SimpleQueryBeanWriter(TypeElement element, ProcessingContext processingContext) {
@@ -92,8 +87,12 @@ class SimpleQueryBeanWriter {
this.element = element;
this.processingContext = processingContext;
this.beanFullName = element.getQualifiedName().toString();
this.destPackage = derivePackage(beanFullName) + ".query";
this.shortName = deriveShortName(beanFullName);
boolean nested = element.getNestingKind().isNested();
this.destPackage = Util.packageOf(nested, beanFullName) + ".query";
String sn = Util.shortName(nested, beanFullName);
this.shortInnerName = Util.shortName(false, sn);
this.shortName = sn.replace(".", "");
this.origShortName = shortName;
this.isEntity = processingContext.isEntity(element);
this.embeddable = processingContext.isEmbeddable(element);
this.dbName = findDbName();
@@ -129,10 +128,7 @@ class SimpleQueryBeanWriter {
* </p>
*/
private void addClassProperties() {
List<VariableElement> fields = processingContext.allFields(element);
for (VariableElement field : fields) {
for (VariableElement field : processingContext.allFields(element)) {
PropertyType type = processingContext.getPropertyType(field);
if (type != null) {
type.addImports(importTypes);
@@ -145,9 +141,7 @@ class SimpleQueryBeanWriter {
* Write the type query bean (root bean).
*/
void writeRootBean() throws IOException {
gatherPropertyDetails();
if (isEmbeddable()) {
processingContext.addEntity(beanFullName, dbName);
} else if (isEntity()) {
@@ -193,15 +187,12 @@ class SimpleQueryBeanWriter {
* Write the type query assoc bean.
*/
void writeAssocBean() throws IOException {
writingAssocBean = true;
origDestPackage = destPackage;
destPackage = destPackage + ".assoc";
origShortName = shortName;
shortName = "Assoc" + shortName;
prepareAssocBeanImports();
writer = new Append(createFileWriter());
writePackage();
@@ -279,7 +270,6 @@ class SimpleQueryBeanWriter {
* Write all the fields.
*/
private void writeFields() {
for (PropertyMeta property : properties) {
String typeDefn = property.getTypeDefn(shortName, writingAssocBean);
lang().fieldDefn(writer, property.getName(), typeDefn);
@@ -292,7 +282,6 @@ class SimpleQueryBeanWriter {
* Write the class definition.
*/
private void writeClass() {
if (writingAssocBean) {
writer.append("/**").eol();
writer.append(" * Association query bean for %s.", shortName).eol();
@@ -301,7 +290,7 @@ class SimpleQueryBeanWriter {
writer.append(" */").eol();
writer.append(Constants.AT_GENERATED).eol();
writer.append(Constants.AT_TYPEQUERYBEAN).eol();
lang().beginAssocClass(writer, shortName, origShortName);
lang().beginAssocClass(writer, shortName, shortInnerName);
} else {
writer.append("/**").eol();
@@ -331,7 +320,6 @@ class SimpleQueryBeanWriter {
* Write all the imports.
*/
private void writeImports() {
for (String importType : importTypes) {
writer.append("import %s;", importType).eol();
}
@@ -343,9 +331,7 @@ class SimpleQueryBeanWriter {
}
private Writer createFileWriter() throws IOException {
String relPath = destPackage.replace('.', '/');
File absDir = new File(generatedSourcesDir, relPath);
if (!absDir.exists() && !absDir.mkdirs()) {
processingContext.logNote("failed to create directories for:" + absDir.getAbsolutePath());
@@ -356,19 +342,4 @@ class SimpleQueryBeanWriter {
return new FileWriter(absFile);
}
private String derivePackage(String name) {
int pos = name.lastIndexOf('.');
if (pos == -1) {
return "";
}
return name.substring(0, pos);
}
private String deriveShortName(String name) {
int pos = name.lastIndexOf('.');
if (pos == -1) {
return name;
}
return name.substring(pos + 1);
}
}
@@ -0,0 +1,46 @@
package io.ebean.querybean.generator;
class Util {
static String packageOf(boolean nested, String originName) {
return nested ? nestedPackageOf(originName) : packageOf(originName);
}
private static String nestedPackageOf(String cls) {
int pos = cls.lastIndexOf('.');
if (pos < 0) {
return "";
}
pos = cls.lastIndexOf('.', pos - 1);
return (pos == -1) ? "" : cls.substring(0, pos);
}
private static String packageOf(String cls) {
int pos = cls.lastIndexOf('.');
return (pos == -1) ? "" : cls.substring(0, pos);
}
static String shortName(boolean nested, String fullType) {
return nested ? nestedShortName(fullType): shortName(fullType);
}
private static String nestedShortName(String fullType) {
int pos = fullType.lastIndexOf('.');
if (pos < 0) {
return fullType;
} else {
pos = fullType.lastIndexOf('.', pos - 1);
return pos < 0 ? fullType : fullType.substring(pos + 1);
}
}
private static String shortName(String fullType) {
int p = fullType.lastIndexOf('.');
if (p == -1) {
return fullType;
} else {
return fullType.substring(p + 1);
}
}
}
@@ -0,0 +1,20 @@
package io.ebean.querybean.generator
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
internal class UtilTest {
@Test
fun packageOf() {
Assertions.assertEquals(Util.packageOf(true, "com.example.Foo.Bar"), "com.example")
Assertions.assertEquals(Util.packageOf(false, "com.example.other.foo.Bar"), "com.example.other.foo")
}
@Test
fun shortName() {
Assertions.assertEquals(Util.shortName(true, "com.example.Foo.Bar"), "Foo.Bar")
Assertions.assertEquals(Util.shortName(false, "com.example.foo.Bar"), "Bar")
Assertions.assertEquals(Util.shortName(false, "Bar"), "Bar")
}
}
@@ -0,0 +1,20 @@
package org.example.domain
import javax.persistence.*
@Entity
@Table(name = "t_inner")
@IdClass(MyKotInner.Mid::class)
class MyKotInner {
@Id
var one: String = "0"
@Id
var two: String = "0"
var description: String? = null
@Embeddable
data class Mid(val one: String, val two: String)
}
@@ -0,0 +1,23 @@
package org.example.otherpackage
import io.ebean.DB
import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
import org.example.domain.MyKotInner
import org.junit.jupiter.api.Test
class InnerTest {
@Test
fun split_normal() {
val my = MyKotInner()
my.one = "a"
my.two = "b"
my.description = "desc"
DB.save(my)
val myId = MyKotInner.Mid("a", "b")
val found = DB.find(MyKotInner::class.java, myId)
assertThat(found).isNotNull
}
}
@@ -0,0 +1 @@
ebean-version: 141
@@ -0,0 +1,9 @@
ebean.ddl.generate=true
ebean.ddl.run=true
#datasource.default=h2
datasource.db.username=sa
datasource.db.password=
datasource.db.url=jdbc:h2:mem:testKotlin
@@ -18,13 +18,9 @@ import java.util.TreeSet;
class SimpleQueryBeanWriter {
private final Set<String> importTypes = new TreeSet<>();
private final List<PropertyMeta> properties = new ArrayList<>();
private final TypeElement element;
private final ProcessingContext processingContext;
private final String dbName;
private final String beanFullName;
private final boolean isEntity;
@@ -33,17 +29,21 @@ class SimpleQueryBeanWriter {
private String destPackage;
private String origDestPackage;
private String shortName;
private String origShortName;
private final String shortInnerName;
private final String origShortName;
private Append writer;
SimpleQueryBeanWriter(TypeElement element, ProcessingContext processingContext) {
this.element = element;
this.processingContext = processingContext;
this.beanFullName = element.getQualifiedName().toString();
this.destPackage = derivePackage(beanFullName) + ".query";
this.shortName = deriveShortName(beanFullName);
boolean nested = element.getNestingKind().isNested();
this.destPackage = Util.packageOf(nested, beanFullName) + ".query";
String sn = Util.shortName(nested, beanFullName);
this.shortInnerName = Util.shortName(false, sn);
this.shortName = sn.replace('.', '$');
this.origShortName = shortName;
this.isEntity = processingContext.isEntity(element);
this.embeddable = processingContext.isEmbeddable(element);
this.dbName = findDbName();
@@ -125,7 +125,6 @@ class SimpleQueryBeanWriter {
writingAssocBean = true;
origDestPackage = destPackage;
destPackage = destPackage + ".assoc";
origShortName = shortName;
shortName = "Assoc" + shortName;
prepareAssocBeanImports();
@@ -318,7 +317,7 @@ class SimpleQueryBeanWriter {
writer.append(" */").eol();
writer.append(Constants.AT_GENERATED).eol();
writer.append(Constants.AT_TYPEQUERYBEAN).eol();
writer.append("public class Q%s<R> extends TQAssocBean<%s,R> {", shortName, origShortName).eol();
writer.append("public class Q%s<R> extends TQAssocBean<%s,R> {", shortName, shortInnerName).eol();
} else {
writer.append("/**").eol();
@@ -388,19 +387,4 @@ class SimpleQueryBeanWriter {
return jfo.openWriter();
}
private String derivePackage(String name) {
int pos = name.lastIndexOf('.');
if (pos == -1) {
return "";
}
return name.substring(0, pos);
}
private String deriveShortName(String name) {
int pos = name.lastIndexOf('.');
if (pos == -1) {
return name;
}
return name.substring(pos + 1);
}
}
@@ -0,0 +1,46 @@
package io.ebean.querybean.generator;
class Util {
static String packageOf(boolean nested, String originName) {
return nested ? nestedPackageOf(originName) : packageOf(originName);
}
private static String nestedPackageOf(String cls) {
int pos = cls.lastIndexOf('.');
if (pos < 0) {
return "";
}
pos = cls.lastIndexOf('.', pos - 1);
return (pos == -1) ? "" : cls.substring(0, pos);
}
private static String packageOf(String cls) {
int pos = cls.lastIndexOf('.');
return (pos == -1) ? "" : cls.substring(0, pos);
}
static String shortName(boolean nested, String fullType) {
return nested ? nestedShortName(fullType): shortName(fullType);
}
private static String nestedShortName(String fullType) {
int pos = fullType.lastIndexOf('.');
if (pos < 0) {
return fullType;
} else {
pos = fullType.lastIndexOf('.', pos - 1);
return pos < 0 ? fullType : fullType.substring(pos + 1);
}
}
private static String shortName(String fullType) {
int p = fullType.lastIndexOf('.');
if (p == -1) {
return fullType;
} else {
return fullType.substring(p + 1);
}
}
}
@@ -0,0 +1,22 @@
package io.ebean.querybean.generator;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class UtilTest {
@Test
void packageOf() {
assertEquals(Util.packageOf(true, "com.example.Foo.Bar"), "com.example");
assertEquals(Util.packageOf(false, "com.example.other.foo.Bar"), "com.example.other.foo");
}
@Test
void shortName() {
assertEquals(Util.shortName(true, "com.example.Foo.Bar"), "Foo.Bar");
assertEquals(Util.shortName(false, "com.example.foo.Bar"), "Bar");
assertEquals(Util.shortName(false, "Bar"), "Bar");
}
}