Skip to content

Commit f98ce16

Browse files
committed
HHH-18252 fix handling of @IdClass in repository methods
1 parent c938346 commit f98ce16

File tree

10 files changed

+130
-51
lines changed

10 files changed

+130
-51
lines changed

tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/BookAuthorRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public interface BookAuthorRepository {
5656
Book book(String isbn);
5757

5858
@Find
59-
Optional<Book> bookMaybe(@By("#id") String id);
59+
Optional<Book> bookMaybe(@By("id(this)") String id);
6060

6161
@Find
6262
Book[] books(@By("isbn") String[] isbns);

tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/idclass/CompositeIdClassTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ public class CompositeIdClassTest extends CompilationTest {
1717
@Test
1818
@WithClasses({
1919
MyRepository.class,
20+
YourRepository.class,
2021
MyEntity.class,
2122
})
2223
public void test() {
2324
System.out.println( getMetaModelSourceAsString( MyEntity.class ) );
2425
System.out.println( getMetaModelSourceAsString( MyEntity.class, true ) );
2526
System.out.println( getMetaModelSourceAsString( MyRepository.class ) );
27+
System.out.println( getMetaModelSourceAsString( YourRepository.class ) );
2628
assertMetamodelClassGeneratedFor( MyEntity.class );
2729
assertMetamodelClassGeneratedFor( MyEntity.class, true );
2830
assertMetamodelClassGeneratedFor( MyRepository.class );
31+
assertMetamodelClassGeneratedFor( YourRepository.class );
2932
assertPresenceOfMethodInMetamodelFor(
3033
MyRepository.class,
3134
"findById",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.processor.test.data.idclass;
6+
7+
import jakarta.data.repository.BasicRepository;
8+
import jakarta.data.repository.By;
9+
import jakarta.data.repository.Find;
10+
import jakarta.data.repository.Repository;
11+
12+
import java.util.List;
13+
14+
@Repository
15+
public interface YourRepository
16+
extends BasicRepository<MyEntity, MyEntity.MyEntityId> {
17+
@Find
18+
List<MyEntity> findThem(@By("id(this)") MyEntity.MyEntityId id);
19+
@Find
20+
MyEntity findIt(@By("id(this)") MyEntity.MyEntityId id);
21+
22+
}

tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/SuperRepo.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public interface SuperRepo<T,K> {
2626
<S extends T> List<S> saveAll(List<S> entities);
2727

2828
@Find
29-
Optional<T> findById(@By("#id") K id);
29+
Optional<T> findById(@By("id(this)") K id);
3030

3131
@Find
3232
Optional<T> findById2(@By("id(this)") K id);
@@ -37,8 +37,8 @@ public interface SuperRepo<T,K> {
3737
@Find
3838
Page<T> findAll(PageRequest pageRequest, Order<T> order);
3939

40-
// @Delete
41-
// void deleteById(@By("#id") K id);
40+
// @Delete
41+
// void deleteById(@By("id(this)") K id);
4242

4343
@Delete
4444
void delete(T entity);

tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractAnnotatedMethod.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import static java.util.Collections.emptyList;
1515
import static java.util.stream.Collectors.toList;
16+
import static org.hibernate.metamodel.mapping.EntityIdentifierMapping.ID_ROLE_NAME;
1617
import static org.hibernate.processor.util.Constants.ENTITY_MANAGER;
1718
import static org.hibernate.processor.util.Constants.OBJECTS;
1819
import static org.hibernate.processor.util.TypeUtils.hasAnnotation;
@@ -80,12 +81,18 @@ void nullCheck(StringBuilder declaration, String paramName) {
8081
.append('\t')
8182
.append(annotationMetaEntity.staticImport(OBJECTS, "requireNonNull"))
8283
.append('(')
83-
.append(paramName.replace('.', '$'))
84+
.append(parameterName(paramName))
8485
.append(", \"Null ")
85-
.append(paramName)
86+
.append(ID_ROLE_NAME.equals(paramName) ? "id" : paramName)
8687
.append("\");\n");
8788
}
8889

90+
static String parameterName(String name) {
91+
return name.equals(ID_ROLE_NAME)
92+
? "id"
93+
: name.replace('.', '$');
94+
}
95+
8996
protected void handle(StringBuilder declaration, String handled, String rethrown) {
9097
if ( isReactive() ) {
9198
declaration.append( "\n\t\t\t.onFailure(" )

tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractCriteriaMethod.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.List;
1212
import java.util.StringTokenizer;
1313

14+
import static org.hibernate.metamodel.mapping.EntityIdentifierMapping.ID_ROLE_NAME;
1415
import static org.hibernate.processor.util.TypeUtils.getGeneratedClassFullyQualifiedName;
1516
import static org.hibernate.processor.util.TypeUtils.isPrimitive;
1617

@@ -179,7 +180,7 @@ void where(StringBuilder declaration, List<String> paramTypes) {
179180
private void condition(StringBuilder declaration, int i, String paramName, String paramType) {
180181
declaration
181182
.append("\n\t\t\t");
182-
final String parameterName = paramName.replace('.', '$');
183+
final String parameterName = parameterName(paramName);
183184
if ( isNullable(i) && !isPrimitive(paramType) ) {
184185
declaration
185186
.append(parameterName)
@@ -226,15 +227,26 @@ private void path(StringBuilder declaration, String paramName) {
226227
final StringTokenizer tokens = new StringTokenizer(paramName, ".");
227228
String typeName = entity;
228229
while ( typeName != null && tokens.hasMoreTokens() ) {
229-
final TypeElement typeElement = annotationMetaEntity.getContext().getElementUtils()
230-
.getTypeElement( typeName );
230+
final TypeElement typeElement =
231+
annotationMetaEntity.getContext().getElementUtils()
232+
.getTypeElement( typeName );
231233
final String memberName = tokens.nextToken();
232234
declaration
233-
.append(".get(")
234-
.append( annotationMetaEntity.importType( getGeneratedClassFullyQualifiedName( typeElement, false ) ) )
235-
.append('.')
236-
.append(memberName)
237-
.append(')');
235+
.append( ".get(" );
236+
if ( ID_ROLE_NAME.equals(memberName) ) {
237+
declaration
238+
.append( '"' )
239+
.append( memberName )
240+
.append( '"' );
241+
}
242+
else {
243+
declaration
244+
.append( annotationMetaEntity.importType(
245+
getGeneratedClassFullyQualifiedName( typeElement, false ) ) )
246+
.append( '.' )
247+
.append( memberName );
248+
}
249+
declaration.append( ')' );
238250
typeName = annotationMetaEntity.getMemberType(typeName, memberName);
239251
}
240252
}

tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractFinderMethod.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import javax.lang.model.element.ExecutableElement;
1010
import java.util.List;
1111

12+
import static org.hibernate.metamodel.mapping.EntityIdentifierMapping.ID_ROLE_NAME;
1213
import static org.hibernate.processor.util.Constants.HIB_SESSION;
1314

1415
/**
@@ -73,8 +74,9 @@ public String getAttributeNameDeclarationString() {
7374
void comment(StringBuilder declaration) {
7475
declaration
7576
.append("\n/**")
76-
.append("\n * Find ")
77-
.append("{@link ")
77+
.append("\n * ")
78+
.append(this instanceof CriteriaDeleteMethod ? "Delete" : "Find")
79+
.append(" {@link ")
7880
.append(annotationMetaEntity.importType(entity))
7981
.append("}");
8082
long paramCount = paramTypes.stream()
@@ -99,14 +101,20 @@ void comment(StringBuilder declaration) {
99101
}
100102
count++;
101103
final String path = paramNames.get(i);
102-
declaration
103-
.append("{@link ")
104-
.append(annotationMetaEntity.importType(entity))
105-
.append('#')
106-
.append(qualifier(path))
107-
.append(' ')
108-
.append(path)
109-
.append("}");
104+
if ( ID_ROLE_NAME.equals(path) ) {
105+
declaration
106+
.append("identifier");
107+
}
108+
else {
109+
declaration
110+
.append("{@link ")
111+
.append(annotationMetaEntity.importType(entity))
112+
.append('#')
113+
.append(qualifier(path))
114+
.append(' ')
115+
.append(path)
116+
.append("}");
117+
}
110118
}
111119
}
112120
}

tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractQueryMethod.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ void parameters(List<String> paramTypes, StringBuilder declaration) {
170170
declaration
171171
.append(annotationMetaEntity.importType(paramType))
172172
.append(" ")
173-
.append(paramNames.get(i).replace('.', '$'));
173+
.append(parameterName(paramNames.get(i)));
174174
}
175175
declaration
176176
.append(")");
@@ -317,8 +317,9 @@ void handleRestrictionParameters(
317317
}
318318
}
319319
else if ( isRangeParam(paramType) && returnTypeName!= null ) {
320-
final TypeElement entityElement = annotationMetaEntity.getContext().getElementUtils()
321-
.getTypeElement( returnTypeName );
320+
final TypeElement entityElement =
321+
annotationMetaEntity.getContext().getElementUtils()
322+
.getTypeElement( returnTypeName );
322323
declaration
323324
.append("\t_spec.restrict(")
324325
.append(annotationMetaEntity.importType(HIB_RESTRICTION))

tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import static org.hibernate.grammars.hql.HqlLexer.WHERE;
7878
import static org.hibernate.internal.util.StringHelper.qualify;
7979
import static org.hibernate.internal.util.StringHelper.unqualify;
80+
import static org.hibernate.metamodel.mapping.EntityIdentifierMapping.ID_ROLE_NAME;
8081
import static org.hibernate.processor.annotation.AbstractQueryMethod.isRangeParam;
8182
import static org.hibernate.processor.annotation.AbstractQueryMethod.isRestrictionParam;
8283
import static org.hibernate.processor.annotation.AbstractQueryMethod.isSessionParameter;
@@ -2271,7 +2272,9 @@ enum FieldType {
22712272
}
22722273

22732274
private @Nullable FieldType validateFinderParameter(TypeElement entityType, VariableElement param) {
2274-
final Element member = memberMatchingPath( entityType, parameterName( param ) );
2275+
final String path = parameterName( param );
2276+
final boolean idClassRef = isIdRef( path ) && hasAnnotation( entityType, ID_CLASS );
2277+
final Element member = idClassRef ? null : memberMatchingPath( entityType, path );
22752278
if ( member != null ) {
22762279
if ( containsAnnotation( member, MANY_TO_MANY, ONE_TO_MANY, ELEMENT_COLLECTION ) ) {
22772280
message( param,
@@ -2302,25 +2305,47 @@ else if ( containsAnnotation( member, NATURAL_ID ) ) {
23022305
return FieldType.BASIC;
23032306
}
23042307
}
2305-
else {
2308+
else if ( idClassRef ) {
23062309
final AnnotationMirror idClass = getAnnotationMirror( entityType, ID_CLASS );
2307-
if ( idClass != null ) {
2310+
if ( idClass == null ) {
2311+
return null; // cannot happen!
2312+
}
2313+
else {
23082314
final AnnotationValue value = getAnnotationValue( idClass );
2309-
if ( value != null ) {
2310-
if ( isSameType( param.asType(), (TypeMirror) value.getValue() ) ) {
2311-
return FieldType.ID;
2312-
}
2315+
if ( value != null
2316+
&& isSameType( actualParameterType( param ),
2317+
(TypeMirror) value.getValue() ) ) {
2318+
return FieldType.ID;
2319+
}
2320+
else {
2321+
message( param,
2322+
"does not match id class of entity class '" + entityType + "'",
2323+
Diagnostic.Kind.ERROR );
2324+
return null;
23132325
}
23142326
}
2315-
2327+
}
2328+
else {
23162329
message( param,
2317-
"no matching field named '" + parameterName( param )
2318-
+ "' in entity class '" + entityType + "'",
2330+
"no matching field named '" + path
2331+
+ "' in entity class '" + entityType + "'",
23192332
Diagnostic.Kind.ERROR );
23202333
return null;
23212334
}
23222335
}
23232336

2337+
private TypeMirror actualParameterType(VariableElement param) {
2338+
final ExecutableElement method =
2339+
(ExecutableElement)
2340+
param.getEnclosingElement();
2341+
final ExecutableType methodType =
2342+
(ExecutableType)
2343+
context.getTypeUtils()
2344+
.asMemberOf( (DeclaredType) element.asType(), method );
2345+
return methodType.getParameterTypes()
2346+
.get( method.getParameters().indexOf( param ) );
2347+
}
2348+
23242349
/**
23252350
* Check the type of a parameter of a {@code @Find} method against the field type
23262351
* in the entity class.
@@ -2447,8 +2472,7 @@ private static TypeMirror memberType(Element member) {
24472472
}
24482473

24492474
private static boolean isIdRef(String token) {
2450-
return "#id".equals(token) // for Jakarta Data M4 release
2451-
|| "id(this)".equalsIgnoreCase(token); // post M4
2475+
return "id(this)".equalsIgnoreCase(token); // post M4
24522476
}
24532477

24542478
private @Nullable Element memberMatchingPath(
@@ -3165,27 +3189,29 @@ private ExecutableType memberMethodType(ExecutableElement method) {
31653189
private static List<Boolean> parameterPatterns(ExecutableElement method) {
31663190
return method.getParameters().stream()
31673191
.map(param -> hasAnnotation(param, PATTERN))
3168-
.collect(toList());
3192+
.toList();
31693193
}
31703194

31713195
private List<String> parameterNames(ExecutableElement method, TypeElement entity) {
31723196
final String idName =
3173-
// account for special @By("#id") hack in Jakarta Data
3174-
entity.getEnclosedElements().stream()
3175-
.filter(member -> hasAnnotation(member, ID))
3176-
.map(TypeUtils::propertyName)
3177-
.findFirst()
3178-
.orElse("id");
3197+
hasAnnotation( entity, ID_CLASS )
3198+
? ID_ROLE_NAME
3199+
// account for special @By("id(this)") hack in Jakarta Data
3200+
: entity.getEnclosedElements().stream()
3201+
.filter(member -> hasAnnotation(member, ID))
3202+
.map(TypeUtils::propertyName)
3203+
.findFirst()
3204+
.orElse(ID_ROLE_NAME);
31793205
return method.getParameters().stream()
31803206
.map(AnnotationMetaEntity::parameterName)
31813207
.map(name -> isIdRef(name) ? idName : name)
3182-
.collect(toList());
3208+
.toList();
31833209
}
31843210

31853211
private static List<String> parameterNames(ExecutableElement method) {
31863212
return method.getParameters().stream()
31873213
.map(AnnotationMetaEntity::parameterName)
3188-
.collect(toList());
3214+
.toList();
31893215
}
31903216

31913217
private static String parameterName(VariableElement parameter) {

tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/IdFinderMethod.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ private void throwEmptyResult(StringBuilder declaration) {
138138
.append( "(\"No '" )
139139
.append( annotationMetaEntity.importType( entity ) )
140140
.append( "' for given id [\" + " )
141-
.append( paramName )
141+
.append( parameterName(paramName) )
142142
.append( " + \"]\",\n\t\t\t\t\tnew " )
143143
.append( annotationMetaEntity.importType( "org.hibernate.ObjectNotFoundException" ) )
144144
.append( "((Object) " )
145-
.append( paramName )
145+
.append( parameterName(paramName) )
146146
.append( ", \"" )
147147
.append( entity )
148148
.append( "\"))");
@@ -207,7 +207,7 @@ private void findWithNoFetchProfiles(StringBuilder declaration) {
207207
.append(isUsingStatelessSession() ? ".get(" : ".find(")
208208
.append(annotationMetaEntity.importType(entity))
209209
.append(".class, ")
210-
.append(paramName);
210+
.append(parameterName(paramName));
211211
if ( isReactiveSessionAccess() ) {
212212
declaration
213213
.append(')');

0 commit comments

Comments
 (0)