HHH-17873 handle generic supertypes of repositories

This commit is contained in:
Gavin King 2024-03-22 01:08:38 +01:00 committed by Christian Beikov
parent ae56e16b6d
commit 985887964d
10 changed files with 235 additions and 46 deletions

View File

@ -0,0 +1,23 @@
package org.hibernate.processor.test.data.superdao.generic;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import org.hibernate.annotations.NaturalId;
import org.hibernate.processor.test.hqlsql.Publisher;
import java.math.BigDecimal;
import java.time.LocalDate;
@Entity
public class Book {
@Id String isbn;
@NaturalId String title;
String text;
@NaturalId String authorName;
@ManyToOne
Publisher publisher;
BigDecimal price;
int pages;
LocalDate publicationDate;
}

View File

@ -0,0 +1,10 @@
package org.hibernate.processor.test.data.superdao.generic;
import jakarta.data.repository.Repository;
import org.hibernate.annotations.processing.Find;
@Repository
public interface Repo extends SuperRepo<Book,String> {
@Find
Book get(String isbn);
}

View File

@ -0,0 +1,40 @@
package org.hibernate.processor.test.data.superdao.generic;
import jakarta.data.page.Page;
import jakarta.data.page.PageRequest;
import jakarta.data.repository.By;
import jakarta.data.repository.Delete;
import jakarta.data.repository.Find;
import jakarta.data.repository.Save;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public interface SuperRepo<T,K> {
@Save
<S extends T> S save(S entity);
@Save
<S extends T> List<S> saveAll(List<S> entities);
@Find
Optional<T> findById(@By("#id") K id);
@Find
Stream<T> findAll();
@Find
Page<T> findAll(PageRequest<T> pageRequest);
@Delete
void deleteById(@By("#id") K id);
@Delete
void delete(T entity);
@Delete
void deleteAll(List<? extends T> entities);
}

View File

@ -0,0 +1,29 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.processor.test.data.superdao.generic;
import org.hibernate.processor.test.util.CompilationTest;
import org.hibernate.processor.test.util.TestUtil;
import org.hibernate.processor.test.util.WithClasses;
import org.junit.Test;
import static org.hibernate.processor.test.util.TestUtil.assertMetamodelClassGeneratedFor;
/**
* @author Gavin King
*/
public class SuperRepoTest extends CompilationTest {
@Test
@WithClasses({ Book.class, SuperRepo.class, Repo.class })
public void testQueryMethod() {
// System.out.println( TestUtil.getMetaModelSourceAsString( SuperRepo.class ) );
System.out.println( TestUtil.getMetaModelSourceAsString( Repo.class ) );
assertMetamodelClassGeneratedFor( Book.class );
// assertMetamodelClassGeneratedFor( SuperRepo.class );
assertMetamodelClassGeneratedFor( Repo.class );
}
}

View File

