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 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 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 elements, boolean onlyAnnotatedConstructors) { + EntityType entityType, + List 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); } } }