Skip to content

Commit

Permalink
Merge pull request quarkusio#28359 from loicmathieu/record-inside-pan…
Browse files Browse the repository at this point in the history
…ache

Record inside panache
  • Loading branch information
FroMage authored Oct 21, 2022
2 parents e50ae5d + f97a54a commit c61912b
Show file tree
Hide file tree
Showing 20 changed files with 416 additions and 31 deletions.
4 changes: 3 additions & 1 deletion docs/src/main/asciidoc/hibernate-orm-panache.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ Hibernate will use **DTO projection** and generate a SELECT clause with the attr
This is also called **dynamic instantiation** or **constructor expression**, more info can be found on the Hibernate guide:
link:https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#hql-select-clause[hql select clause]

The projection class needs to be a valid Java Bean and have a constructor that contains all its attributes, this constructor will be used to
The projection class needs to have a constructor that contains all its attributes, this constructor will be used to
instantiate the projection DTO instead of using the entity class. This class must have a matching constructor with all the class attributes as parameters.


Expand Down Expand Up @@ -765,6 +765,8 @@ so the compiler must be configured to store parameter names inside the compiled
This is enabled by default if you are using the Quarkus Maven archetype. If you are not using it, add the property `<maven.compiler.parameters>true</maven.compiler.parameters>` to your `pom.xml`.
====

TIP: If you run Java 17+, records are a good fit for projection classes.

If in the DTO projection object you have a field from a referenced entity, you can use the `@ProjectedFieldName` annotation to provide the path for the SELECT statement.

[source,java]
Expand Down
2 changes: 2 additions & 0 deletions docs/src/main/asciidoc/mongodb-panache.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,8 @@ TIP: Using `@BsonProperty` is not needed to define custom column mappings, as th

TIP: You can have your projection class extends from another class. In this case, the parent class also needs to have use `@ProjectionFor` annotation.

TIP: If you run Java 17+, records are a good fit for projection classes.

== Query debugging

As MongoDB with Panache allows writing simplified queries, it is sometimes handy to log the generated native queries for debugging purpose.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void buildNamedQueryMap(List<PanacheNamedQueryEntityClassBuildStep> namedQueryEn
}

private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set<String> namedQueries) {
ClassInfo classInfo = index.getIndex().getClassByName(name);
ClassInfo classInfo = index.getComputingIndex().getClassByName(name);
if (classInfo == null) {
return;
}
Expand All @@ -80,7 +80,7 @@ private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set<
// climb up the hierarchy of types
if (!classInfo.superClassType().name().equals(JandexUtil.DOTNAME_OBJECT)) {
Type superType = classInfo.superClassType();
ClassInfo superClass = index.getIndex().getClassByName(superType.name());
ClassInfo superClass = index.getComputingIndex().getClassByName(superType.name());
if (superClass != null) {
lookupNamedQueries(index, superClass.name(), namedQueries);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ AdditionalBeanBuildItem registerTransactionalExecutor() {
void findEntityResources(CombinedIndexBuildItem index,
BuildProducer<GeneratedBeanBuildItem> implementationsProducer,
BuildProducer<RestDataResourceBuildItem> restDataResourceProducer) {
ResourceImplementor resourceImplementor = new ResourceImplementor(new EntityClassHelper(index.getIndex()));
ResourceImplementor resourceImplementor = new ResourceImplementor(new EntityClassHelper(index.getComputingIndex()));
ClassOutput classOutput = new GeneratedBeanGizmoAdaptor(implementationsProducer);

for (ClassInfo classInfo : index.getIndex().getKnownDirectImplementors(PANACHE_ENTITY_RESOURCE_INTERFACE)) {
validateResource(index.getIndex(), classInfo);
for (ClassInfo classInfo : index.getComputingIndex().getKnownDirectImplementors(PANACHE_ENTITY_RESOURCE_INTERFACE)) {
validateResource(index.getComputingIndex(), classInfo);

List<Type> generics = getGenericTypes(classInfo);
String resourceInterface = classInfo.name().toString();
Expand All @@ -95,11 +95,12 @@ void findRepositoryResources(CombinedIndexBuildItem index,
BuildProducer<GeneratedBeanBuildItem> implementationsProducer,
BuildProducer<RestDataResourceBuildItem> restDataResourceProducer,
BuildProducer<UnremovableBeanBuildItem> unremovableBeansProducer) {
ResourceImplementor resourceImplementor = new ResourceImplementor(new EntityClassHelper(index.getIndex()));
ResourceImplementor resourceImplementor = new ResourceImplementor(new EntityClassHelper(index.getComputingIndex()));
ClassOutput classOutput = new GeneratedBeanGizmoAdaptor(implementationsProducer);

for (ClassInfo classInfo : index.getIndex().getKnownDirectImplementors(PANACHE_REPOSITORY_RESOURCE_INTERFACE)) {
validateResource(index.getIndex(), classInfo);
for (ClassInfo classInfo : index.getComputingIndex()
.getKnownDirectImplementors(PANACHE_REPOSITORY_RESOURCE_INTERFACE)) {
validateResource(index.getComputingIndex(), classInfo);

List<Type> generics = getGenericTypes(classInfo);
String resourceInterface = classInfo.name().toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void buildNamedQueryMap(List<PanacheNamedQueryEntityClassBuildStep> namedQueryEn
}

private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set<String> namedQueries) {
ClassInfo classInfo = index.getIndex().getClassByName(name);
ClassInfo classInfo = index.getComputingIndex().getClassByName(name);
if (classInfo == null) {
return;
}
Expand All @@ -115,7 +115,7 @@ private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set<
// climb up the hierarchy of types
if (!classInfo.superClassType().name().equals(JandexUtil.DOTNAME_OBJECT)) {
Type superType = classInfo.superClassType();
ClassInfo superClass = index.getIndex().getClassByName(superType.name());
ClassInfo superClass = index.getComputingIndex().getClassByName(superType.name());
if (superClass != null) {
lookupNamedQueries(index, superClass.name(), namedQueries);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ protected void buildReplacementMap(List<PropertyMappingClassBuildStep> propertyM
Map<String, Map<String, String>> replacementMap = new ConcurrentHashMap<>();
for (PropertyMappingClassBuildStep classToMap : propertyMappingClasses) {
DotName dotName = createSimple(classToMap.getClassName());
ClassInfo classInfo = index.getIndex().getClassByName(dotName);
ClassInfo classInfo = index.getComputingIndex().getClassByName(dotName);
if (classInfo != null) {
// only compute field replacement for types inside the index
Map<String, String> classReplacementMap = replacementMap.computeIfAbsent(classToMap.getClassName(),
Expand Down Expand Up @@ -179,7 +179,7 @@ private void extractMappings(Map<String, String> classPropertyMapping, ClassInfo
// climb up the hierarchy of types
if (!target.superClassType().name().equals(JandexUtil.DOTNAME_OBJECT)) {
Type superType = target.superClassType();
ClassInfo superClass = index.getIndex().getClassByName(superType.name());
ClassInfo superClass = index.getComputingIndex().getClassByName(superType.name());
extractMappings(classPropertyMapping, superClass, index);
}
}
Expand All @@ -206,14 +206,14 @@ protected void handleProjectionFor(CombinedIndexBuildItem index,
BuildProducer<BytecodeTransformerBuildItem> transformers) {
// manage @BsonProperty for the @ProjectionFor annotation
Map<DotName, Map<String, String>> propertyMapping = new HashMap<>();
for (AnnotationInstance annotationInstance : index.getIndex().getAnnotations(PROJECTION_FOR)) {
for (AnnotationInstance annotationInstance : index.getComputingIndex().getAnnotations(PROJECTION_FOR)) {
Type targetClass = annotationInstance.value().asClass();
ClassInfo target = index.getIndex().getClassByName(targetClass.name());
ClassInfo target = index.getComputingIndex().getClassByName(targetClass.name());
Map<String, String> classPropertyMapping = new HashMap<>();
extractMappings(classPropertyMapping, target, index);
propertyMapping.put(targetClass.name(), classPropertyMapping);
}
for (AnnotationInstance annotationInstance : index.getIndex().getAnnotations(PROJECTION_FOR)) {
for (AnnotationInstance annotationInstance : index.getComputingIndex().getAnnotations(PROJECTION_FOR)) {
Type targetClass = annotationInstance.value().asClass();
Map<String, String> targetPropertyMapping = propertyMapping.get(targetClass.name());
if (targetPropertyMapping != null && !targetPropertyMapping.isEmpty()) {
Expand Down Expand Up @@ -259,14 +259,14 @@ protected void processEntities(CombinedIndexBuildItem index,
Set<String> modelClasses = new HashSet<>();
// Note that we do this in two passes because for some reason Jandex does not give us subtypes
// of PanacheMongoEntity if we ask for subtypes of PanacheMongoEntityBase
for (ClassInfo classInfo : index.getIndex().getAllKnownSubclasses(typeBundle.entityBase().dotName())) {
for (ClassInfo classInfo : index.getComputingIndex().getAllKnownSubclasses(typeBundle.entityBase().dotName())) {
if (classInfo.name().equals(typeBundle.entity().dotName())) {
continue;
}
if (modelClasses.add(classInfo.name().toString()))
modelInfo.addEntityModel(createEntityModel(classInfo));
}
for (ClassInfo classInfo : index.getIndex().getAllKnownSubclasses(typeBundle.entity().dotName())) {
for (ClassInfo classInfo : index.getComputingIndex().getAllKnownSubclasses(typeBundle.entity().dotName())) {
if (modelClasses.add(classInfo.name().toString()))
modelInfo.addEntityModel(createEntityModel(classInfo));
}
Expand Down Expand Up @@ -338,22 +338,22 @@ protected void processRepositories(CombinedIndexBuildItem index,
Set<String> daoClasses = new HashSet<>();
Set<Type> daoTypeParameters = new HashSet<>();
DotName dotName = typeBundle.repositoryBase().dotName();
for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(dotName)) {
for (ClassInfo classInfo : index.getComputingIndex().getAllKnownImplementors(dotName)) {
// Skip PanacheMongoRepository and abstract repositories
if (classInfo.name().equals(typeBundle.repository().dotName()) || repositoryEnhancer.skipRepository(classInfo)) {
continue;
}
daoClasses.add(classInfo.name().toString());
daoTypeParameters.addAll(
resolveTypeParameters(classInfo.name(), typeBundle.repositoryBase().dotName(), index.getIndex()));
resolveTypeParameters(classInfo.name(), typeBundle.repositoryBase().dotName(), index.getComputingIndex()));
}
for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(typeBundle.repository().dotName())) {
for (ClassInfo classInfo : index.getComputingIndex().getAllKnownImplementors(typeBundle.repository().dotName())) {
if (repositoryEnhancer.skipRepository(classInfo)) {
continue;
}
daoClasses.add(classInfo.name().toString());
daoTypeParameters.addAll(
resolveTypeParameters(classInfo.name(), typeBundle.repositoryBase().dotName(), index.getIndex()));
resolveTypeParameters(classInfo.name(), typeBundle.repositoryBase().dotName(), index.getComputingIndex()));
}
for (String daoClass : daoClasses) {
transformers.produce(new BytecodeTransformerBuildItem(daoClass, repositoryEnhancer));
Expand Down Expand Up @@ -416,16 +416,16 @@ public void unremovableClients(BuildProducer<MongoUnremovableClientsBuildItem> u
protected ValidationPhaseBuildItem.ValidationErrorBuildItem validate(ValidationPhaseBuildItem validationPhase,
CombinedIndexBuildItem index) throws BuildException {
// we verify that no ID fields are defined (via @BsonId) when extending PanacheMongoEntity or ReactivePanacheMongoEntity
for (AnnotationInstance annotationInstance : index.getIndex().getAnnotations(BSON_ID)) {
for (AnnotationInstance annotationInstance : index.getComputingIndex().getAnnotations(BSON_ID)) {
ClassInfo info = JandexUtil.getEnclosingClass(annotationInstance);
if (JandexUtil.isSubclassOf(index.getIndex(), info,
if (JandexUtil.isSubclassOf(index.getComputingIndex(), info,
getImperativeTypeBundle().entity().dotName())) {
BuildException be = new BuildException("You provide a MongoDB identifier via @BsonId inside '" + info.name() +
"' but one is already provided by PanacheMongoEntity, " +
"your class should extend PanacheMongoEntityBase instead, or use the id provided by PanacheMongoEntity",
Collections.emptyList());
return new ValidationPhaseBuildItem.ValidationErrorBuildItem(be);
} else if (JandexUtil.isSubclassOf(index.getIndex(), info,
} else if (JandexUtil.isSubclassOf(index.getComputingIndex(), info,
getReactiveTypeBundle().entity().dotName())) {
BuildException be = new BuildException("You provide a MongoDB identifier via @BsonId inside '" + info.name() +
"' but one is already provided by ReactivePanacheMongoEntity, " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ void findEntityResources(CombinedIndexBuildItem index, Capabilities capabilities
BuildProducer<GeneratedBeanBuildItem> implementationsProducer,
BuildProducer<RestDataResourceBuildItem> restDataResourceProducer,
BuildProducer<BytecodeTransformerBuildItem> bytecodeTransformersProducer) {
EntityClassHelper entityClassHelper = new EntityClassHelper(index.getIndex());
EntityClassHelper entityClassHelper = new EntityClassHelper(index.getComputingIndex());
ResourceImplementor resourceImplementor = new ResourceImplementor(entityClassHelper);
ClassOutput classOutput = new GeneratedBeanGizmoAdaptor(implementationsProducer);

for (ClassInfo classInfo : index.getIndex()
for (ClassInfo classInfo : index.getComputingIndex()
.getKnownDirectImplementors(PANACHE_MONGO_ENTITY_RESOURCE_INTERFACE)) {
validateResource(index.getIndex(), classInfo);
validateResource(index.getComputingIndex(), classInfo);

List<Type> generics = getGenericTypes(classInfo);
String resourceInterface = classInfo.name().toString();
Expand All @@ -103,13 +103,13 @@ void findRepositoryResources(CombinedIndexBuildItem index, Capabilities capabili
BuildProducer<RestDataResourceBuildItem> restDataResourceProducer,
BuildProducer<UnremovableBeanBuildItem> unremovableBeansProducer,
BuildProducer<BytecodeTransformerBuildItem> bytecodeTransformersProducer) {
EntityClassHelper entityClassHelper = new EntityClassHelper(index.getIndex());
EntityClassHelper entityClassHelper = new EntityClassHelper(index.getComputingIndex());
ResourceImplementor resourceImplementor = new ResourceImplementor(entityClassHelper);
ClassOutput classOutput = new GeneratedBeanGizmoAdaptor(implementationsProducer);

for (ClassInfo classInfo : index.getIndex()
for (ClassInfo classInfo : index.getComputingIndex()
.getKnownDirectImplementors(PANACHE_MONGO_REPOSITORY_RESOURCE_INTERFACE)) {
validateResource(index.getIndex(), classInfo);
validateResource(index.getComputingIndex(), classInfo);

List<Type> generics = getGenericTypes(classInfo);
String resourceInterface = classInfo.name().toString();
Expand Down
Loading

0 comments on commit c61912b

Please sign in to comment.