@ -365,7 +365,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
if ( isGetterOrSetter( method ) ) { if ( isGetterOrSetter( method ) ) {
gettersAndSettersOfClass.add( method ); gettersAndSettersOfClass.add( method );
} }
else if ( containsAnnotation( method, HQL, SQL, FIND ) ) { else if ( element.getTypeParameters().isEmpty()
&& containsAnnotation( method, HQL, SQL, FIND ) ) {
queryMethods.add( method ); queryMethods.add( method );
} }
} }
@ -427,32 +428,34 @@ public class AnnotationMetaEntity extends AnnotationMeta {
} }
private void setupSession() { private void setupSession() {
jakartaDataRepository = hasAnnotation( element, JD_REPOSITORY ); if ( element.getTypeParameters().isEmpty() ) {
final ExecutableElement getter = findSessionGetter( element ); jakartaDataRepository = hasAnnotation( element, JD_REPOSITORY );
if ( getter != null ) { final ExecutableElement getter = findSessionGetter( element );
// Never make a DAO for Panache subtypes if ( getter != null ) {
if ( !isPanacheType( element ) ) { // Never make a DAO for Panache subtypes
if ( !isPanacheType( element ) ) {
repository = true;
sessionType = addDaoConstructor( getter );
}
else {
// For Panache subtypes, we look at the session type, but no DAO, we want static methods
sessionType = getter.getReturnType().toString();
}
}
else if ( element.getKind() == ElementKind.INTERFACE
&& ( context.usesQuarkusOrm() || context.usesQuarkusReactive() ) ) {
// if we don't have a getter, but we're in Quarkus, we know how to find the default sessions
repository = true; repository = true;
sessionType = addDaoConstructor( getter ); sessionType = setupQuarkusDaoConstructor();
} }
else { if ( !repository && jakartaDataRepository ) {
// For Panache subtypes, we look at the session type, but no DAO, we want static methods repository = true;
sessionType = getter.getReturnType().toString(); sessionType = HIB_STATELESS_SESSION;
addDaoConstructor( null );
}
if ( jakartaDataRepository && !quarkusInjection ) {
addDefaultConstructor();
} }
}
else if ( element.getKind() == ElementKind.INTERFACE
&& ( context.usesQuarkusOrm() || context.usesQuarkusReactive() ) ) {
// if we don't have a getter, but we're in Quarkus, we know how to find the default sessions
repository = true;
sessionType = setupQuarkusDaoConstructor();
}
if ( !repository && jakartaDataRepository ) {
repository = true;
sessionType = HIB_STATELESS_SESSION;
addDaoConstructor( null );
}
if ( jakartaDataRepository && !quarkusInjection ) {
addDefaultConstructor();
} }
} }
@ -720,7 +723,10 @@ public class AnnotationMetaEntity extends AnnotationMeta {
} }
private void addQueryMethod(ExecutableElement method) { private void addQueryMethod(ExecutableElement method) {
TypeMirror returnType = method.getReturnType(); final ExecutableType methodType =
(ExecutableType) context.getTypeUtils()
.asMemberOf((DeclaredType) element.asType(), method);
final TypeMirror returnType = methodType.getReturnType();
final TypeKind kind = returnType.getKind(); final TypeKind kind = returnType.getKind();
if ( kind == TypeKind.VOID || kind == TypeKind.ARRAY || kind.isPrimitive() ) { if ( kind == TypeKind.VOID || kind == TypeKind.ARRAY || kind.isPrimitive() ) {
addQueryMethod( method, returnType, null ); addQueryMethod( method, returnType, null );
@ -872,7 +878,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final String operation = lifecycleOperation( method ); final String operation = lifecycleOperation( method );
final VariableElement parameter = method.getParameters().get(0); final VariableElement parameter = method.getParameters().get(0);
final TypeMirror declaredParameterType = parameter.asType(); final TypeMirror declaredParameterType = parameter.asType();
final TypeMirror parameterType = parameterType( declaredParameterType ); final TypeMirror parameterType = parameterType( parameter );
final DeclaredType declaredType = entityType( parameterType ); final DeclaredType declaredType = entityType( parameterType );
if ( declaredType == null ) { if ( declaredType == null ) {
context.message( parameter, context.message( parameter,
@ -915,6 +921,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
} }
private @Nullable DeclaredType entityType(TypeMirror parameterType) { private @Nullable DeclaredType entityType(TypeMirror parameterType) {
final Types types = context.getTypeUtils();
switch ( parameterType.getKind() ) { switch ( parameterType.getKind() ) {
case TYPEVAR: case TYPEVAR:
final TypeVariable typeVariable = (TypeVariable) parameterType; final TypeVariable typeVariable = (TypeVariable) parameterType;
@ -922,12 +929,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
//INTENTIONAL FALL THROUGH //INTENTIONAL FALL THROUGH
case DECLARED: case DECLARED:
final DeclaredType declaredType = (DeclaredType) parameterType; final DeclaredType declaredType = (DeclaredType) parameterType;
final Types types = context.getTypeUtils();
final Elements elements = context.getElementUtils(); final Elements elements = context.getElementUtils();
if ( types.isAssignable( declaredType, if ( types.isAssignable( declaredType,
types.erasure( elements.getTypeElement(ITERABLE).asType() ) ) types.erasure( elements.getTypeElement(ITERABLE).asType() ) )
&& !declaredType.getTypeArguments().isEmpty() ) { && !declaredType.getTypeArguments().isEmpty() ) {
final TypeMirror elementType = parameterType( declaredType.getTypeArguments().get(0) ); final TypeMirror elementType = types.erasure( declaredType.getTypeArguments().get(0) );
return elementType.getKind() == TypeKind.DECLARED ? (DeclaredType) elementType : null; return elementType.getKind() == TypeKind.DECLARED ? (DeclaredType) elementType : null;
} }
else { else {
@ -935,7 +941,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
} }
case ARRAY: case ARRAY:
final ArrayType arrayType = (ArrayType) parameterType; final ArrayType arrayType = (ArrayType) parameterType;
final TypeMirror componentType = parameterType( arrayType.getComponentType() ); final TypeMirror componentType = types.erasure( arrayType.getComponentType() );
return componentType.getKind() == TypeKind.DECLARED ? (DeclaredType) componentType : null; return componentType.getKind() == TypeKind.DECLARED ? (DeclaredType) componentType : null;
default: default:
return null; return null;
@ -1057,7 +1063,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
else { else {
multivalued.add( false ); multivalued.add( false );
final Types types = context.getTypeUtils(); final Types types = context.getTypeUtils();
final TypeMirror parameterType = parameter.asType(); final TypeMirror parameterType = parameterType( parameter );
final String type = parameterType.toString(); final String type = parameterType.toString();
boolean pageRequest = type.startsWith(JD_PAGE_REQUEST); boolean pageRequest = type.startsWith(JD_PAGE_REQUEST);
if ( isOrderParam(type) || pageRequest ) { if ( isOrderParam(type) || pageRequest ) {
@ -1428,15 +1434,6 @@ public class AnnotationMetaEntity extends AnnotationMeta {
return null; return null;
} }
// final String memberType = attributeType.toString();
// final String paramType = parameterType.toString();
// if ( !isLegalAssignment( paramType, memberType ) ) {
// context.message( param,
// "matching field has type '" + memberType
// + "' in entity class '" + entityType + "'",
// Diagnostic.Kind.ERROR );
// }
if ( checkParameterType( entityType, param, memberType( member ) ) ) { if ( checkParameterType( entityType, param, memberType( member ) ) ) {
return FieldType.MULTIVALUED; return FieldType.MULTIVALUED;
} }
@ -1485,7 +1482,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private boolean checkParameterType(TypeElement entityType, VariableElement param, TypeMirror attributeType) { private boolean checkParameterType(TypeElement entityType, VariableElement param, TypeMirror attributeType) {
final Types types = context.getTypeUtils(); final Types types = context.getTypeUtils();
if ( entityType.getKind() == CLASS ) { // do no checks if the entity type is a type variable if ( entityType.getKind() == CLASS ) { // do no checks if the entity type is a type variable
TypeMirror parameterType = param.asType(); TypeMirror parameterType = parameterType( param );
if ( types.isSameType( parameterType, attributeType ) ) { if ( types.isSameType( parameterType, attributeType ) ) {
return false; return false;
} }
@ -2088,12 +2085,19 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private List<String> parameterTypes(ExecutableElement method) { private List<String> parameterTypes(ExecutableElement method) {
return method.getParameters().stream() return method.getParameters().stream()
.map(param -> parameterType(param.asType()).toString()) .map(param -> parameterType(param).toString())
.collect(toList()); .collect(toList());
} }
private TypeMirror parameterType(TypeMirror type) { private TypeMirror parameterType(VariableElement parameter) {
switch (type.getKind()) { final ExecutableElement method =
(ExecutableElement) parameter.getEnclosingElement();
final ExecutableType methodType =
(ExecutableType) context.getTypeUtils()
.asMemberOf((DeclaredType) element.asType(), method);
final TypeMirror type = methodType.getParameterTypes()
.get( method.getParameters().indexOf(parameter) );
switch ( type.getKind() ) {
case TYPEVAR: case TYPEVAR:
final TypeVariable typeVariable = (TypeVariable) type; final TypeVariable typeVariable = (TypeVariable) type;
return context.getTypeUtils().erasure(typeVariable); return context.getTypeUtils().erasure(typeVariable);
@ -2213,7 +2217,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
if ( returnType != null ) { if ( returnType != null ) {
final Types types = context.getTypeUtils(); final Types types = context.getTypeUtils();
for ( VariableElement parameter : method.getParameters() ) { for ( VariableElement parameter : method.getParameters() ) {
final TypeMirror parameterType = parameter.asType(); final TypeMirror parameterType = parameterType( parameter );
final TypeMirror typeArgument = getTypeArgument( parameterType ); final TypeMirror typeArgument = getTypeArgument( parameterType );
final String type = parameterType.toString(); final String type = parameterType.toString();
final boolean pageRequest = type.startsWith(JD_PAGE_REQUEST); final boolean pageRequest = type.startsWith(JD_PAGE_REQUEST);

View File

@ -20,10 +20,10 @@ public class SuperDaoTest extends CompilationTest {
@Test @Test
@WithClasses({ Book.class, SuperDao.class, Dao.class }) @WithClasses({ Book.class, SuperDao.class, Dao.class })
public void testQueryMethod() { public void testQueryMethod() {
System.out.println( TestUtil.getMetaModelSourceAsString( SuperDao.class ) ); // System.out.println( TestUtil.getMetaModelSourceAsString( SuperDao.class ) );
System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) ); System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) );
assertMetamodelClassGeneratedFor( Book.class ); assertMetamodelClassGeneratedFor( Book.class );
assertMetamodelClassGeneratedFor( SuperDao.class ); // assertMetamodelClassGeneratedFor( SuperDao.class );
assertMetamodelClassGeneratedFor( Dao.class ); assertMetamodelClassGeneratedFor( Dao.class );
} }
} }

View File

@ -0,0 +1,23 @@
package org.hibernate.processor.test.superdao.generic;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import org.hibernate.annotations.NaturalId;
import org.hibernate.processor.test.hqlsql.Publisher;
import java.math.BigDecimal;
import java.time.LocalDate;
@Entity
public class Book {
@Id String isbn;
@NaturalId String title;
String text;
@NaturalId String authorName;
@ManyToOne
Publisher publisher;
BigDecimal price;
int pages;
LocalDate publicationDate;
}

View File

@ -0,0 +1,8 @@
package org.hibernate.processor.test.superdao.generic;
import org.hibernate.annotations.processing.Find;
public interface Dao extends SuperDao<Book,String> {
@Find
Book getConc(String isbn);
}

View File

@ -0,0 +1,23 @@
package org.hibernate.processor.test.superdao.generic;
import jakarta.persistence.EntityManager;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import org.hibernate.annotations.processing.Pattern;
import java.util.List;
public interface SuperDao<T,K> {
EntityManager em();
@Find
T get(K isbn);
@Find
List<T> books1(@Pattern String title);
@HQL("where title like :title")
List<T> books2(String title);
}

View File

@ -0,0 +1,29 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.processor.test.superdao.generic;
import org.hibernate.processor.test.util.CompilationTest;
import org.hibernate.processor.test.util.TestUtil;
import org.hibernate.processor.test.util.WithClasses;
import org.junit.Test;
import static org.hibernate.processor.test.util.TestUtil.assertMetamodelClassGeneratedFor;
/**
* @author Gavin King
*/
public class SuperDaoTest extends CompilationTest {
@Test
@WithClasses({ Book.class, SuperDao.class, Dao.class })
public void testQueryMethod() {
System.out.println( TestUtil.getMetaModelSourceAsString( SuperDao.class ) );
System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) );
assertMetamodelClassGeneratedFor( Book.class );
assertMetamodelClassGeneratedFor( SuperDao.class );
assertMetamodelClassGeneratedFor( Dao.class );
}
}