Skip to content

Commit 517a001

Browse files
FroMagegavinking
authored andcommitted
[HHH-19586] Generate repository accessors in the metamodel of Panache2 entities
Comes with 4 out of the box: - managed/blocking (generated) - managed/reactive (generated if reactive-common is in the CP) - stateless/blocking (generated) - stateless/reactive (generated if reactive-common is in the CP) - whatever nested repositories from the entity -- and if they implement one of the first four, we use this instead of the generated one
1 parent 59a06ee commit 517a001

File tree

3 files changed

+287
-0
lines changed

3 files changed

+287
-0
lines changed

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

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,10 @@ && containsAnnotation( method, HQL, SQL, FIND ) ) {
470470
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
471471

472472
addIdClassIfNeeded( fieldsOfClass, gettersAndSettersOfClass );
473+
474+
if( hasAnnotation( element, ENTITY) && isPanache2Type(element) && !jakartaDataStaticModel ) {
475+
addRepositoryMembers( element );
476+
}
473477
}
474478

475479
addAuxiliaryMembers();
@@ -523,6 +527,41 @@ private void addIdClassIfNeeded(List<VariableElement> fields, List<ExecutableEle
523527
}
524528
}
525529

530+
private void addRepositoryMembers(TypeElement element) {
531+
Element managedBlockingRepository = null;
532+
Element statelessBlockingRepository = null;
533+
Element managedReactiveRepository = null;
534+
Element statelessReactiveRepository = null;
535+
for ( Element enclosedElement : element.getEnclosedElements() ) {
536+
if ( enclosedElement.getKind() == ElementKind.INTERFACE ) {
537+
members.put( enclosedElement.getSimpleName().toString(), new CDIAccessorMetaAttribute( this, enclosedElement ) );
538+
if ( implementsInterface( (TypeElement) enclosedElement, Constants.PANACHE2_MANAGED_BLOCKING_REPOSITORY_BASE ) ) {
539+
managedBlockingRepository = enclosedElement;
540+
}
541+
else if ( implementsInterface( (TypeElement) enclosedElement, Constants.PANACHE2_STATELESS_BLOCKING_REPOSITORY_BASE ) ) {
542+
statelessBlockingRepository = enclosedElement;
543+
}
544+
else if ( implementsInterface( (TypeElement) enclosedElement, Constants.PANACHE2_MANAGED_REACTIVE_REPOSITORY_BASE ) ) {
545+
managedReactiveRepository = enclosedElement;
546+
}
547+
else if ( implementsInterface( (TypeElement) enclosedElement, Constants.PANACHE2_STATELESS_REACTIVE_REPOSITORY_BASE ) ) {
548+
statelessReactiveRepository = enclosedElement;
549+
}
550+
}
551+
}
552+
if ( quarkusInjection ) {
553+
// FIXME: perhaps import id type?
554+
TypeMirror idType = findIdType();
555+
addAccessors(managedBlockingRepository, idType, "managedBlocking", PANACHE2_MANAGED_BLOCKING_REPOSITORY_BASE);
556+
addAccessors(statelessBlockingRepository, idType, "statelessBlocking", PANACHE2_STATELESS_BLOCKING_REPOSITORY_BASE);
557+
// Only add those if HR is in the classpath, otherwise it causes a compilation issue
558+
if( context.usesQuarkusReactiveCommon() ) {
559+
addAccessors(managedReactiveRepository, idType, "managedReactive", PANACHE2_MANAGED_REACTIVE_REPOSITORY_BASE);
560+
addAccessors(statelessReactiveRepository, idType, "statelessReactive", PANACHE2_STATELESS_REACTIVE_REPOSITORY_BASE);
561+
}
562+
}
563+
}
564+
526565
private List<MetaAttribute> getIdMemberNames(List<VariableElement> fields, List<ExecutableElement> methods) {
527566
final List<MetaAttribute> components = new ArrayList<>();
528567
for ( var field : fields ) {
@@ -650,6 +689,56 @@ private boolean isEquivalentPrimitiveType(TypeMirror type, TypeMirror match) {
650689
&& isSameType( context.getTypeUtils().boxedClass( ((PrimitiveType) type) ).asType(), match );
651690
}
652691

692+
private void addAccessors(@Nullable Element repositoryType, @Nullable TypeMirror idType,
693+
String repositoryAccessor, String repositorySuperType) {
694+
TypeElement finalPrimaryEntity = primaryEntity;
695+
if ( repositoryType != null ) {
696+
members.put( repositoryAccessor, new CDIAccessorMetaAttribute( this, repositoryAccessor, repositoryType.getSimpleName().toString() ) );
697+
}
698+
else if ( idType != null && finalPrimaryEntity != null ) {
699+
String repositoryTypeName = "Panache"+repositoryAccessor.substring(0,1).toUpperCase()+repositoryAccessor.substring(1)+"Repository";
700+
members.put( repositoryAccessor, new CDIAccessorMetaAttribute( this, repositoryAccessor, repositoryTypeName ) );
701+
members.put( repositoryAccessor + "Repository", new CDITypeMetaAttribute( this, repositoryTypeName, repositorySuperType +"<"+ finalPrimaryEntity.getSimpleName()+", "+ idType.toString()+">" ) );
702+
}
703+
}
704+
705+
private @Nullable TypeMirror findIdType() {
706+
Element idMember = findIdMember();
707+
TypeElement primaryEntityForTest = primaryEntity;
708+
if ( idMember != null && primaryEntityForTest != null ) {
709+
TypeMirror typedIdMember = this.context.getTypeUtils().asMemberOf((DeclaredType) primaryEntityForTest.asType(), idMember);
710+
return switch(typedIdMember.getKind()) {
711+
case ARRAY, DECLARED, BOOLEAN, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE -> typedIdMember;
712+
case EXECUTABLE -> ((ExecutableType) typedIdMember).getReturnType();
713+
default -> {
714+
message( element,
715+
"Unhandled id member kind: "+typedIdMember+" for id "+idMember,
716+
Diagnostic.Kind.ERROR );
717+
yield null;
718+
}
719+
};
720+
}
721+
return null;
722+
}
723+
724+
private @Nullable Element findIdMember() {
725+
if ( primaryEntity == null ) {
726+
message( element,
727+
"No primary entity defined to find id member",
728+
Diagnostic.Kind.ERROR );
729+
return null;
730+
}
731+
for ( Element member : context.getAllMembers( primaryEntity ) ) {
732+
if ( hasAnnotation( member, ID, EMBEDDED_ID ) ) {
733+
return member;
734+
}
735+
}
736+
message( element,
737+
"Could not find any member annotated with @Id or @EmbeddedId",
738+
Diagnostic.Kind.ERROR );
739+
return null;
740+
}
741+
653742
private boolean checkEntities(List<ExecutableElement> lifecycleMethods, boolean hibernateRepo) {
654743
boolean foundPersistenceEntity = false;
655744
VariableElement nonPersistenceParameter = null;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.processor.annotation;
6+
7+
import javax.lang.model.element.Element;
8+
9+
import org.hibernate.processor.model.MetaAttribute;
10+
import org.hibernate.processor.model.Metamodel;
11+
import org.hibernate.processor.util.StringUtil;
12+
13+
public class CDIAccessorMetaAttribute implements MetaAttribute {
14+
15+
private AnnotationMetaEntity annotationMetaEntity;
16+
private String propertyName;
17+
private String typeName;
18+
19+
public CDIAccessorMetaAttribute(AnnotationMetaEntity annotationMetaEntity, Element repositoryElement) {
20+
this.annotationMetaEntity = annotationMetaEntity;
21+
// turn the name into lowercase
22+
String name = repositoryElement.getSimpleName().toString();
23+
// FIXME: this is wrong for types like STEFQueries
24+
this.propertyName = StringUtil.decapitalize( name );
25+
this.typeName = name;
26+
}
27+
28+
public CDIAccessorMetaAttribute(AnnotationMetaEntity annotationMetaEntity, String propertyName, String className) {
29+
this.annotationMetaEntity = annotationMetaEntity;
30+
this.propertyName = propertyName;
31+
this.typeName = className;
32+
}
33+
34+
@Override
35+
public boolean hasTypedAttribute() {
36+
return true;
37+
}
38+
39+
@Override
40+
public boolean hasStringAttribute() {
41+
return false;
42+
}
43+
44+
@Override
45+
public String getAttributeDeclarationString() {
46+
final StringBuilder declaration = new StringBuilder();
47+
modifiers( declaration );
48+
preamble( declaration );
49+
returnCDI( declaration );
50+
closingBrace( declaration );
51+
return declaration.toString();
52+
}
53+
54+
private void returnCDI(StringBuilder declaration) {
55+
annotationMetaEntity.importType("jakarta.enterprise.inject.spi.CDI");
56+
declaration
57+
.append("\treturn CDI.current().select(")
58+
.append(typeName)
59+
.append(".class).get();\n");
60+
}
61+
62+
void closingBrace(StringBuilder declaration) {
63+
declaration.append("}");
64+
}
65+
66+
void preamble(StringBuilder declaration) {
67+
declaration
68+
.append(typeName)
69+
.append(" ")
70+
.append( getPropertyName() );
71+
declaration
72+
.append("() {\n");
73+
}
74+
75+
@Override
76+
public String getAttributeNameDeclarationString() {
77+
return "";
78+
}
79+
80+
@Override
81+
public String getMetaType() {
82+
throw new UnsupportedOperationException("operation not supported");
83+
}
84+
85+
@Override
86+
public String getPropertyName() {
87+
return propertyName;
88+
}
89+
90+
@Override
91+
public String getTypeDeclaration() {
92+
return "";
93+
}
94+
95+
void modifiers(StringBuilder declaration) {
96+
declaration
97+
.append("\npublic static ");
98+
}
99+
100+
101+
@Override
102+
public Metamodel getHostingEntity() {
103+
return annotationMetaEntity;
104+
}
105+
106+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.processor.annotation;
6+
7+
import org.hibernate.processor.HibernateProcessor;
8+
import org.hibernate.processor.model.MetaAttribute;
9+
import org.hibernate.processor.model.Metamodel;
10+
11+
public class CDITypeMetaAttribute implements MetaAttribute {
12+
13+
private AnnotationMetaEntity annotationMetaEntity;
14+
private String typeName;
15+
private Object superTypeName;
16+
17+
public CDITypeMetaAttribute(AnnotationMetaEntity annotationMetaEntity, String className, String superTypeName) {
18+
this.annotationMetaEntity = annotationMetaEntity;
19+
this.superTypeName = superTypeName;
20+
this.typeName = className;
21+
}
22+
23+
@Override
24+
public boolean hasTypedAttribute() {
25+
return true;
26+
}
27+
28+
@Override
29+
public boolean hasStringAttribute() {
30+
return false;
31+
}
32+
33+
@Override
34+
public String getAttributeDeclarationString() {
35+
final StringBuilder declaration = new StringBuilder();
36+
modifiers( declaration );
37+
preamble( declaration );
38+
closingBrace( declaration );
39+
return declaration.toString();
40+
}
41+
42+
void closingBrace(StringBuilder declaration) {
43+
declaration.append("}");
44+
}
45+
46+
void preamble(StringBuilder declaration) {
47+
declaration
48+
.append("class ")
49+
.append(typeName)
50+
.append(" implements ")
51+
.append( superTypeName );
52+
declaration
53+
.append(" {\n");
54+
}
55+
56+
@Override
57+
public String getAttributeNameDeclarationString() {
58+
return "";
59+
}
60+
61+
@Override
62+
public String getMetaType() {
63+
throw new UnsupportedOperationException("operation not supported");
64+
}
65+
66+
@Override
67+
public String getPropertyName() {
68+
return "";
69+
}
70+
71+
@Override
72+
public String getTypeDeclaration() {
73+
return "";
74+
}
75+
76+
void modifiers(StringBuilder declaration) {
77+
annotationMetaEntity.importType("jakarta.annotation.Generated");
78+
annotationMetaEntity.importType("jakarta.enterprise.context.Dependent");
79+
declaration
80+
.append("\n@Dependent\n")
81+
.append("@Generated(\""+HibernateProcessor.class.getName()+"\")\n");
82+
declaration
83+
.append("public static ");
84+
}
85+
86+
87+
@Override
88+
public Metamodel getHostingEntity() {
89+
return annotationMetaEntity;
90+
}
91+
92+
}

0 commit comments

Comments
 (0)