Skip to content

feat(@QueryProjection): support builder-based Q class generation#1078

Merged
velo merged 20 commits intoOpenFeign:masterfrom
Cole-SJ:feature/add-Query-Projection-Builder
Apr 18, 2025
Merged

feat(@QueryProjection): support builder-based Q class generation#1078
velo merged 20 commits intoOpenFeign:masterfrom
Cole-SJ:feature/add-Query-Projection-Builder

Conversation

@Cole-SJ
Copy link
Copy Markdown
Contributor

@Cole-SJ Cole-SJ commented Apr 10, 2025

Multi-builder Support

  • You can now define multiple @QueryProjection(useBuilder = true, builderName = "...") constructors per DTO.
  • Each builder will be uniquely named inside the Q class and exposed via static factory methods.
  • Advantages of { concise syntax, no explicit new, IDE friendly}

Highlights

  1. Fully backward compatible — default remains constructor-style
  2. No need to manually define a builder in the DTO
  3. Improves readability and safety in complex DTO projections
  4. All builder method arguments are Expression<?>, just like in constructor-style projections

Required builderName when useBuilder = true

To support multiple builder configurations, @QueryProjection(useBuilder = true) now requires a non-empty builderName.
This helps prevent name collisions when generating builder classes and factory methods.

Example

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;
  }

and generated QClass is below

/**
 * com.querydsl.apt.domain.QQueryProjectionBuilderTestEntity is a Querydsl Projection type for QueryProjectionBuilderTestEntity
 */
@SuppressWarnings("this-escape")
@Generated("com.querydsl.codegen.DefaultProjectionSerializer")
public class QQueryProjectionBuilderTestEntity extends ConstructorExpression<QueryProjectionBuilderTestEntity> {

    private static final long serialVersionUID = 264670126L;

    public QQueryProjectionBuilderTestEntity(com.querydsl.core.types.Expression<String> property) {
        super(QueryProjectionBuilderTestEntity.class, new Class<?>[]{String.class}, property);
    }

    public QQueryProjectionBuilderTestEntity(com.querydsl.core.types.Expression<String> property, com.querydsl.core.types.Expression<Integer> intProperty) {
        super(QueryProjectionBuilderTestEntity.class, new Class<?>[]{String.class, int.class}, property, intProperty);
    }

    public QQueryProjectionBuilderTestEntity(com.querydsl.core.types.Expression<String> property, com.querydsl.core.types.Expression<Integer> intProperty, com.querydsl.core.types.Expression<? extends QueryProjectionBuilderTestEntity.Test> test) {
        super(QueryProjectionBuilderTestEntity.class, new Class<?>[]{String.class, int.class, QueryProjectionBuilderTestEntity.Test.class}, property, intProperty, test);
    }

    public static class Test1Builder {

        private com.querydsl.core.types.Expression<String> property;

        public Test1Builder setProperty(com.querydsl.core.types.Expression<String> property) {
            this.property = property;
            return this;
        }

        public QQueryProjectionBuilderTestEntity build() {
            return new QQueryProjectionBuilderTestEntity(property);
        }

    }

    public static Test1Builder builderTest1() {
        return new Test1Builder();
    }

    public static class Test2Builder {

        private com.querydsl.core.types.Expression<String> property;

        private com.querydsl.core.types.Expression<Integer> intProperty;

        public Test2Builder setProperty(com.querydsl.core.types.Expression<String> property) {
            this.property = property;
            return this;
        }

        public Test2Builder setIntProperty(com.querydsl.core.types.Expression<Integer> intProperty) {
            this.intProperty = intProperty;
            return this;
        }

        public QQueryProjectionBuilderTestEntity build() {
            return new QQueryProjectionBuilderTestEntity(property, intProperty);
        }

    }

    public static Test2Builder builderTest2() {
        return new Test2Builder();
    }

    public static class Test3Builder {

        private com.querydsl.core.types.Expression<String> property;

        private com.querydsl.core.types.Expression<Integer> intProperty;

        private com.querydsl.core.types.Expression<? extends QueryProjectionBuilderTestEntity.Test> test;

        public Test3Builder setProperty(com.querydsl.core.types.Expression<String> property) {
            this.property = property;
            return this;
        }

        public Test3Builder setIntProperty(com.querydsl.core.types.Expression<Integer> intProperty) {
            this.intProperty = intProperty;
            return this;
        }

        public Test3Builder setTest(com.querydsl.core.types.Expression<? extends QueryProjectionBuilderTestEntity.Test> test) {
            this.test = test;
            return this;
        }

        public QQueryProjectionBuilderTestEntity build() {
            return new QQueryProjectionBuilderTestEntity(property, intProperty, test);
        }

    }

    public static Test3Builder builderTest3() {
        return new Test3Builder();
    }

}

Question

this commit

I don't want to update pom.xml of root, how can I resolve binary incompatible problem

