HHH-17873 initial impl of repository inheritance
needed for Jakarta Data, and useful limited to single inheritance for now
This commit is contained in:
parent
b34462138d
commit
9305610a32
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -137,6 +137,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
*/
|
*/
|
||||||
private boolean repository = false;
|
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.
|
* The type of the "session getter" method of a DAO-style repository.
|
||||||
*/
|
*/
|
||||||
|
@ -215,8 +220,21 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable String getSupertypeName() {
|
public @Nullable String getSupertypeName() {
|
||||||
|
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 );
|
return findMappedSuperClass( this, context );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String getPackageName() {
|
public final String getPackageName() {
|
||||||
|
@ -354,6 +372,13 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
|
|
||||||
setupSession();
|
setupSession();
|
||||||
|
|
||||||
|
if ( repository ) {
|
||||||
|
superRepository = findSuperRepository( element );
|
||||||
|
if ( superRepository != null ) {
|
||||||
|
importType( superRepository.toString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( managed && !jakartaDataStaticModel ) {
|
if ( managed && !jakartaDataStaticModel ) {
|
||||||
putMember( "class", new AnnotationMetaType(this) );
|
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) {
|
private @Nullable ExecutableElement findSessionGetter(TypeElement type) {
|
||||||
if ( !hasAnnotation( type, ENTITY, MAPPED_SUPERCLASS, EMBEDDABLE )
|
if ( !hasAnnotation( type, ENTITY, MAPPED_SUPERCLASS, EMBEDDABLE )
|
||||||
|| isPanacheType( type ) ) {
|
|| isPanacheType( type ) ) {
|
||||||
|
@ -438,7 +487,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
final TypeMirror superclass = type.getSuperclass();
|
final TypeMirror superclass = type.getSuperclass();
|
||||||
if ( superclass.getKind() == TypeKind.DECLARED ) {
|
if ( superclass.getKind() == TypeKind.DECLARED ) {
|
||||||
final DeclaredType declaredType = (DeclaredType) superclass;
|
final DeclaredType declaredType = (DeclaredType) superclass;
|
||||||
ExecutableElement ret = findSessionGetter( (TypeElement) declaredType.asElement() );
|
final ExecutableElement ret = findSessionGetter( (TypeElement) declaredType.asElement() );
|
||||||
if ( ret != null ) {
|
if ( ret != null ) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -446,7 +495,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
for ( TypeMirror superinterface : type.getInterfaces() ) {
|
for ( TypeMirror superinterface : type.getInterfaces() ) {
|
||||||
if ( superinterface.getKind() == TypeKind.DECLARED ) {
|
if ( superinterface.getKind() == TypeKind.DECLARED ) {
|
||||||
final DeclaredType declaredType = (DeclaredType) superinterface;
|
final DeclaredType declaredType = (DeclaredType) superinterface;
|
||||||
ExecutableElement ret = findSessionGetter( (TypeElement) declaredType.asElement() );
|
final ExecutableElement ret = findSessionGetter( (TypeElement) declaredType.asElement() );
|
||||||
if ( ret != null ) {
|
if ( ret != null ) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,8 +57,10 @@ public class DefaultConstructor implements MetaAttribute {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAttributeDeclarationString() {
|
public String getAttributeDeclarationString() {
|
||||||
StringBuilder declaration = new StringBuilder();
|
final StringBuilder declaration = new StringBuilder();
|
||||||
declaration.append('\n');
|
declaration
|
||||||
|
.append('\n');
|
||||||
|
if ( annotationMetaEntity.getSupertypeName() == null ) {
|
||||||
declaration
|
declaration
|
||||||
.append("@")
|
.append("@")
|
||||||
.append(annotationMetaEntity.importType("jakarta.persistence.PersistenceUnit"));
|
.append(annotationMetaEntity.importType("jakarta.persistence.PersistenceUnit"));
|
||||||
|
@ -74,12 +76,6 @@ public class DefaultConstructor implements MetaAttribute {
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(sessionVariableName)
|
.append(sessionVariableName)
|
||||||
.append("Factory;\n\n");
|
.append("Factory;\n\n");
|
||||||
inject( declaration );
|
|
||||||
declaration
|
|
||||||
.append(constructorName)
|
|
||||||
.append("(")
|
|
||||||
.append(") {")
|
|
||||||
.append("\n}\n\n");
|
|
||||||
declaration.append('@')
|
declaration.append('@')
|
||||||
.append(annotationMetaEntity.importType("jakarta.annotation.PostConstruct"))
|
.append(annotationMetaEntity.importType("jakarta.annotation.PostConstruct"))
|
||||||
.append("\nprivate void openSession() {")
|
.append("\nprivate void openSession() {")
|
||||||
|
@ -97,6 +93,13 @@ public class DefaultConstructor implements MetaAttribute {
|
||||||
.append("\n\t")
|
.append("\n\t")
|
||||||
.append(sessionVariableName)
|
.append(sessionVariableName)
|
||||||
.append(".close();")
|
.append(".close();")
|
||||||
|
.append("\n}\n\n");
|
||||||
|
}
|
||||||
|
inject( declaration );
|
||||||
|
declaration
|
||||||
|
.append(constructorName)
|
||||||
|
.append("(")
|
||||||
|
.append(") {")
|
||||||
.append("\n}");
|
.append("\n}");
|
||||||
return declaration.toString();
|
return declaration.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,9 +66,12 @@ public class RepositoryConstructor implements MetaAttribute {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAttributeDeclarationString() {
|
public String getAttributeDeclarationString() {
|
||||||
StringBuilder declaration = new StringBuilder();
|
final StringBuilder declaration = new StringBuilder();
|
||||||
declaration
|
declaration
|
||||||
.append("\nprivate ");
|
.append('\n');
|
||||||
|
if ( annotationMetaEntity.getSupertypeName() == null ) {
|
||||||
|
declaration
|
||||||
|
.append("protected ");
|
||||||
if ( !dataRepository ) {
|
if ( !dataRepository ) {
|
||||||
// don't mark the field final
|
// don't mark the field final
|
||||||
// because it will be initialized
|
// because it will be initialized
|
||||||
|
@ -81,8 +84,8 @@ public class RepositoryConstructor implements MetaAttribute {
|
||||||
.append(annotationMetaEntity.importType(sessionTypeName))
|
.append(annotationMetaEntity.importType(sessionTypeName))
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(sessionVariableName)
|
.append(sessionVariableName)
|
||||||
.append(";")
|
.append(";\n\n");
|
||||||
.append("\n\n");
|
}
|
||||||
inject( declaration );
|
inject( declaration );
|
||||||
declaration
|
declaration
|
||||||
.append("public ")
|
.append("public ")
|
||||||
|
@ -94,13 +97,25 @@ public class RepositoryConstructor implements MetaAttribute {
|
||||||
.append(annotationMetaEntity.importType(sessionTypeName))
|
.append(annotationMetaEntity.importType(sessionTypeName))
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(sessionVariableName)
|
.append(sessionVariableName)
|
||||||
.append(") {")
|
.append(") {\n");
|
||||||
.append("\n\tthis.")
|
if ( annotationMetaEntity.getSupertypeName() != null ) {
|
||||||
|
declaration
|
||||||
|
.append("\tsuper(")
|
||||||
|
.append(sessionVariableName)
|
||||||
|
.append(");\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
declaration
|
||||||
|
.append("\tthis.")
|
||||||
.append(sessionVariableName)
|
.append(sessionVariableName)
|
||||||
.append(" = ")
|
.append(" = ")
|
||||||
.append(sessionVariableName)
|
.append(sessionVariableName)
|
||||||
.append(";")
|
.append(";\n");
|
||||||
.append("\n}")
|
}
|
||||||
|
declaration
|
||||||
|
.append("}");
|
||||||
|
if ( annotationMetaEntity.getSupertypeName() == null ) {
|
||||||
|
declaration
|
||||||
.append("\n\n");
|
.append("\n\n");
|
||||||
if (addOverrideAnnotation) {
|
if (addOverrideAnnotation) {
|
||||||
declaration.append("@Override\n");
|
declaration.append("@Override\n");
|
||||||
|
@ -115,8 +130,8 @@ public class RepositoryConstructor implements MetaAttribute {
|
||||||
.append("() {")
|
.append("() {")
|
||||||
.append("\n\treturn ")
|
.append("\n\treturn ")
|
||||||
.append(sessionVariableName)
|
.append(sessionVariableName)
|
||||||
.append(";")
|
.append(";\n}");
|
||||||
.append("\n}");
|
}
|
||||||
return declaration.toString();
|
return declaration.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -612,17 +612,18 @@ public final class TypeUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable String findMappedSuperClass(Metamodel entity, Context context) {
|
public static @Nullable String findMappedSuperClass(Metamodel entity, Context context) {
|
||||||
Element element = entity.getElement();
|
final Element element = entity.getElement();
|
||||||
if ( element instanceof TypeElement ) {
|
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
|
//superclass of Object is of NoType which returns some other kind
|
||||||
while ( superClass.getKind() == TypeKind.DECLARED ) {
|
while ( superClass.getKind() == TypeKind.DECLARED ) {
|
||||||
final Element superClassElement = ( (DeclaredType) superClass ).asElement();
|
final DeclaredType declaredType = (DeclaredType) superClass;
|
||||||
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
|
final TypeElement superClassElement = (TypeElement) declaredType.asElement();
|
||||||
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
|
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
|
||||||
return superClassName;
|
return superClassElement.getQualifiedName().toString();
|
||||||
}
|
}
|
||||||
superClass = ( (TypeElement) superClassElement ).getSuperclass();
|
superClass = superClassElement.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -643,21 +644,14 @@ public final class TypeUtils {
|
||||||
*/
|
*/
|
||||||
private static boolean extendsSuperMetaModel(Element superClassElement, boolean entityMetaComplete, Context context) {
|
private static boolean extendsSuperMetaModel(Element superClassElement, boolean entityMetaComplete, Context context) {
|
||||||
// if we processed the superclass in the same run we definitely need to extend
|
// if we processed the superclass in the same run we definitely need to extend
|
||||||
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
|
final TypeElement typeElement = (TypeElement) superClassElement;
|
||||||
if ( context.containsMetaEntity( superClassName )
|
final String superClassName = typeElement.getQualifiedName().toString();
|
||||||
|| context.containsMetaEmbeddable( superClassName ) ) {
|
return context.containsMetaEntity( superClassName )
|
||||||
return true;
|
|| context.containsMetaEmbeddable( superClassName )
|
||||||
}
|
|
||||||
|
|
||||||
// to allow for the case that the metamodel class for the super entity is for example contained in another
|
// 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
|
// jar file we use reflection. However, we need to consider the fact that there is xml configuration
|
||||||
// and annotations should be ignored
|
// and annotations should be ignored
|
||||||
if ( !entityMetaComplete
|
|| !entityMetaComplete && containsAnnotation( superClassElement, ENTITY, MAPPED_SUPERCLASS );
|
||||||
&& containsAnnotation( superClassElement, Constants.ENTITY, Constants.MAPPED_SUPERCLASS ) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor8<@Nullable String, Element> {
|
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor8<@Nullable String, Element> {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue