HHH-17772 make Jakarta Data repositories into proper CDI components

This commit is contained in:
Gavin King 2024-02-24 14:12:21 +01:00
parent 235fc26ee6
commit d6125e21f5
12 changed files with 265 additions and 53 deletions

View File

@ -92,15 +92,15 @@ public final class ClassWriter {
try ( PrintWriter pw = new PrintWriter(sw) ) {
if ( context.addDependentAnnotation() && entity.isInjectable() ) {
pw.println( writeDependentAnnotation( entity ) );
pw.println( writeScopeAnnotation( entity ) );
}
if ( entity.getElement() instanceof TypeElement ) {
if ( entity.getElement() instanceof TypeElement && !entity.isInjectable() ) {
pw.println( writeStaticMetaModelAnnotation( entity, context ) );
}
if ( context.addGeneratedAnnotation() ) {
pw.println( writeGeneratedAnnotation( entity, context ) );
}
if ( context.isAddSuppressWarningsAnnotation() ) {
if ( context.addSuppressWarningsAnnotation() ) {
pw.println( writeSuppressWarnings() );
}
@ -235,8 +235,8 @@ public final class ClassWriter {
return "@SuppressWarnings({\"deprecation\", \"rawtypes\"})";
}
private static String writeDependentAnnotation(Metamodel entity) {
return "@" + entity.importType( "jakarta.enterprise.context.Dependent" );
private static String writeScopeAnnotation(Metamodel entity) {
return "@" + entity.importType( entity.scope() );
}
private static String writeStaticMetaModelAnnotation(Metamodel entity, Context context) {

View File

@ -73,6 +73,7 @@ public final class Context {
private boolean addGeneratedAnnotation = true;
private boolean addGenerationDate;
private boolean addSuppressWarningsAnnotation;
private boolean addTransactionScopedAnnotation;
private AccessType persistenceUnitDefaultAccessType;
private boolean jakartaDataStyle;
@ -167,7 +168,7 @@ public final class Context {
this.addGenerationDate = addGenerationDate;
}
public boolean isAddSuppressWarningsAnnotation() {
public boolean addSuppressWarningsAnnotation() {
return addSuppressWarningsAnnotation;
}
@ -175,6 +176,14 @@ public final class Context {
this.addSuppressWarningsAnnotation = addSuppressWarningsAnnotation;
}
public boolean addTransactionScopedAnnotation() {
return addTransactionScopedAnnotation;
}
public void setAddTransactionScopedAnnotation(boolean addTransactionScopedAnnotation) {
this.addTransactionScopedAnnotation = addTransactionScopedAnnotation;
}
public Elements getElementUtils() {
return processingEnvironment.getElementUtils();
}

View File

@ -149,11 +149,15 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
final PackageElement jakartaContextPackage =
context.getProcessingEnvironment().getElementUtils()
.getPackageElement( "jakarta.enterprise.context" );
final PackageElement jakartaTransactionsPackage =
context.getProcessingEnvironment().getElementUtils()
.getPackageElement( "jakarta.transactions" );
context.setAddInjectAnnotation( jakartaInjectPackage != null );
context.setAddNonnullAnnotation( jakartaAnnotationPackage != null );
context.setAddGeneratedAnnotation( jakartaAnnotationPackage != null );
context.setAddDependentAnnotation( jakartaContextPackage != null );
context.setAddTransactionScopedAnnotation( jakartaTransactionsPackage != null );
final Map<String, String> options = environment.getOptions();

View File

@ -110,7 +110,7 @@ public abstract class AnnotationMeta implements Metamodel {
this,
(SqmSelectStatement<?>) statement,
name.substring(1),
belongsToDao(),
isRepository(),
getSessionType(),
getSessionVariableName(),
getContext().addNonnullAnnotation()
@ -167,7 +167,7 @@ public abstract class AnnotationMeta implements Metamodel {
return "entityManager";
}
abstract boolean belongsToDao();
abstract boolean isRepository();
abstract @Nullable String getSessionType();

View File

@ -139,7 +139,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
/**
* True if this "metamodel class" is actually an instantiable DAO-style repository.
*/
private boolean dao = false;
private boolean repository = false;
/**
* The type of the "session getter" method of a DAO-style repository.
@ -179,7 +179,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
@Override
public boolean isImplementation() {
return dao;
return repository;
}
@Override
@ -265,8 +265,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
}
@Override
boolean belongsToDao() {
return dao;
boolean isRepository() {
return repository;
}
@Override
@ -276,7 +276,19 @@ public class AnnotationMetaEntity extends AnnotationMeta {
@Override
public boolean isInjectable() {
return dao;
return repository;
}
@Override
public String scope() {
if (dataRepository) {
return context.addTransactionScopedAnnotation()
? "javax.transaction.TransactionScoped"
: "jakarta.enterprise.context.RequestScoped";
}
else {
return "jakarta.enterprise.context.Dependent";
}
}
@Override
@ -316,11 +328,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
dataRepository = hasAnnotation( element, JD_REPOSITORY );
findSessionGetter( element );
if ( !dao && dataRepository) {
dao = true;
if ( !repository && dataRepository) {
repository = true;
sessionType = HIB_STATELESS_SESSION;
addDaoConstructor( null );
}
if ( dataRepository ) {
addDefaultConstructor();
}
if ( managed ) {
putMember( "class", new AnnotationMetaType(this) );
@ -340,6 +355,20 @@ public class AnnotationMetaEntity extends AnnotationMeta {
initialized = true;
}
private void addDefaultConstructor() {
final String sessionVariableName = getSessionVariableName(sessionType);
final String typeName = element.getSimpleName().toString() + '_';
putMember("_", new DefaultConstructor(
this,
typeName,
sessionVariableName,
sessionType,
sessionVariableName,
dataStore(),
context.addInjectAnnotation()
));
}
private @Nullable String dataStore() {
final AnnotationMirror repo = getAnnotationMirror( element, JD_REPOSITORY );
if ( repo != null ) {
@ -357,11 +386,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
&& !hasAnnotation( type, Constants.EMBEDDABLE ) ) {
for ( ExecutableElement method : methodsIn( type.getEnclosedElements() ) ) {
if ( isSessionGetter( method ) ) {
dao = true;
repository = true;
sessionType = addDaoConstructor( method );
}
}
if ( !dao ) {
if ( !repository) {
final TypeMirror superclass = type.getSuperclass();
if ( superclass.getKind() == TypeKind.DECLARED ) {
final DeclaredType declaredType = (DeclaredType) superclass;
@ -388,7 +417,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final String name = method == null ? sessionVariableName : method.getSimpleName().toString();
final String typeName = element.getSimpleName().toString() + '_';
putMember( name,
new DaoConstructor(
new RepositoryConstructor(
this,
typeName,
name,
@ -397,7 +426,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
dataStore(),
context.addInjectAnnotation(),
context.addNonnullAnnotation(),
method != null
method != null,
dataRepository
)
);
return sessionType;
@ -751,7 +781,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramNames,
paramTypes,
parameterNullability(method, entity),
dao,
repository,
sessionType[0],
sessionType[1],
enabledFetchProfiles( method ),
@ -906,7 +936,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramNames,
paramTypes,
parameterNullability(method, entity),
dao,
repository,
sessionType[0],
sessionType[1],
enabledFetchProfiles( method ),
@ -925,7 +955,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramNames,
paramTypes,
parameterNullability(method, entity),
dao,
repository,
sessionType[0],
sessionType[1],
enabledFetchProfiles( method ),
@ -959,7 +989,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
returnType.toString(),
paramNames,
paramTypes,
dao,
repository,
sessionType[0],
sessionType[1],
profiles,
@ -977,7 +1007,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramNames,
paramTypes,
parameterNullability(method, entity),
dao,
repository,
sessionType[0],
sessionType[1],
profiles,
@ -996,7 +1026,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramNames,
paramTypes,
parameterNullability(method, entity),
dao,
repository,
sessionType[0],
sessionType[1],
profiles,
@ -1233,7 +1263,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
paramTypes,
isInsertUpdateDelete( queryString ),
isNative,
dao,
repository,
sessionType[0],
sessionType[1],
context.addNonnullAnnotation(),

View File

@ -136,7 +136,7 @@ public class AnnotationMetaPackage extends AnnotationMeta {
}
@Override
boolean belongsToDao() {
boolean isRepository() {
return false;
}
@ -149,4 +149,9 @@ public class AnnotationMetaPackage extends AnnotationMeta {
public boolean isInjectable() {
return false;
}
@Override
public String scope() {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,139 @@
/*
* 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.jpamodelgen.annotation;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants;
import static org.hibernate.jpamodelgen.util.Constants.ENTITY_MANAGER_FACTORY;
import static org.hibernate.jpamodelgen.util.Constants.HIB_SESSION_FACTORY;
/**
* Used by the container to instantiate a Jakarta Data repository.
*
* @author Gavin King
*/
public class DefaultConstructor implements MetaAttribute {
private final Metamodel annotationMetaEntity;
private final String constructorName;
private final String methodName;
private final String sessionTypeName;
private final String sessionVariableName;
private final @Nullable String dataStore;
private final boolean addInjectAnnotation;
public DefaultConstructor(
Metamodel annotationMetaEntity,
String constructorName,
String methodName,
String sessionTypeName,
String sessionVariableName,
@Nullable String dataStore,
boolean addInjectAnnotation) {
this.annotationMetaEntity = annotationMetaEntity;
this.constructorName = constructorName;
this.methodName = methodName;
this.sessionTypeName = sessionTypeName;
this.sessionVariableName = sessionVariableName;
this.dataStore = dataStore;
this.addInjectAnnotation = addInjectAnnotation;
}
@Override
public boolean hasTypedAttribute() {
return true;
}
@Override
public boolean hasStringAttribute() {
return false;
}
@Override
public String getAttributeDeclarationString() {
StringBuilder declaration = new StringBuilder();
declaration.append('\n');
inject( declaration );
declaration
.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");
inject( declaration );
declaration
.append("public ")
.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();
}
private void inject(StringBuilder declaration) {
if ( addInjectAnnotation ) {
declaration
.append('@')
.append(annotationMetaEntity.importType("jakarta.inject.Inject"))
.append('\n');
}
}
@Override
public String getAttributeNameDeclarationString() {
throw new UnsupportedOperationException();
}
@Override
public String getMetaType() {
throw new UnsupportedOperationException();
}
@Override
public String getPropertyName() {
return methodName;
}
@Override
public String getTypeDeclaration() {
return Constants.ENTITY_MANAGER;
}
@Override
public Metamodel getHostingEntity() {
return annotationMetaEntity;
}
}

View File

@ -31,7 +31,7 @@ class NamedQueryMethod implements MetaAttribute {
private final AnnotationMeta annotationMeta;
private final SqmSelectStatement<?> select;
private final String name;
private final boolean belongsToDao;
private final boolean belongsToRepository;
private final boolean reactive;
private final String sessionVariableName;
private final boolean addNonnullAnnotation;
@ -40,14 +40,14 @@ class NamedQueryMethod implements MetaAttribute {
AnnotationMeta annotationMeta,
SqmSelectStatement<?> select,
String name,
boolean belongsToDao,
boolean belongsToRepository,
@Nullable String sessionType,
String sessionVariableName,
boolean addNonnullAnnotation) {
this.annotationMeta = annotationMeta;
this.select = select;
this.name = name;
this.belongsToDao = belongsToDao;
this.belongsToRepository = belongsToRepository;
this.reactive = Constants.MUTINY_SESSION.equals(sessionType);
this.sessionVariableName = sessionVariableName;
this.addNonnullAnnotation = addNonnullAnnotation;
@ -136,7 +136,7 @@ class NamedQueryMethod implements MetaAttribute {
private void modifiers(StringBuilder declaration) {
declaration
.append(belongsToDao ? "public " : "public static ");
.append(belongsToRepository ? "public " : "public static ");
}
private void returnType(StringBuilder declaration) {
@ -160,7 +160,7 @@ class NamedQueryMethod implements MetaAttribute {
private void parameters(TreeSet<SqmParameter<?>> sortedParameters, StringBuilder declaration) {
declaration
.append('(');
if ( !belongsToDao ) {
if ( !belongsToRepository) {
notNull( declaration );
declaration
.append(annotationMeta.importType(Constants.ENTITY_MANAGER))
@ -169,7 +169,7 @@ class NamedQueryMethod implements MetaAttribute {
}
int i = 0;
for ( SqmParameter<?> param : sortedParameters) {
if ( 0 < i++ || !belongsToDao ) {
if ( 0 < i++ || !belongsToRepository) {
declaration
.append(", ");
}

View File

@ -12,9 +12,11 @@ import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants;
/**
* A general purpose constructor which accepts the session.
*
* @author Gavin King
*/
public class DaoConstructor implements MetaAttribute {
public class RepositoryConstructor implements MetaAttribute {
private final Metamodel annotationMetaEntity;
private final String constructorName;
private final String methodName;
@ -24,8 +26,9 @@ public class DaoConstructor implements MetaAttribute {
private final boolean addInjectAnnotation;
private final boolean addNonnullAnnotation;
private final boolean addOverrideAnnotation;
private final boolean dataRepository;
public DaoConstructor(
public RepositoryConstructor(
Metamodel annotationMetaEntity,
String constructorName,
String methodName,
@ -34,7 +37,8 @@ public class DaoConstructor implements MetaAttribute {
@Nullable String dataStore,
boolean addInjectAnnotation,
boolean addNonnullAnnotation,
boolean addOverrideAnnotation) {
boolean addOverrideAnnotation,
boolean dataRepository) {
this.annotationMetaEntity = annotationMetaEntity;
this.constructorName = constructorName;
this.methodName = methodName;
@ -44,6 +48,7 @@ public class DaoConstructor implements MetaAttribute {
this.addInjectAnnotation = addInjectAnnotation;
this.addNonnullAnnotation = addNonnullAnnotation;
this.addOverrideAnnotation = addOverrideAnnotation;
this.dataRepository = dataRepository;
}
@Override
@ -60,21 +65,28 @@ public class DaoConstructor implements MetaAttribute {
public String getAttributeDeclarationString() {
StringBuilder declaration = new StringBuilder();
declaration
.append("\nprivate final ");
.append("\nprivate ");
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(";")
.append("\n");
.append("\n\n");
inject( declaration );
declaration
.append("\npublic ")
.append("public ")
.append(constructorName)
.append("(");
notNull( declaration );
named( declaration );
// named( declaration );
declaration
.append(annotationMetaEntity.importType(sessionTypeName))
.append(" ")
@ -105,22 +117,26 @@ public class DaoConstructor implements MetaAttribute {
return declaration.toString();
}
private void named(StringBuilder declaration) {
if ( addInjectAnnotation && dataStore != null ) {
declaration
.append("@")
.append(annotationMetaEntity.importType("jakarta.inject.Named"))
.append("(\"")
.append(dataStore)
.append("\") ");
}
}
// private void named(StringBuilder declaration) {
// if ( addInjectAnnotation && !dataRepository && dataStore != null ) {
// declaration
// .append('@')
// .append(annotationMetaEntity.importType("jakarta.inject.Named"))
// .append("(\"")
// .append(dataStore)
// .append("\") ");
// }
// }
private void inject(StringBuilder declaration) {
if ( addInjectAnnotation ) {
// Jakarta Data repositories are instantiated
// via the default constructor, so in that
// case, this one is just for testing
if ( addInjectAnnotation && !dataRepository ) {
declaration
.append("\n@")
.append(annotationMetaEntity.importType("jakarta.inject.Inject"));
.append('@')
.append(annotationMetaEntity.importType("jakarta.inject.Inject"))
.append('\n');
}
}

View File

@ -38,4 +38,6 @@ public interface Metamodel extends ImportContext {
boolean isImplementation();
boolean isInjectable();
String scope();
}

View File

@ -75,11 +75,13 @@ public final class Constants {
public static final String CHECK_HQL = "org.hibernate.annotations.processing.CheckHQL";
public static final String ENTITY_MANAGER = "jakarta.persistence.EntityManager";
public static final String ENTITY_MANAGER_FACTORY = "jakarta.persistence.EntityManagerFactory";
public static final String QUERY = "jakarta.persistence.Query";
public static final String TYPED_QUERY = "jakarta.persistence.TypedQuery";
public static final String HIB_QUERY = "org.hibernate.query.Query";
public static final String HIB_SELECTION_QUERY = "org.hibernate.query.SelectionQuery";
public static final String HIB_SESSION = "org.hibernate.Session";
public static final String HIB_SESSION_FACTORY = "org.hibernate.SessionFactory";
public static final String HIB_STATELESS_SESSION = "org.hibernate.StatelessSession";
public static final String MUTINY_SESSION = "org.hibernate.reactive.mutiny.Mutiny.Session";

View File

@ -627,4 +627,9 @@ public class XmlMetaEntity implements Metamodel {
public boolean isInjectable() {
return false;
}
@Override
public String scope() {
throw new UnsupportedOperationException();
}
}