HHH-17873 handle generic supertypes of repositories

This commit is contained in:
Gavin King 2024-03-22 01:08:38 +01:00
parent 3176f25be5
commit e16f0938d4
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 ) ) {
gettersAndSettersOfClass.add( method );
}
else if ( containsAnnotation( method, HQL, SQL, FIND ) ) {
else if ( element.getTypeParameters().isEmpty()
&& containsAnnotation( method, HQL, SQL, FIND ) ) {
queryMethods.add( method );
}
}
@ -427,32 +428,34 @@ public class AnnotationMetaEntity extends AnnotationMeta {
}
private void setupSession() {
jakartaDataRepository = hasAnnotation( element, JD_REPOSITORY );
final ExecutableElement getter = findSessionGetter( element );
if ( getter != null ) {
// Never make a DAO for Panache subtypes
if ( !isPanacheType( element ) ) {
if ( element.getTypeParameters().isEmpty() ) {
jakartaDataRepository = hasAnnotation( element, JD_REPOSITORY );
final ExecutableElement getter = findSessionGetter( element );
if ( getter != null ) {
// 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;
sessionType = addDaoConstructor( getter );
sessionType = setupQuarkusDaoConstructor();
}
else {
// For Panache subtypes, we look at the session type, but no DAO, we want static methods
sessionType = getter.getReturnType().toString();
if ( !repository && jakartaDataRepository ) {
repository = true;
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) {
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();
if ( kind == TypeKind.VOID || kind == TypeKind.ARRAY || kind.isPrimitive() ) {
addQueryMethod( method, returnType, null );
@ -872,7 +878,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final String operation = lifecycleOperation( method );
final VariableElement parameter = method.getParameters().get(0);
final TypeMirror declaredParameterType = parameter.asType();
final TypeMirror parameterType = parameterType( declaredParameterType );
final TypeMirror parameterType = parameterType( parameter );
final DeclaredType declaredType = entityType( parameterType );
if ( declaredType == null ) {
context.message( parameter,
@ -915,6 +921,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
}
private @Nullable DeclaredType entityType(TypeMirror parameterType) {
final Types types = context.getTypeUtils();
switch ( parameterType.getKind() ) {
case TYPEVAR:
final TypeVariable typeVariable = (TypeVariable) parameterType;
@ -922,12 +929,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
//INTENTIONAL FALL THROUGH
case DECLARED:
final DeclaredType declaredType = (DeclaredType) parameterType;
final Types types = context.getTypeUtils();
final Elements elements = context.getElementUtils();
if ( types.isAssignable( declaredType,
types.erasure( elements.getTypeElement(ITERABLE).asType() ) )
&& !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;
}
else {
@ -935,7 +941,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
}
case ARRAY:
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;
default:
return null;
@ -1057,7 +1063,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
else {
multivalued.add( false );
final Types types = context.getTypeUtils();
final TypeMirror parameterType = parameter.asType();
final TypeMirror parameterType = parameterType( parameter );
final String type = parameterType.toString();
boolean pageRequest = type.startsWith(JD_PAGE_REQUEST);
if ( isOrderParam(type) || pageRequest ) {
@ -1428,15 +1434,6 @@ public class AnnotationMetaEntity extends AnnotationMeta {
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 ) ) ) {
return FieldType.MULTIVALUED;
}
@ -1485,7 +1482,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private boolean checkParameterType(TypeElement entityType, VariableElement param, TypeMirror attributeType) {
final Types types = context.getTypeUtils();
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 ) ) {
return false;
}
@ -2088,12 +2085,19 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private List<String> parameterTypes(ExecutableElement method) {
return method.getParameters().stream()
.map(param -> parameterType(param.asType()).toString())
.map(param -> parameterType(param).toString())
.collect(toList());
}
private TypeMirror parameterType(TypeMirror type) {
switch (type.getKind()) {
private TypeMirror parameterType(VariableElement parameter) {
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:
final TypeVariable typeVariable = (TypeVariable) type;
return context.getTypeUtils().erasure(typeVariable);
@ -2213,7 +2217,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
if ( returnType != null ) {
final Types types = context.getTypeUtils();
for ( VariableElement parameter : method.getParameters() ) {
final TypeMirror parameterType = parameter.asType();
final TypeMirror parameterType = parameterType( parameter );
final TypeMirror typeArgument = getTypeArgument( parameterType );
final String type = parameterType.toString();
final boolean pageRequest = type.startsWith(JD_PAGE_REQUEST);

View File

@ -20,10 +20,10 @@ 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( SuperDao.class ) );
System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) );
assertMetamodelClassGeneratedFor( Book.class );
assertMetamodelClassGeneratedFor( SuperDao.class );
// assertMetamodelClassGeneratedFor( SuperDao.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 );
}
}