@Cole-SJ Cole-SJ marked this pull request as ready for review April 11, 2025 18:50
@Cole-SJ Cole-SJ changed the title feature: Add query projection builder feat(@QueryProjection): support builder-based Q class generation Apr 11, 2025
@Cole-SJ
Copy link
Copy Markdown
Contributor Author

Cole-SJ commented Apr 11, 2025

@velo
Hello. I am truly honored to have created a PR to suggest adding a feature to this repository. I'd love to have my work reviewed by you, so please feel free to check out this PR at your convenience and give me any feedback you'd like to incorporate.
Thank you

@velo
Copy link
Copy Markdown
Member

velo commented Apr 12, 2025

Question

this commit

I don't want to update pom.xml of root, how can I resolve binary incompatible problem

Hrmm

Codegen classes should be change prone, as they are internal classes used to generate code, no public apis. So I will take care of that.

Now, you change the annotation in a way that is backwards compatible. Looks like a big in the plugin

@Cole-SJ Cole-SJ force-pushed the feature/add-Query-Projection-Builder branch from a22480b to 9d1bea4 Compare April 13, 2025 14:44
@Cole-SJ
Copy link
Copy Markdown
Contributor Author

Cole-SJ commented Apr 14, 2025

@velo
Thank you for your replying. Did you mean that you will fix those problems of the plugin in this PR?

Comment thread pom.xml Outdated
<parameter>
<excludes>
<exclude>com.querydsl.core.annotations.QueryProjection</exclude>
<exclude>com.querydsl.codegen.utils.AbstractCodeWriter</exclude>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can revert most of this now, probably all

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you :)
I removed most of them except for @QueryProjection( because it would be exposed at public level interface 😢)

@Cole-SJ Cole-SJ force-pushed the feature/add-Query-Projection-Builder branch from 5195c82 to 1438ef6 Compare April 17, 2025 05:12
@velo velo merged commit cd2e167 into OpenFeign:master Apr 18, 2025
4 checks passed
velo added a commit that referenced this pull request Apr 18, 2025
* Bump io.projectreactor:reactor-bom from 2024.0.4 to 2024.0.5

Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2024.0.4 to 2024.0.5.
- [Release notes](https://github.com/reactor/reactor/releases)
- [Commits](reactor/reactor@2024.0.4...2024.0.5)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-bom
  dependency-version: 2024.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump quarkus.version from 3.21.2 to 3.21.3

Bumps `quarkus.version` from 3.21.2 to 3.21.3.

Updates `io.quarkus.platform:quarkus-bom` from 3.21.2 to 3.21.3
- [Commits](quarkusio/quarkus-platform@3.21.2...3.21.3)

Updates `io.quarkus.platform:quarkus-maven-plugin` from 3.21.2 to 3.21.3
- [Commits](quarkusio/quarkus-platform@3.21.2...3.21.3)

---
updated-dependencies:
- dependency-name: io.quarkus.platform:quarkus-bom
  dependency-version: 3.21.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: io.quarkus.platform:quarkus-maven-plugin
  dependency-version: 3.21.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Enable easy-jacoco for project (#1091)

* Enable easy-jacoco for project

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Upload coverage reports

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Upload codecov on windows

* Using newer image with codecov support

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Enable missing db2 testing

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Setup codecov to upload project wide coverage report

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Give db2 more time to start

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Setup codecov to upload project wide coverage report

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Remove codecov orb

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Reports are only generated at verify phase, need change build

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Download latest version of codecov

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Add badges

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Include new package on coverage reports

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Use internal @generated annotations

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* Fix kotlin-codegen build

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

---------

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* feat(@QueryProjection): support builder-based Q class generation (#1078)

* feature: add builder properties in QueryProjection annotation

* feature: add builder properties in Constructor class

* feature: add public static inner class write methods

* feature: add QueryProjection Builder feature on annotation processor

* feature: add inner builder class and factory method to generated QClass

* test: add QueryProjectionBuilderTestEntity for test

* feature, test: add QueryProjectionBuilderTestEntity for GenericExporter

* question: how can i resolve binary incompatible change without this commit

* feature: add duplicate builder name check in TypeElementHandler

* docs: update javadoc for QueryProjection

* feature: enhance builder name validation in TypeElementHandler

* test: add unit tests for QueryProjectionBuilder

* Skip API validation on tooling projects

Signed-off-by: Marvin Froeder <velo.br@gmail.com>

* modify: remove inner class, methods binary compatibility validation bypass option

---------

Signed-off-by: Marvin Froeder <velo.br@gmail.com>
Co-authored-by: Marvin Froeder <velo.br@gmail.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Marvin Froeder <velo.br@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Marvin <velo@users.noreply.github.com>
Co-authored-by: Cole-SJ <91147303+Cole-SJ@users.noreply.github.com>
Co-authored-by: Marvin Froeder <velo.br@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants