HHH-17873 initial impl of repository inheritance

needed for Jakarta Data, and useful
limited to single inheritance for now
This commit is contained in:
Gavin King 2024-03-20 22:49:55 +01:00
parent b34462138d
commit 9305610a32
12 changed files with 311 additions and 91 deletions

View File

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

View File

@ -0,0 +1,17 @@
package org.hibernate.processor.test.data.superdao;
import jakarta.data.repository.Repository;
import org.hibernate.annotations.processing.Find;
import org.hibernate.annotations.processing.HQL;
import org.hibernate.annotations.processing.Pattern;
import java.util.List;
@Repository
public interface SuperRepo {
@Find
List<Book> books1(@Pattern String title);
@HQL("where title like :title")
List<Book> 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.data.superdao;
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

@ -137,6 +137,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
*/
private boolean repository = false;
/**
* A repository type that this repository inherits.
*/
private @Nullable TypeMirror superRepository;
/**
* The type of the "session getter" method of a DAO-style repository.
*/
@ -215,7 +220,20 @@ public class AnnotationMetaEntity extends AnnotationMeta {
@Override
public @Nullable String getSupertypeName() {
return findMappedSuperClass( this, context );
if ( repository ) {
if ( superRepository == null ) {
return null;
}
else {
final DeclaredType declaredType = (DeclaredType) superRepository;
final TypeElement typeElement = (TypeElement) declaredType.asElement();
// the import should already have been added earlier
return importType( typeElement.getQualifiedName().toString() );
}
}
else {
return findMappedSuperClass( this, context );
}
}
@Override
@ -354,6 +372,13 @@ public class AnnotationMetaEntity extends AnnotationMeta {
setupSession();
if ( repository ) {
superRepository = findSuperRepository( element );
if ( superRepository != null ) {
importType( superRepository.toString() );
}
}
if ( managed && !jakartaDataStaticModel ) {
putMember( "class", new AnnotationMetaType(this) );
}
@ -427,6 +452,30 @@ public class AnnotationMetaEntity extends AnnotationMeta {
}
}
private @Nullable TypeMirror findSuperRepository(TypeElement type) {
for ( TypeMirror superinterface : type.getInterfaces() ) {
if ( superinterface.getKind() == TypeKind.DECLARED ) {
final DeclaredType declaredType = (DeclaredType) superinterface;
final TypeElement typeElement = (TypeElement) declaredType.asElement();
if ( hasAnnotation( typeElement, JD_REPOSITORY ) ) {
return superinterface;
}
else if ( typeElement.getEnclosedElements().stream()
.anyMatch( member -> hasAnnotation( member,
HQL, SQL, JD_QUERY, FIND, JD_FIND, JD_INSERT, JD_UPDATE, JD_DELETE, JD_SAVE ) ) ) {
return superinterface;
}
else {
final TypeMirror ret = findSuperRepository( typeElement );
if ( ret != null ) {
return ret;
}
}
}
}
return null;
}
private @Nullable ExecutableElement findSessionGetter(TypeElement type) {
if ( !hasAnnotation( type, ENTITY, MAPPED_SUPERCLASS, EMBEDDABLE )
|| isPanacheType( type ) ) {
@ -438,7 +487,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final TypeMirror superclass = type.getSuperclass();
if ( superclass.getKind() == TypeKind.DECLARED ) {
final DeclaredType declaredType = (DeclaredType) superclass;
ExecutableElement ret = findSessionGetter( (TypeElement) declaredType.asElement() );
final ExecutableElement ret = findSessionGetter( (TypeElement) declaredType.asElement() );
if ( ret != null ) {
return ret;
}
@ -446,7 +495,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
for ( TypeMirror superinterface : type.getInterfaces() ) {
if ( superinterface.getKind() == TypeKind.DECLARED ) {
final DeclaredType declaredType = (DeclaredType) superinterface;
ExecutableElement ret = findSessionGetter( (TypeElement) declaredType.asElement() );
final ExecutableElement ret = findSessionGetter( (TypeElement) declaredType.asElement() );
if ( ret != null ) {
return ret;
}

View File

@ -57,46 +57,49 @@ public class DefaultConstructor implements MetaAttribute {
@Override
public String getAttributeDeclarationString() {
StringBuilder declaration = new StringBuilder();
declaration.append('\n');
final StringBuilder declaration = new StringBuilder();
declaration
.append("@")
.append(annotationMetaEntity.importType("jakarta.persistence.PersistenceUnit"));
if ( dataStore != null ) {
.append('\n');
if ( annotationMetaEntity.getSupertypeName() == null ) {
declaration
.append("(unitName=\"")
.append(dataStore)
.append("\")");
.append("@")
.append(annotationMetaEntity.importType("jakarta.persistence.PersistenceUnit"));
if ( dataStore != null ) {
declaration
.append("(unitName=\"")
.append(dataStore)
.append("\")");
}
declaration
.append("\nprivate ")
.append(annotationMetaEntity.importType(ENTITY_MANAGER_FACTORY))
.append(" ")
.append(sessionVariableName)
.append("Factory;\n\n");
declaration.append('@')
.append(annotationMetaEntity.importType("jakarta.annotation.PostConstruct"))
.append("\nprivate void openSession() {")
.append("\n\t")
.append(sessionVariableName)
.append(" = ")
.append(sessionVariableName)
.append("Factory.unwrap(")
.append(annotationMetaEntity.importType(HIB_SESSION_FACTORY))
.append(".class).openStatelessSession();")
.append("\n}\n\n");
declaration.append('@')
.append(annotationMetaEntity.importType("jakarta.annotation.PreDestroy"))
.append("\nprivate void closeSession() {")
.append("\n\t")
.append(sessionVariableName)
.append(".close();")
.append("\n}\n\n");
}
declaration
.append("\nprivate ")
.append(annotationMetaEntity.importType(ENTITY_MANAGER_FACTORY))
.append(" ")
.append(sessionVariableName)
.append("Factory;\n\n");
inject( declaration );
declaration
.append(constructorName)
.append("(")
.append(") {")
.append("\n}\n\n");
declaration.append('@')
.append(annotationMetaEntity.importType("jakarta.annotation.PostConstruct"))
.append("\nprivate void openSession() {")
.append("\n\t")
.append(sessionVariableName)
.append(" = ")
.append(sessionVariableName)
.append("Factory.unwrap(")
.append(annotationMetaEntity.importType(HIB_SESSION_FACTORY))
.append(".class).openStatelessSession();")
.append("\n}\n\n");
declaration.append('@')
.append(annotationMetaEntity.importType("jakarta.annotation.PreDestroy"))
.append("\nprivate void closeSession() {")
.append("\n\t")
.append(sessionVariableName)
.append(".close();")
.append("\n}");
return declaration.toString();
}

View File

@ -66,23 +66,26 @@ public class RepositoryConstructor implements MetaAttribute {
@Override
public String getAttributeDeclarationString() {
StringBuilder declaration = new StringBuilder();
final StringBuilder declaration = new StringBuilder();
declaration
.append("\nprivate ");
if ( !dataRepository ) {
// don't mark the field final
// because it will be initialized
// in @PostConstruct
.append('\n');
if ( annotationMetaEntity.getSupertypeName() == null ) {
declaration
.append("final ");
.append("protected ");
if ( !dataRepository ) {
// don't mark the field final
// because it will be initialized
// in @PostConstruct
declaration
.append("final ");
}
notNull( declaration );
declaration
.append(annotationMetaEntity.importType(sessionTypeName))
.append(" ")
.append(sessionVariableName)
.append(";\n\n");
}
notNull( declaration );
declaration
.append(annotationMetaEntity.importType(sessionTypeName))
.append(" ")
.append(sessionVariableName)
.append(";")
.append("\n\n");
inject( declaration );
declaration
.append("public ")
@ -94,29 +97,41 @@ public class RepositoryConstructor implements MetaAttribute {
.append(annotationMetaEntity.importType(sessionTypeName))
.append(" ")
.append(sessionVariableName)
.append(") {")
.append("\n\tthis.")
.append(sessionVariableName)
.append(" = ")
.append(sessionVariableName)
.append(";")
.append("\n}")
.append("\n\n");
if (addOverrideAnnotation) {
declaration.append("@Override\n");
.append(") {\n");
if ( annotationMetaEntity.getSupertypeName() != null ) {
declaration
.append("\tsuper(")
.append(sessionVariableName)
.append(");\n");
}
else {
declaration
.append("\tthis.")
.append(sessionVariableName)
.append(" = ")
.append(sessionVariableName)
.append(";\n");
}
declaration
.append("public ");
notNull( declaration );
declaration
.append(annotationMetaEntity.importType(sessionTypeName))
.append(" ")
.append(methodName)
.append("() {")
.append("\n\treturn ")
.append(sessionVariableName)
.append(";")
.append("\n}");
.append("}");
if ( annotationMetaEntity.getSupertypeName() == null ) {
declaration
.append("\n\n");
if (addOverrideAnnotation) {
declaration.append("@Override\n");
}
declaration
.append("public ");
notNull( declaration );
declaration
.append(annotationMetaEntity.importType(sessionTypeName))
.append(" ")
.append(methodName)
.append("() {")
.append("\n\treturn ")
.append(sessionVariableName)
.append(";\n}");
}
return declaration.toString();
}

View File

@ -612,17 +612,18 @@ public final class TypeUtils {
}
public static @Nullable String findMappedSuperClass(Metamodel entity, Context context) {
Element element = entity.getElement();
final Element element = entity.getElement();
if ( element instanceof TypeElement ) {
TypeMirror superClass = ((TypeElement) element).getSuperclass();
final TypeElement typeElement = (TypeElement) element;
TypeMirror superClass = typeElement.getSuperclass();
//superclass of Object is of NoType which returns some other kind
while ( superClass.getKind() == TypeKind.DECLARED ) {
final Element superClassElement = ( (DeclaredType) superClass ).asElement();
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
final DeclaredType declaredType = (DeclaredType) superClass;
final TypeElement superClassElement = (TypeElement) declaredType.asElement();
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
return superClassName;
return superClassElement.getQualifiedName().toString();
}
superClass = ( (TypeElement) superClassElement ).getSuperclass();
superClass = superClassElement.getSuperclass();
}
}
return null;
@ -643,21 +644,14 @@ public final class TypeUtils {
*/
private static boolean extendsSuperMetaModel(Element superClassElement, boolean entityMetaComplete, Context context) {
// if we processed the superclass in the same run we definitely need to extend
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
if ( context.containsMetaEntity( superClassName )
|| context.containsMetaEmbeddable( superClassName ) ) {
return true;
}
// to allow for the case that the metamodel class for the super entity is for example contained in another
// jar file we use reflection. However, we need to consider the fact that there is xml configuration
// and annotations should be ignored
if ( !entityMetaComplete
&& containsAnnotation( superClassElement, Constants.ENTITY, Constants.MAPPED_SUPERCLASS ) ) {
return true;
}
return false;
final TypeElement typeElement = (TypeElement) superClassElement;
final String superClassName = typeElement.getQualifiedName().toString();
return context.containsMetaEntity( superClassName )
|| context.containsMetaEmbeddable( superClassName )
// to allow for the case that the metamodel class for the super entity is for example contained in another
// jar file we use reflection. However, we need to consider the fact that there is xml configuration
// and annotations should be ignored
|| !entityMetaComplete && containsAnnotation( superClassElement, ENTITY, MAPPED_SUPERCLASS );
}
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor8<@Nullable String, Element> {

View File

@ -0,0 +1,23 @@
package org.hibernate.processor.test.superdao;
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;
import org.hibernate.annotations.processing.Find;
public interface Dao extends SuperDao {
@Find
Book get(String isbn);
}

View File

@ -0,0 +1,20 @@
package org.hibernate.processor.test.superdao;
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 {
EntityManager em();
@Find
List<Book> books1(@Pattern String title);
@HQL("where title like :title")
List<Book> 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;
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 );
}
}