Skip to content

Commit 8835aa5

Browse files
authored
Made possible to generate projections on java records (#164)
Co-authored-by: Marvin Froeder <[email protected]>
1 parent 6a7af2a commit 8835aa5

File tree

8 files changed

+44
-25
lines changed

8 files changed

+44
-25
lines changed

querydsl-apt/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@
116116
<artifactId>ecj</artifactId>
117117
<scope>test</scope>
118118
</dependency>
119+
120+
<dependency>
121+
<groupId>org.junit.jupiter</groupId>
122+
<artifactId>junit-jupiter</artifactId>
123+
<scope>test</scope>
124+
</dependency>
119125
</dependencies>
120126

121127
<build>

querydsl-apt/src/main/java/com/querydsl/apt/AbstractQuerydslProcessor.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,20 @@ private void registerTypeElement(String entityName, TypeElement element) {
315315
private void processProjectionTypes(Set<TypeElement> elements) {
316316
Set<Element> visited = new HashSet<Element>();
317317
for (Element element : getElements(QueryProjection.class)) {
318-
Element parent = element.getEnclosingElement();
319-
if (!elements.contains(parent) && !visited.contains(parent)) {
320-
EntityType model = elementHandler.handleProjectionType((TypeElement) parent);
321-
registerTypeElement(model.getFullName(), (TypeElement) parent);
318+
if (element.getKind() == ElementKind.CONSTRUCTOR) {
319+
Element parent = element.getEnclosingElement();
320+
if (!elements.contains(parent) && !visited.contains(parent)) {
321+
EntityType model = elementHandler.handleProjectionType((TypeElement) parent, true);
322+
registerTypeElement(model.getFullName(), (TypeElement) parent);
323+
context.projectionTypes.put(model.getFullName(), model);
324+
visited.add(parent);
325+
}
326+
}
327+
if (element.getKind().isClass() && !visited.contains(element)) {
328+
EntityType model = elementHandler.handleProjectionType((TypeElement) element, false);
329+
registerTypeElement(model.getFullName(), (TypeElement) element);
322330
context.projectionTypes.put(model.getFullName(), model);
323-
visited.add(parent);
331+
visited.add(element);
324332
}
325333
}
326334
}

querydsl-apt/src/main/java/com/querydsl/apt/Configuration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public interface Configuration {
8383

8484
boolean isUseGetters();
8585

86-
boolean isValidConstructor(ExecutableElement constructor);
86+
boolean isValidConstructor(ExecutableElement constructor, boolean onlyAnnotatedConstructors);
8787

8888
boolean isValidField(VariableElement field);
8989

querydsl-apt/src/main/java/com/querydsl/apt/DefaultConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,9 +469,10 @@ public boolean isUseGetters() {
469469
}
470470

471471
@Override
472-
public boolean isValidConstructor(ExecutableElement constructor) {
472+
public boolean isValidConstructor(
473+
ExecutableElement constructor, boolean onlyAnnotatedConstructors) {
473474
return constructor.getModifiers().contains(Modifier.PUBLIC)
474-
&& constructor.getAnnotation(QueryProjection.class) != null
475+
&& (!onlyAnnotatedConstructors || constructor.getAnnotation(QueryProjection.class) != null)
475476
&& !constructor.getParameters().isEmpty();
476477
}
477478

querydsl-apt/src/main/java/com/querydsl/apt/TypeElementHandler.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public EntityType handleEntityType(TypeElement element) {
7878

7979
// constructors
8080
if (config.visitConstructors()) {
81-
handleConstructors(entityType, elements);
81+
handleConstructors(entityType, elements, true);
8282
}
8383

8484
// fields
@@ -175,13 +175,13 @@ private Property toProperty(
175175
return new Property(entityType, name, propertyType, inits);
176176
}
177177

178-
public EntityType handleProjectionType(TypeElement e) {
178+
public EntityType handleProjectionType(TypeElement e, boolean onlyAnnotatedConstructors) {
179179
Type c = typeFactory.getType(e.asType(), true);
180180
EntityType entityType =
181181
new EntityType(c.as(TypeCategory.ENTITY), configuration.getVariableNameFunction());
182182
typeMappings.register(entityType, queryTypeFactory.create(entityType));
183183
List<? extends Element> elements = e.getEnclosedElements();
184-
handleConstructors(entityType, elements);
184+
handleConstructors(entityType, elements, onlyAnnotatedConstructors);
185185
return entityType;
186186
}
187187

@@ -197,9 +197,10 @@ private Type getType(VariableElement element) {
197197
return rv;
198198
}
199199

200-
private void handleConstructors(EntityType entityType, List<? extends Element> elements) {
200+
private void handleConstructors(
201+
EntityType entityType, List<? extends Element> elements, boolean onlyAnnotatedConstructors) {
201202
for (ExecutableElement constructor : ElementFilter.constructorsIn(elements)) {
202-
if (configuration.isValidConstructor(constructor)) {
203+
if (configuration.isValidConstructor(constructor, onlyAnnotatedConstructors)) {
203204
List<Parameter> parameters = transformParams(constructor.getParameters());
204205
entityType.addConstructor(new Constructor(parameters));
205206
}

querydsl-apt/src/test/java/com/querydsl/apt/GenericExporterTest.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.querydsl.apt;
22

3-
import static org.assertj.core.api.Assertions.fail;
3+
import static org.assertj.core.api.Assertions.assertThat;
44

55
import com.querydsl.apt.domain.AbstractEntityTest;
66
import com.querydsl.apt.domain.CustomCollection;
@@ -147,16 +147,8 @@ private void execute(List<String> expected, String genericExporterFolder, String
147147
expected.remove("QGeneric4Test_HidaBez.java"); // unstable
148148
expected.remove("QGeneric16Test_HidaBezGruppe.java"); // unstable
149149
expected.remove("QGeneric4Test_HidaBezGruppe.java"); // unstable
150-
if (!expected.isEmpty()) {
151-
fail("Following expected failures succeeded: " + expected);
152-
}
153150

154-
if (!failures.isEmpty()) {
155-
for (String failure : failures) {
156-
System.err.println(failure);
157-
}
158-
fail(
159-
"Failed with " + failures.size() + " failures, " + successes + " succeeded, " + failures);
160-
}
151+
assertThat(expected).isEmpty();
152+
assertThat(failures).isEmpty();
161153
}
162154
}

querydsl-apt/src/test/java/com/querydsl/apt/domain/QueryProjectionTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,15 @@ public void dto_case() throws SecurityException, NoSuchMethodException {
100100
new QQueryProjectionTest_DTOWithProjection(longExpr, stringExpr).newInstance(0L, "");
101101
new QQueryProjectionTest_DTOWithProjection(stringExpr, stringExpr).newInstance("", "");
102102
}
103+
104+
@QueryProjection
105+
public static record RecordProjection(long param0, String param1) {}
106+
107+
@Test
108+
public void record_case() throws SecurityException, NoSuchMethodException {
109+
NumberExpression<Long> longExpr = Expressions.numberPath(Long.class, "x");
110+
StringExpression stringExpr = Expressions.stringPath("x");
111+
112+
new QQueryProjectionTest_RecordProjection(longExpr, stringExpr).newInstance(0L, "");
113+
}
103114
}

querydsl-core/src/main/java/com/querydsl/core/annotations/QueryProjection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,6 @@
5151
* }</pre>
5252
*/
5353
@Documented
54-
@Target(ElementType.CONSTRUCTOR)
54+
@Target({ElementType.CONSTRUCTOR, ElementType.TYPE})
5555
@Retention(RUNTIME)
5656
public @interface QueryProjection {}

0 commit comments

Comments
 (0)