diff --git a/pom.xml b/pom.xml
index 91220825ff..828604589a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -452,6 +452,9 @@
0.23.1
+
+ com.querydsl.core.annotations.QueryProjection
+
true
true
true
@@ -465,14 +468,6 @@
public
-
-
-
- cmp
-
- verify
-
-
org.apache.maven.plugins
diff --git a/querydsl-libraries/pom.xml b/querydsl-libraries/pom.xml
index 80c9f46d73..3968e36ad3 100644
--- a/querydsl-libraries/pom.xml
+++ b/querydsl-libraries/pom.xml
@@ -138,6 +138,18 @@
+
+ com.github.siom79.japicmp
+ japicmp-maven-plugin
+
+
+
+ cmp
+
+ verify
+
+
+
diff --git a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/annotations/QueryProjection.java b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/annotations/QueryProjection.java
index 311697daa2..b370a4ec08 100644
--- a/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/annotations/QueryProjection.java
+++ b/querydsl-libraries/querydsl-core/src/main/java/com/querydsl/core/annotations/QueryProjection.java
@@ -30,7 +30,7 @@
*
* private String firstName, lastName;
*
- * {@code @QueryProjection}
+ * {@code @QueryProjection(useBuilder = true, builderName = "new")}
* public UserInfo(String firstName, String lastName) {
* this.firstName = firstName;
* this.lastName = lastName;
@@ -49,8 +49,27 @@
* .select(new QUserInfo(user.firstName, user.lastName))
* .fetch();
* }
+ *
+ * or(with Builder)
+ *
+ *
{@code
+ * QUser user = QUser.user;
+ * List result = querydsl.from(user)
+ * .where(user.valid.eq(true))
+ * .select(QUserInfo.builderNew()
+ * .firstName(user.firstName)
+ * .lastName(user.lastName)
+ * .build()
+ * )
+ * .fetch();
+ * }
*/
@Documented
@Target({ElementType.CONSTRUCTOR, ElementType.TYPE})
@Retention(RUNTIME)
-public @interface QueryProjection {}
+public @interface QueryProjection {
+
+ boolean useBuilder() default false;
+
+ String builderName() default "";
+}
diff --git a/querydsl-tooling/querydsl-apt/src/main/java/com/querydsl/apt/AbstractQuerydslProcessor.java b/querydsl-tooling/querydsl-apt/src/main/java/com/querydsl/apt/AbstractQuerydslProcessor.java
index e03d74a4ce..6d0682e137 100644
--- a/querydsl-tooling/querydsl-apt/src/main/java/com/querydsl/apt/AbstractQuerydslProcessor.java
+++ b/querydsl-tooling/querydsl-apt/src/main/java/com/querydsl/apt/AbstractQuerydslProcessor.java
@@ -57,6 +57,7 @@
import java.util.Set;
import java.util.stream.Stream;
import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
@@ -164,7 +165,7 @@ protected void processAnnotations() {
var altEntityAnn = conf.getAlternativeEntityAnnotation() != null;
var superAnn = conf.getSuperTypeAnnotation() != null;
for (TypeElement element : elements) {
- var entityType = elementHandler.handleEntityType(element);
+ var entityType = elementHandler.handleEntityType(element, processingEnv.getMessager());
registerTypeElement(entityType.getFullName(), element);
if (typeFactory.isSimpleTypeEntity(element, conf.getEntityAnnotation())) {
context.entityTypes.put(entityType.getFullName(), entityType);
@@ -189,7 +190,7 @@ protected void processAnnotations() {
if (!context.allTypes.containsKey(fullName)) {
var element = processingEnv.getElementUtils().getTypeElement(fullName);
if (element != null) {
- elementHandler.handleEntityType(element);
+ elementHandler.handleEntityType(element, processingEnv.getMessager());
}
}
}
@@ -206,7 +207,7 @@ protected void processAnnotations() {
addSupertypeFields(entityType, handled);
}
- processProjectionTypes(elements);
+ processProjectionTypes(elements, processingEnv.getMessager());
// extend entity types
typeFactory.extendTypes();
@@ -231,7 +232,8 @@ private void addExternalParents(EntityType entityType) {
&& !TypeUtils.hasAnnotationOfType(typeElement, conf.getEntityAnnotations())) {
continue;
}
- var superEntityType = elementHandler.handleEntityType(typeElement);
+ var superEntityType =
+ elementHandler.handleEntityType(typeElement, processingEnv.getMessager());
if (superEntityType.getSuperType() != null) {
superTypes.push(superEntityType.getSuperType().getType());
}
@@ -306,7 +308,7 @@ protected Set collectElements() {
// register found elements
for (TypeElement element : embeddedElements) {
if (!elements.contains(element)) {
- elementHandler.handleEntityType(element);
+ elementHandler.handleEntityType(element, processingEnv.getMessager());
}
}
}
@@ -343,20 +345,20 @@ private void registerTypeElement(String entityName, TypeElement element) {
elements.add(element);
}
- private void processProjectionTypes(Set elements) {
+ private void processProjectionTypes(Set elements, Messager messager) {
Set visited = new HashSet<>();
for (Element element : getElements(QueryProjection.class)) {
if (element.getKind() == ElementKind.CONSTRUCTOR) {
var parent = element.getEnclosingElement();
if (!elements.contains(parent) && !visited.contains(parent)) {
- var model = elementHandler.handleProjectionType((TypeElement) parent, true);
+ var model = elementHandler.handleProjectionType((TypeElement) parent, true, messager);
registerTypeElement(model.getFullName(), (TypeElement) parent);
context.projectionTypes.put(model.getFullName(), model);
visited.add(parent);
}
}
if (element.getKind().isClass() && !visited.contains(element)) {
- var model = elementHandler.handleProjectionType((TypeElement) element, false);
+ var model = elementHandler.handleProjectionType((TypeElement) element, false, messager);
registerTypeElement(model.getFullName(), (TypeElement) element);
context.projectionTypes.put(model.getFullName(), model);
visited.add(element);
diff --git a/querydsl-tooling/querydsl-apt/src/main/java/com/querydsl/apt/TypeElementHandler.java b/querydsl-tooling/querydsl-apt/src/main/java/com/querydsl/apt/TypeElementHandler.java
index 392bee384b..bddad76d85 100644
--- a/querydsl-tooling/querydsl-apt/src/main/java/com/querydsl/apt/TypeElementHandler.java
+++ b/querydsl-tooling/querydsl-apt/src/main/java/com/querydsl/apt/TypeElementHandler.java
@@ -23,6 +23,7 @@
import com.querydsl.codegen.utils.model.TypeCategory;
import com.querydsl.core.annotations.PropertyType;
import com.querydsl.core.annotations.QueryInit;
+import com.querydsl.core.annotations.QueryProjection;
import com.querydsl.core.annotations.QueryType;
import com.querydsl.core.util.Annotations;
import com.querydsl.core.util.BeanUtils;
@@ -34,12 +35,14 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
+import javax.tools.Diagnostic;
/**
* {@code TypeElementHandler} is an APT visitor for entity types
@@ -67,7 +70,7 @@ public TypeElementHandler(
this.queryTypeFactory = queryTypeFactory;
}
- public EntityType handleEntityType(TypeElement element) {
+ public EntityType handleEntityType(TypeElement element, Messager messager) {
EntityType entityType = typeFactory.getEntityType(element.asType(), true);
List extends Element> elements = element.getEnclosedElements();
var config = configuration.getConfig(element, elements);
@@ -78,7 +81,7 @@ public EntityType handleEntityType(TypeElement element) {
// constructors
if (config.visitConstructors()) {
- handleConstructors(entityType, elements, true);
+ handleConstructors(entityType, elements, true, messager);
}
// fields
@@ -175,13 +178,14 @@ private Property toProperty(
return new Property(entityType, name, propertyType, inits);
}
- public EntityType handleProjectionType(TypeElement e, boolean onlyAnnotatedConstructors) {
+ public EntityType handleProjectionType(
+ TypeElement e, boolean onlyAnnotatedConstructors, Messager messager) {
Type c = typeFactory.getType(e.asType(), true);
var entityType =
new EntityType(c.as(TypeCategory.ENTITY), configuration.getVariableNameFunction());
typeMappings.register(entityType, queryTypeFactory.create(entityType));
List extends Element> elements = e.getEnclosedElements();
- handleConstructors(entityType, elements, onlyAnnotatedConstructors);
+ handleConstructors(entityType, elements, onlyAnnotatedConstructors, messager);
return entityType;
}
@@ -198,11 +202,40 @@ private Type getType(VariableElement element) {
}
private void handleConstructors(
- EntityType entityType, List extends Element> elements, boolean onlyAnnotatedConstructors) {
+ EntityType entityType,
+ List extends Element> elements,
+ boolean onlyAnnotatedConstructors,
+ Messager messager) {
+ var builderNameSet = new HashSet();
for (ExecutableElement constructor : ElementFilter.constructorsIn(elements)) {
if (configuration.isValidConstructor(constructor, onlyAnnotatedConstructors)) {
var parameters = transformParams(constructor.getParameters());
- entityType.addConstructor(new Constructor(parameters));
+ QueryProjection projection = constructor.getAnnotation(QueryProjection.class);
+ if (projection != null
+ && projection.useBuilder()
+ && projection.builderName().trim().isEmpty()) {
+ messager.printMessage(
+ Diagnostic.Kind.ERROR,
+ "@QueryProjection with builder=true requires a non-empty builderName",
+ constructor);
+ return;
+ }
+
+ Constructor constructorModel = new Constructor(parameters);
+ constructorModel.setUseBuilder(projection != null && projection.useBuilder());
+ constructorModel.setBuilderName(projection != null ? projection.builderName() : "");
+
+ if (constructorModel.useBuilder()
+ && builderNameSet.contains(constructorModel.getBuilderName())) {
+ messager.printMessage(
+ Diagnostic.Kind.ERROR,
+ "Duplicate builderName found: " + constructorModel.getBuilderName(),
+ constructor);
+ return;
+ }
+
+ builderNameSet.add(constructorModel.getBuilderName());
+ entityType.addConstructor(constructorModel);
}
}
}
diff --git a/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/GenericExporterTest.java b/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/GenericExporterTest.java
index a0463f1e4e..b5ef81e61c 100644
--- a/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/GenericExporterTest.java
+++ b/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/GenericExporterTest.java
@@ -50,6 +50,7 @@ public void execute() throws IOException {
expected.add("QQueryProjectionTest_EntityWithProjection.java");
expected.add("QEmbeddable3Test_EmbeddableClass.java");
expected.add("QQueryEmbedded4Test_User.java");
+ expected.add("QQueryProjectionBuilderTestEntity.java");
execute(expected, "GenericExporterTest", "QuerydslAnnotationProcessor");
}
@@ -89,6 +90,7 @@ public void execute2() throws IOException {
expected.add("QOneToOneTest_Person.java");
expected.add("QGeneric16Test_HidaBez.java");
expected.add("QGeneric16Test_HidaBezGruppe.java");
+ expected.add("QQueryProjectionBuilderTestEntity.java");
execute(expected, "GenericExporterTest2", "HibernateAnnotationProcessor");
}
diff --git a/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/domain/QueryProjectionBuilderTest.java b/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/domain/QueryProjectionBuilderTest.java
new file mode 100644
index 0000000000..cb4a19122f
--- /dev/null
+++ b/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/domain/QueryProjectionBuilderTest.java
@@ -0,0 +1,34 @@
+package com.querydsl.apt.domain;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.querydsl.core.types.dsl.Expressions;
+import org.junit.Test;
+
+public class QueryProjectionBuilderTest {
+
+ @Test
+ public void builder_buildsQClassCorrectly() {
+ var property = Expressions.stringPath("x");
+ QQueryProjectionBuilderTestEntity dto =
+ QQueryProjectionBuilderTestEntity.builderTest1().setProperty(property).build();
+
+ assertNotNull(dto);
+ assertEquals(QueryProjectionBuilderTestEntity.class, dto.getType());
+ }
+
+ @Test
+ public void builder_buildsQClassCorrectly2() {
+ var property = Expressions.stringPath("x");
+ var intProperty = Expressions.numberPath(Integer.class, "y");
+ QQueryProjectionBuilderTestEntity dto =
+ QQueryProjectionBuilderTestEntity.builderTest2()
+ .setProperty(property)
+ .setIntProperty(intProperty)
+ .build();
+
+ assertNotNull(dto);
+ assertEquals(QueryProjectionBuilderTestEntity.class, dto.getType());
+ }
+}
diff --git a/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/domain/QueryProjectionBuilderTestEntity.java b/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/domain/QueryProjectionBuilderTestEntity.java
new file mode 100644
index 0000000000..21a949b075
--- /dev/null
+++ b/querydsl-tooling/querydsl-apt/src/test/java/com/querydsl/apt/domain/QueryProjectionBuilderTestEntity.java
@@ -0,0 +1,32 @@
+package com.querydsl.apt.domain;
+
+import com.querydsl.core.annotations.QueryProjection;
+
+public class QueryProjectionBuilderTestEntity {
+ private String property;
+ private int intProperty;
+ private Test test;
+
+ @QueryProjection(useBuilder = true, builderName = "Test1")
+ public QueryProjectionBuilderTestEntity(String property) {
+ this.property = property;
+ }
+
+ @QueryProjection(useBuilder = true, builderName = "Test2")
+ public QueryProjectionBuilderTestEntity(String property, int intProperty) {
+ this.property = property;
+ this.intProperty = intProperty;
+ }
+
+ @QueryProjection(useBuilder = true, builderName = "Test3")
+ public QueryProjectionBuilderTestEntity(String property, int intProperty, Test test) {
+ this.property = property;
+ this.intProperty = intProperty;
+ this.test = test;
+ }
+
+ public static class Test {
+ private String property;
+ private int intProperty;
+ }
+}
diff --git a/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/CodeWriter.java b/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/CodeWriter.java
index da9ed03fe0..701847fac1 100644
--- a/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/CodeWriter.java
+++ b/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/CodeWriter.java
@@ -41,6 +41,11 @@ public interface CodeWriter extends Appendable {
CodeWriter beginClass(Type type, Type superClass, Type... interfaces) throws IOException;
+ CodeWriter beginInnerStaticClass(Type type) throws IOException;
+
+ CodeWriter beginInnerStaticClass(Type type, Type superClass, Type... interfaces)
+ throws IOException;
+
CodeWriter beginConstructor(Collection params, Function transformer)
throws IOException;
diff --git a/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/JavaWriter.java b/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/JavaWriter.java
index 944b0068b8..7661da1e67 100644
--- a/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/JavaWriter.java
+++ b/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/JavaWriter.java
@@ -57,6 +57,8 @@ public final class JavaWriter extends AbstractCodeWriter {
private static final String PUBLIC_CLASS = "public class ";
+ private static final String PUBLIC_STATIC_CLASS = "public static class ";
+
private static final String PUBLIC_FINAL = "public final ";
private static final String PUBLIC_INTERFACE = "public interface ";
@@ -202,6 +204,33 @@ public JavaWriter beginClass(Type type, Type superClass, Type... interfaces) thr
return this;
}
+ @Override
+ public CodeWriter beginInnerStaticClass(Type type) throws IOException {
+ return beginInnerStaticClass(type, null);
+ }
+
+ @Override
+ public CodeWriter beginInnerStaticClass(Type type, Type superClass, Type... interfaces)
+ throws IOException {
+ beginLine(PUBLIC_STATIC_CLASS, type.getGenericName(false, packages, classes));
+ if (superClass != null) {
+ append(EXTENDS).append(superClass.getGenericName(false, packages, classes));
+ }
+ if (interfaces.length > 0) {
+ append(IMPLEMENTS);
+ for (int i = 0; i < interfaces.length; i++) {
+ if (i > 0) {
+ append(Symbols.COMMA);
+ }
+ append(interfaces[i].getGenericName(false, packages, classes));
+ }
+ }
+ append(" {").nl().nl();
+ goIn();
+ types.push(type);
+ return this;
+ }
+
@Override
public JavaWriter beginConstructor(
Collection parameters, Function transformer) throws IOException {
diff --git a/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/ScalaWriter.java b/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/ScalaWriter.java
index dcf7c06b03..a9d00fd526 100644
--- a/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/ScalaWriter.java
+++ b/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/ScalaWriter.java
@@ -253,6 +253,16 @@ public ScalaWriter beginClass(Type type, Type superClass, Type... interfaces) th
return this;
}
+ @Override
+ public CodeWriter beginInnerStaticClass(Type type) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public CodeWriter beginInnerStaticClass(Type type, Type superClass, Type... interfaces) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
@Override
public ScalaWriter beginConstructor(
Collection parameters, Function transformer) throws IOException {
diff --git a/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/model/Constructor.java b/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/model/Constructor.java
index a30f086f17..52687c5a8c 100644
--- a/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/model/Constructor.java
+++ b/querydsl-tooling/querydsl-codegen-utils/src/main/java/com/querydsl/codegen/utils/model/Constructor.java
@@ -21,6 +21,8 @@
public final class Constructor implements Comparable {
private final Collection parameters;
+ private boolean useBuilder = false;
+ private String builderName = "";
public Constructor(Collection params) {
parameters = params;
@@ -41,6 +43,22 @@ public Collection getParameters() {
return parameters;
}
+ public boolean useBuilder() {
+ return useBuilder;
+ }
+
+ public void setUseBuilder(boolean useBuilder) {
+ this.useBuilder = useBuilder;
+ }
+
+ public String getBuilderName() {
+ return builderName;
+ }
+
+ public void setBuilderName(String builderName) {
+ this.builderName = builderName;
+ }
+
@Override
public int hashCode() {
return parameters.hashCode();
diff --git a/querydsl-tooling/querydsl-codegen/src/main/java/com/querydsl/codegen/DefaultProjectionSerializer.java b/querydsl-tooling/querydsl-codegen/src/main/java/com/querydsl/codegen/DefaultProjectionSerializer.java
index a8334d497f..85b9845993 100644
--- a/querydsl-tooling/querydsl-codegen/src/main/java/com/querydsl/codegen/DefaultProjectionSerializer.java
+++ b/querydsl-tooling/querydsl-codegen/src/main/java/com/querydsl/codegen/DefaultProjectionSerializer.java
@@ -13,16 +13,11 @@
*/
package com.querydsl.codegen;
-import static com.querydsl.codegen.utils.Symbols.THIS_ESCAPE;
+import static com.querydsl.codegen.utils.StringUtils.capitalize;
+import static com.querydsl.codegen.utils.Symbols.*;
import com.querydsl.codegen.utils.CodeWriter;
-import com.querydsl.codegen.utils.model.ClassType;
-import com.querydsl.codegen.utils.model.Constructor;
-import com.querydsl.codegen.utils.model.Parameter;
-import com.querydsl.codegen.utils.model.Type;
-import com.querydsl.codegen.utils.model.TypeCategory;
-import com.querydsl.codegen.utils.model.TypeExtends;
-import com.querydsl.codegen.utils.model.Types;
+import com.querydsl.codegen.utils.model.*;
import com.querydsl.core.types.ConstructorExpression;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.dsl.NumberExpression;
@@ -73,6 +68,16 @@ public DefaultProjectionSerializer(
this.generatedAnnotationClass = generatedAnnotationClass;
}
+ private Parameter getExpressionParameter(EntityType model, boolean asExpr, Parameter p) {
+ var type =
+ !asExpr
+ ? typeMappings.getExprType(p.getType(), model, false, false, true)
+ : new ClassType(
+ Expression.class,
+ p.getType().isFinal() ? p.getType() : new TypeExtends(p.getType()));
+ return new Parameter(p.getName(), type);
+ }
+
protected void intro(EntityType model, CodeWriter writer) throws IOException {
var simpleName = model.getSimpleName();
var queryType = typeMappings.getPathType(model, model, false);
@@ -127,37 +132,32 @@ public void serialize(
var localName = writer.getRawName(model);
Set sizes = new HashSet<>();
+ var queryType = typeMappings.getPathType(model, model, false);
+
var constructors = new ArrayList<>(model.getConstructors());
Collections.sort(constructors);
for (Constructor c : constructors) {
- final var asExpr = sizes.add(c.getParameters().size());
+ var parameters = new ArrayList<>(c.getParameters());
+ final var asExpr = sizes.add(parameters.size());
// begin
writer.beginConstructor(
- c.getParameters(),
- (Parameter p) -> {
- Type type;
- if (!asExpr) {
- type = typeMappings.getExprType(p.getType(), model, false, false, true);
- } else if (p.getType().isFinal()) {
- type = new ClassType(Expression.class, p.getType());
- } else {
- type = new ClassType(Expression.class, new TypeExtends(p.getType()));
- }
- return new Parameter(p.getName(), type);
- });
+ parameters, parameter -> getExpressionParameter(model, asExpr, parameter));
// body
- writer.beginLine("super(" + writer.getClassConstant(localName));
+ writer.beginLine(SUPER, "(" + writer.getClassConstant(localName));
// TODO: Fix for Scala (Array[Class])
writer.append(", new Class>[]{");
- var first = true;
-
- for (Parameter p : c.getParameters()) {
- if (!first) {
+ for (int i = 0; i < parameters.size(); i++) {
+ if (i != 0) {
writer.append(", ");
}
- parameterType(writer, p);
- first = false;
+ Parameter p = parameters.get(i);
+ if (Types.PRIMITIVES.containsKey(p.getType())) {
+ var primitive = Types.PRIMITIVES.get(p.getType());
+ writer.append(writer.getClassConstant(primitive.getFullName()));
+ } else {
+ writer.append(writer.getClassConstant(writer.getRawName(p.getType())));
+ }
}
writer.append("}");
@@ -171,6 +171,12 @@ public void serialize(
writer.end();
}
+ sizes = new HashSet();
+ for (Constructor c : model.getConstructors()) {
+ appendInnerStaticBuilderClass(model, writer, queryType, c, sizes);
+ appendStaticBuilderFactoryMethod(writer, c);
+ }
+
// outro
outro(model, writer);
}
@@ -187,4 +193,83 @@ protected void parameterType(CodeWriter writer, Parameter p) throws IOException
protected void parameter(CodeWriter writer, Parameter p) throws IOException {
writer.append(p.getName());
}
+
+ private void appendInnerStaticBuilderClass(
+ EntityType model,
+ CodeWriter writer,
+ Type queryType,
+ Constructor constructor,
+ Set sizes)
+ throws IOException {
+ if (isBuilderDisabled(constructor)) return;
+
+ var builderName = constructor.getBuilderName();
+ var parameters = new ArrayList<>(constructor.getParameters());
+ final var asExpr = sizes.add(parameters.size());
+
+ var builderClassName = capitalize(builderName) + "Builder";
+
+ var builderType = new SimpleType(builderClassName);
+
+ writer.beginInnerStaticClass(builderType);
+ for (var param : parameters) {
+ var expressionParameter = getExpressionParameter(model, asExpr, param);
+ writer.privateField(expressionParameter.getType(), expressionParameter.getName());
+ }
+
+ for (var param : parameters) {
+ var expressionParameter = getExpressionParameter(model, asExpr, param);
+ appendPublicSetterForBuilder(writer, expressionParameter, builderType);
+ }
+
+ appendPublicBuildMethodForBuilder(writer, queryType, parameters);
+
+ writer.end();
+ }
+
+ private void appendPublicSetterForBuilder(
+ CodeWriter writer, Parameter expressionParameter, Type builderType) throws IOException {
+ writer.beginPublicMethod(
+ builderType, "set" + capitalize(expressionParameter.getName()), expressionParameter);
+ writer.line(
+ THIS, DOT, expressionParameter.getName(), ASSIGN, expressionParameter.getName(), SEMICOLON);
+ writer.line(RETURN, THIS, SEMICOLON);
+ writer.end();
+ }
+
+ private void appendPublicBuildMethodForBuilder(
+ CodeWriter writer, Type queryType, ArrayList parameters) throws IOException {
+ writer.beginPublicMethod(queryType, "build");
+ writer.beginLine(RETURN, NEW, queryType.getSimpleName(), "(");
+ for (int i = 0; i < parameters.size(); i++) {
+ if (i != 0) {
+ writer.append(", ");
+ }
+ writer.append(parameters.get(i).getName());
+ if (i == parameters.size() - 1) {
+ writer.append(")").append(SEMICOLON).append(NEWLINE);
+ }
+ }
+ writer.end();
+ }
+
+ private void appendStaticBuilderFactoryMethod(CodeWriter writer, Constructor constructor)
+ throws IOException {
+ if (isBuilderDisabled(constructor)) return;
+
+ var builderName = constructor.getBuilderName();
+ var builderClassName = capitalize(builderName) + "Builder";
+ var factoryMethodName = "builder" + capitalize(builderName);
+
+ var builderType = new SimpleType(builderClassName);
+
+ writer.beginStaticMethod(builderType, factoryMethodName);
+ writer.line(RETURN, NEW, builderType.getSimpleName(), "()", SEMICOLON);
+ writer.end();
+ }
+
+ private boolean isBuilderDisabled(Constructor constructor) {
+ if (!constructor.useBuilder()) return true;
+ return constructor.getBuilderName() == null || constructor.getBuilderName().trim().isEmpty();
+ }
}
diff --git a/querydsl-tooling/querydsl-codegen/src/main/java/com/querydsl/codegen/GenericExporter.java b/querydsl-tooling/querydsl-codegen/src/main/java/com/querydsl/codegen/GenericExporter.java
index 223ff9d9c5..0adccadc87 100644
--- a/querydsl-tooling/querydsl-codegen/src/main/java/com/querydsl/codegen/GenericExporter.java
+++ b/querydsl-tooling/querydsl-codegen/src/main/java/com/querydsl/codegen/GenericExporter.java
@@ -403,7 +403,11 @@ private void addConstructors(Class> cl, EntityType type) {
}
parameters.add(new Parameter("param" + i, parameterType));
}
- type.addConstructor(new com.querydsl.codegen.utils.model.Constructor(parameters));
+ var queryProjection = constructor.getAnnotation(QueryProjection.class);
+ var newConstructor = new com.querydsl.codegen.utils.model.Constructor(parameters);
+ newConstructor.setUseBuilder(queryProjection.useBuilder());
+ newConstructor.setBuilderName(queryProjection.builderName());
+ type.addConstructor(newConstructor);
}
}
}