Skip to content

Commit 846b21c

Browse files
authored
Merge pull request quarkusio#50533 from FroMage/50073
TEST caching for projection queries
2 parents a492f17 + 6a9a923 commit 846b21c

File tree

1 file changed

+26
-6
lines changed
  • extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime

1 file changed

+26
-6
lines changed

extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.Map;
1111
import java.util.Map.Entry;
1212
import java.util.Optional;
13+
import java.util.concurrent.atomic.AtomicReference;
1314
import java.util.stream.Collectors;
1415
import java.util.stream.Stream;
1516

@@ -30,6 +31,21 @@
3031

3132
public class CommonPanacheQueryImpl<Entity> {
3233

34+
/*
35+
* We use this complex caching mechanism to avoid recalculating projection queries
36+
* for recurring classes. In theory this gets stored in the Class object itself so
37+
* it is GCed when the class is disposed, so it auto-cleans itself. The extra
38+
* AtomicReference is as per Franz's advice, for a reason I did not understand.
39+
* We did verify that this improves allocation and cpu a lot, as it avoids
40+
* repeated usage of reflection and string building.
41+
*/
42+
private final static ClassValue<AtomicReference<String>> ProjectionQueryCache = new ClassValue<>() {
43+
@Override
44+
protected AtomicReference<String> computeValue(Class<?> type) {
45+
return new AtomicReference<>();
46+
}
47+
};
48+
3349
private interface NonThrowingCloseable extends AutoCloseable {
3450
@Override
3551
void close();
@@ -120,12 +136,16 @@ public <T> CommonPanacheQueryImpl<T> project(Class<T> type) {
120136
// FIXME: this assumes the query starts with "FROM " probably?
121137

122138
// build select clause with a constructor expression
123-
String selectClause = "SELECT " + getParametersFromClass(type, null);
139+
AtomicReference<String> cachedProjection = ProjectionQueryCache.get(type);
140+
if (cachedProjection.get() == null) {
141+
cachedProjection.set("SELECT " + getParametersFromClass(type, null));
142+
}
143+
String selectClause = cachedProjection.get();
124144
// I think projections do not change the result count, so we can keep the custom count query
125145
return new CommonPanacheQueryImpl<>(this, selectClause + selectQuery, customCountQueryForSpring, null);
126146
}
127147

128-
private StringBuilder getParametersFromClass(Class<?> type, String parentParameter) {
148+
private static StringBuilder getParametersFromClass(Class<?> type, String parentParameter) {
129149
StringBuilder selectClause = new StringBuilder();
130150
Constructor<?> constructor = getConstructor(type);
131151

@@ -138,7 +158,7 @@ private StringBuilder getParametersFromClass(Class<?> type, String parentParamet
138158
return selectClause;
139159
}
140160

141-
private Constructor<?> getConstructor(Class<?> type) {
161+
private static Constructor<?> getConstructor(Class<?> type) {
142162
Constructor<?>[] typeConstructors = type.getDeclaredConstructors();
143163

144164
//We start to look for constructors with @ProjectedConstructor
@@ -172,7 +192,7 @@ private Constructor<?> getConstructor(Class<?> type) {
172192
return typeConstructors[0];
173193
}
174194

175-
private String getParameterName(Class<?> parentType, String parentParameter, Parameter parameter) {
195+
private static String getParameterName(Class<?> parentType, String parentParameter, Parameter parameter) {
176196
String parameterName;
177197
// Check if constructor param is annotated with ProjectedFieldName
178198
if (hasProjectedFieldName(parameter)) {
@@ -201,11 +221,11 @@ private String getParameterName(Class<?> parentType, String parentParameter, Par
201221
}
202222
}
203223

204-
private boolean hasProjectedFieldName(AnnotatedElement annotatedElement) {
224+
private static boolean hasProjectedFieldName(AnnotatedElement annotatedElement) {
205225
return annotatedElement.isAnnotationPresent(ProjectedFieldName.class);
206226
}
207227

208-
private String getNameFromProjectedFieldName(AnnotatedElement annotatedElement) {
228+
private static String getNameFromProjectedFieldName(AnnotatedElement annotatedElement) {
209229
final String name = annotatedElement.getAnnotation(ProjectedFieldName.class).value();
210230
if (name.isEmpty()) {
211231
throw new PanacheQueryException("The annotation ProjectedFieldName must have a non-empty value.");

0 commit comments

Comments
 (0)